;============================================ ASCIIToBin proc near ; Convents ASCII numeric input to binary. ; on entry DS:SI points to start of decimal ASCII string of digits. ; String should contain only the decimal digits 0..9. ; Also allowed are MB KB -- all upper case multipliers ; This routine handles M and K multiplications itself. ; ignores Commas. ; tracks places past decimal in DPL (Forth-like) ; CX contains length of string > 0 ; On exit DX:AX contains binary equivalent of string. ; DPL counts places to right of decimal. ; If there is a decimal, result is integer as if no decimal present. ; ignores embedded colon. ; Works left to right. If invalid chars given, jumps to Abort. data segment even DPL dw 0 ; DPL is used used to keep track of how many decimal places were ; found when converting a number from ASCII to internal form. Ä1 ; means no decimal places 0 means just a decimal point after the ; number, 2 means two places after the decimal. data ends mov DPL,-1 ; Mark no decimal point seen yet push bx ; save regs we will trash push cx push si push di sub ax,ax mov dx,ax ; accumulate in DX:AX OneDigitLoop: ; DX:AX contains sum accumulated so far push ax ; save low order part of accumulating sum lodsb ; get next digit in AL call Ignorable test ah,ah jnz IgnoreDigit cmp al,'K' jne NotK ; K means multiply by 1024 = 2^10 pop ax call MultBy1024 jmp NextDigit NotK: cmp al,'M' jne NotM pop ax call MultBy1024 ; M means multiply by 1 megabyte = 2 ^ 20 call MultBy1024 ; i.e. 1024x1204 jmp Short NextDigit NotM: sub al,'0' ; convert 1 digit ASCII to bin jl NumTrouble ; ensure 0..9 cmp al,9 jg NumTrouble sub di,di mov bx,di mov bl,al pop ax call MultBy10 add ax,bx ; add digit onto accumulated product adc dx,di cmp DPL,-1 ; count digits to right of decimal point je NextDigit inc DPL ; this is a digit to rigt of decimal jmp NextDigit IgnoreDigit: pop ax NextDigit: loop OneDigitLoop ; loop once for each digit ; restore regs pop DI pop SI pop CX pop BX ; save regs we will trash ret NumTrouble: ; Some problem, give standard Syntax Error message. jmp Abort ASCIIToBin endp ;============================================================== CalcWantLevel proc near ; Compute the desired test result, a function of keyword, capacity and ; version. ; Calculate level of presence desired ; e.g. how many bytes free RAM ; power level of CPU ; 1 = feature present ; on entry BP points to the appropriate MCT table entry sub ax,ax ; presume 0 mov dx,ax test [Flags+BP], TimeStyle ; /TIME:HH:MM style? jz NotTime ; the TheCapacity has HH*100+MM ; we want HH*60+MM mov ax,word ptr TheCapacity mov dx,word ptr TheCapacity+2 push bx mov bx,100d div bx ; ax = HH dx = mm cmp dx,59d jg TimeTrouble cmp ax,23d jg TimeTrouble mov bx,60d mul bl ; ax = HH*60 add ax,dx sub dx,dx ; dx:ax = HH*60+MM pop bx jmp short SaveWantLevel TimeTrouble: jmp Abort NotTime: test [Flags+BP],CapacityStyle or TimeStyle ; /RAM:100K style? jz NotCapacity mov ax,word ptr TheCapacity mov dx,word ptr TheCapacity+2 jmp short SaveWantLevel NotCapacity: test [Flags+BP],VersionStyle ; /DOS3.1 style? jz NotVersion mov ax,TheVersion jmp Short SaveWantLevel NotVersion: test [Flags+BP],ImpliedStyle ; /80286 style? power level jz NotImplied mov ax,[AuxInput+BP] jmp Short SaveWantLevel NotImplied: ; nothing, must be a dummy ; leave as 0 SaveWantLevel: mov word ptr WantLevel,ax mov word ptr WantLevel+2,dx ret CalcWantLevel endp ;============================================ CommandLine proc near ; Gets command line string into ES:di, len CX ; Command line does not include the program name. ; ES already set since we are a COM file ; Sets cx to length of command line, not counting 0dh at end ; Trashes no other registers ; counted string at HEX 80 contains command line. ; It has no trailing null. ; It has a trailing 0Dh not part of count ; Why does this take so much longer to describe than code? mov di,81h sub CH,CH mov CL,DS:80h ; cx contains length of command ret CommandLine endp ;============================================ CountrySeps proc near ; Discover the chars used for separators usually comma and decimal ; Store in CommaSep and DeciSep ; see page 396 Ray Duncan's Advanced MS DOS data segment CountryBuf db 34d dup (?) ; stores county dependent info data ends lea dx,CountryBuf ; DS:DX points to country buffer mov ax,3800h ; get country info lea dx,CountryBuf int 21h ; call RAWDOSVer cmp ax,300 jae Dos3Country Dos2country: mov al,CountryBuf+4 ; comma mov ah,CountryBuf+6 ; decimal jmp Short SaveSeps Dos3Country: mov al,CountryBuf+7 ; comma mov ah,CountryBuf+9 ; decimal SaveSeps: cmp al,20h ja CommasepOk mov al,',' CommaSepOk: cmp ah,20h ja DecisepOk mov ah,'.' DecisepOk: mov CommaSep,al ; usually comma mov DeciSep,ah ; usually point mov DeciSep2,al ; usually comma ret CountrySeps endp ;============================================================== displayTheTest proc near ; displays the most recent test result ; Calls the approriate displayxxxx routine ; e.g. displays things like 'DESQview detected' ; on entry BP points to the MCT entry ; on entry Gotlevel contains the recent test result push ds push es push bp ; no other registers need saving mov ax,word ptr GotLevel ; put test result in dx:ax ready for display mov dx,word ptr GotLevel+2 call [dispAddr+BP] ; Call routine to display current state ; of of the test since we failed pop bp pop es pop ds ret displayTheTest endp ;============================================ DivBy10 proc near ; on entry DX:AX contains an unsigned binary number. ; on exit DX:AX contains that number divided by 10 ; Remainder left in BX, 0 .. 9 ; There are no possibilities of overflow or divide by 0 ; Trashes no other registers mov bx,10 cmp dx,bx jae ToughDiv EasyDiv: ; number is small so answer will fit in AX div bx mov bx,dx ; save remainder in bx sub dx,dx ; replace remainder with 0 high order ret ToughDiv: push cx mov cx,ax ; save low order part away mov ax,dx ; divide just the high order part by 10 sub dx,dx div bx ; dx:ax / 10 -> ax remdr -> dx xchg cx,ax ; save high order quotient ax -> cx ; create dividend of remdr of last ; div as high order and low order from ; original ax ; Next div cannot overflow because dx is ; < 10 because it is a remainder. div bx ; quot -> ax remdr -> dx mov bx,dx ; save remainder mov dx,cx ; restore high order part of quot, calc earlier pop cx ret DivBy10 endp ;============================================ DoTheTest proc near ; Do the appropriate test by calling one of the TESTxxxx routines ; i.e. detect the level of presence of that feature. ; on input BP points to the MCT table entry ; on output DX:AX has the level of presence. mov ax,[AuxInput+BP] ; hand off the auxilliary input to the test ; routine. Usually test routine ignores it. ; Only used by Aux-style tests push ds push es push bp ; no other registers need saving call [TestAddr+BP] ; Call routine to do the real work ; of testing presence. ; measured level we have returns in DX:AX ; or AX e.g. amount of RAM pop bp pop es pop ds test [Flags+BP],CapacityStyle jnz DXok sub dx,dx ; fake in test result DX to 32 bits ; non-capacity routines only bother with AX. DXok: mov word ptr GotLevel,ax ; save dx:ax test result mov word ptr GotLevel+2,dx ret DoTheTest endp ;============================================ DumpRegs proc near ; dumps contents of all registers. Cr before and after. ; Preserves all registers including the flags. push ax pushf call SayCr ; new line saystr ' ' ; indent a bit saystr 'AX=' call SayHexWord ; ax saystr 'BX=' mov ax,bx call SayHexWord ; bx saystr 'CX=' mov ax,cx call SayHexWord ; cx saystr 'DX=' mov ax,dx call SayHexWord ; dx saystr 'SI=' mov ax,si call SayHexWord ; si saystr 'DI=' mov ax,di call SayHexWord ; di saystr 'SP=' mov ax,sp add ax,10 ; allow for push ax and pushf, ; allow for two call levels, plush pushf on caller. call SayHexWord ; sp saystr 'FL=' pop ax push ax call SayHexWord ; flags call SayCr ; new line saystr ' ' ; indent a bit saystr 'DS=' mov ax,DS call SayHexWord ; DS saystr 'ES=' mov ax,ES call SayHexWord ; ES saystr 'SS=' mov ax,SS call SayHexWord ; SS call SayCr ; new line popf ; restore flags pop ax ; restore ax ret DumpRegs endp ; =========================================================== Filter proc near ; Convert entire command line to upper case ; Filter out tab chars and slashes call CommandLine ; string addr ES:di, length cx. mov si,di FilterLoop: lodsb cmp al,09h ; tab -> space jne NotTab mov al,20h jmp ConvertChar NotTab: cmp al,'/' ; / -> space jne notSlash mov al,20h jmp short ConvertChar NotSlash: cmp al,'a' ; a -> A b -> B etc jb FineAsIs cmp al,'z' ja FineAsIs sub al,20H ConvertChar: mov [si-1],al FineAsIs: loop FilterLoop ret Filter endp ;============================================================== FindCommand proc near ; Scans MCT table for an entry to match the current command. ; on entry, COMMAND to be looked up is all in upper case ; stored pointed to by si:cx ; on entry al contains a bit mask filter. Only entries with match ; on this filter should be considered. ; We look for the command. If we don't find it we abort. ; If we do find it, we return with pointer to the MCT entry in BP. ; On exit cx, si are trashed ; If we are successful we clear the carry, if not we set it data segment OnlyFilter db 0 ; mask for types of command to look for data ends mov onlyFilter,al dec si ; si points to count byte mov [si],cl ; store count byte just ahead of command ; to search for ; We need to get rid of it later, because it ; overlays a space the parsing routine ; uses. lea di,MCTStart.ExtNameAddr ; point to first element in MCT ; This is where we start looking Findloop: push si ; save addr string looking for push di ; save addr of current MCT entry mov di,[di] ; get addr of potential match cmpsb ; compare length bytes jne Notthisone sub ch,ch mov cl,[si-1] ; get length of string repe cmpsb ; compare strings jne Notthisone AlmostGotmatch: ; we probably found it ; check the capacity requirements pop bp ; pop the pointer to MCT entry ; was pushed as di push bp mov al,[Flags+BP] ; filter out matches that don't count and al,OnlyFilter ; on this pass. jz NotThisOne ; was not really a match ReallyGotIt: pop bp ; GOT IT! Match on both fields pop si mov byte ptr [si],20h ; restore count byte to a space clc ; indicate success ret NotThisOne: pop di ; get old MCT entry back pop si ; restore addr of string looking for add di,size MCT ; point at next MCT entry cmp di,offset COM:MCTEnd jb FindLoop ; fell out the bottom without finding ; what we are looking for. mov byte ptr [si],20h ; restore count byte to a space stc ; indicate failure ret FindCommand endp ;============================================ Goldilocks proc near ; Analyse the test result to see if it is too low, too high or just right. ; on entry a tested value present is in DX:AX ; e.g. the actual free ramspace ; We compare it with the Wanted Level ; We then perform the Goldilocks maunoeuvre ; If we have too much, we return +1, just right 0, too little -1 ; We do NOT apply the OkMask cmp DX,word ptr WantLevel+2 jne Diff cmp AX,word ptr WantLevel jne Diff JustRight: mov ax,Bang ret Diff: ; subtract lsw first sub AX,word ptr WantLevel sbb DX,word ptr WantLevel+2 jl Cold Hot: mov ax,Plus ret Cold: mov ax,Minus ret Goldilocks endp ;============================================================== IgnorAble proc near ; this is a proc just to keep loop short enough ; for LOOP instruction to span in ASCIItoBIN ; tests if char in AL can be ignored. AH=1 if yes cmp al,'B' ; ignore 'B' as in 'MB' je IsIgnorable cmp al,':' ; ignore embedded : je IsIgnorable cmp al,Commasep ; ignore ',' je IsIgnorable cmp al,DeciSep ; it is a '.'? je IsDeciSep cmp al,DeciSep2 ; it is a '.'? jne NotDeciSep IsDeciSep: mov DPL,0 ; mark that decimal point seen IsIgnorable: mov ah,1 ret NotDeciSep: mov ah,0 ; ordinary char ret ignorable endp ;============================================ MTrailing proc near ; on entry BX is addr of string, CX its length. ; Trims off any trailing blanks, leaving result in BX CX ; Length may also be 0 or 1, but not -ve ; If the entire string is blank the result is the null string mov di,bx add di,cx ; calc addr last char in string dec di mov al,20H ; AL = blank -- the search char jcxz mtrailing1 ; jump if null string std repe scasb ; scan ES:DI backwards till hit non blank ; DI points just ahead of it (wrap ok) ; CX is one too small, or 0 if none found cld je mtrailing1 ; jump if whole string was blank inc cx mtrailing1: ret MTrailing endp ;============================================================== Max proc near ; Compares two signed 32 bit numbers in DX:AX and CX:BX ; and leaves the larger in DX:AX mov DI,AX ; calc n2-n1 sub DI,BX mov DI,DX sbb DI,CX jge MAX1 mov AX,BX ; n2=CX:BX was langer mov DX,AX MAX1: ; n1=DX:AX was larger ret Max endp ;============================================ Multby10 proc near ; multiply the number in DX:AX by 10 ; Works because 10*X = 2*X + 8*X ; trashes no registers push di push bx shl ax,1 rcl dx,1 ; DX:AX = x*2 mov bx,ax mov di,dx shl bx,1 rcl di,1 shl bx,1 rcl di,1 ; DI:BX = x*8 add ax,bx adc dx,di ; DX:AX = x*2 + x*8 pop bx pop di ret MultBy10 endp ;============================================ Multby1024 proc near ; multiply the number in DX:AX by 1024 ; Works because 1024=2^10=2^8*2*2 ; trashes no registers ; shove everything left 8 bits mov dh,dl mov dl,ah mov ah,al sub al,al shl ax,1 ; shift left another two bits rcl dx,1 shl ax,1 rcl dx,1 ret MultBy1024 endp ;============================================ NibblesToBin proc near ; input is AX hex number encoded as 4 nibbles. ; output is AX number as binary integer ; e.g. 0510h -> 510d ; Trashes all non-segment registers mov di,ax ; save input mov bx,10d ; will be multiplying by ten sub dx,dx ; result, accumulates here. mov cx,4 ; process 4 hex nibbles, high to low ; in a loop, 4 bits at a time NibblesLoop: mov ax,dx ; multiply accumulation so far by 10 mul bx mov dx,ax ; save only low order word -> dx ; it cannot overflow. sub ax,ax ; clear high order part ready for shift in rcl di,1 ; slide next high order 4 bits of di into ax rcl ax,1 rcl di,1 rcl ax,1 rcl di,1 rcl ax,1 rcl di,1 rcl ax,1 add dx,ax ; add on sum so far loop Nibblesloop mov ax,dx ; leave result in ax ret NibblesToBin endp ;============================================ NIY proc near ; Dummy code for Not Implemented Yet tester routines ; just returns 0 in DX:AX data segment NIYMsg db 'Parameter not implemented yet',13,10,'$' data ends lea dx,NIYMsg sub ax,ax mov dx,ax ret NIY endp ; =================================================================== NthParm proc near ; Parses string for Nth Parameter delimited by blanks ; e.g. " RAM:250K DESQ " with bx=1 would give string "RAM:250K" ; By this point slashes have been stripped. ; on entry: ; ES:di - string ; cx - length of string ; bx - which parm wanted 1=parm1 2=parm2 etc. ; NthParm only finds one parm per call. ; On exit ES:si points to string and cx is its length. ; If there is no parm, the length will be 0. ; It also handles multiple leading/trailing blanks on parms. mov al,20h ; al = blank -- the search char Parmloop: ; Remove leading blanks on parm jcxz NullParm ; jump if null string repe scasb ; scan ES:di forwards till hit non blank ; di points just after it ; cx is one too small, or 0 if none found je NullParm ; jump if entire string was blank inc cx ; cx is length of remainder of string dec di ; di points to non-blank mov si,di ; remember start of string ; Search for terminating blank on parm jcxz NullParm ; jump if null string repne scasb ; scan ES:di forwards till hit blank ; di points just after it ; cx is one too small, or 0 if none found jne NoBlank ; jump if entire string was non blank inc cx ; cx is length of remainder of string dec di ; backup di to point to blank at string end NoBlank: ; di=addr tail end of command string, ; cx=len tail end of command string ; si=addr parm just parsed ; Major loop for each parm dec bx jnz Parmloop ; loop once for each parm mov cx,di sub cx,si ; cx is length of parameter. ret NullParm: ; was no nth parameter sub cx,cx ret NthParm endp ;============================================================== Parse proc near ; Parse the command line for the parameters data segment ParmIndex dw 0 ; track which parm we are working on data ends call CommandLine ; string addr ES:di, length cx. mov bx,di call MTrailing ; remove trailing blanks jcxz IndirectAbort ; null command line is a syntax error jmp Ok IndirectAbort: jmp Abort ; to far to jump directly Ok: call Filter ; clean out lower case, tabs, slashes mov ParmIndex,1 ; Start parsing with the 1st parm ; Each parm is block separated by spaces. ; Note start with 1 not 0! Parseloop: call CommandLine ; get first parm ; string=ES:di, length=cx mov bx,ParmIndex call NthParm ; work left to right jcxz NoMoreParms ; null param means no more ; e.g. DS:si points to RAM:400K+ ; cx is length of that piece call Handle1Parm ; si points to string, cx is length Next: inc ParmIndex ; bump loop counter jmp Parseloop ; loop till hit null param NoMoreParms: ret ; we are done Parse endp ;============================================================== ParseCapacity proc near ; Analyses the capacity portion of the current command. ; On Entry CapAddr:CapLen points to string containing the numeric ; capacity e.g. 400K ; On exit, the number be converted to binary and stored in TheCapacity. mov si,CapAddr mov cx,CapLen jcxz NullCapacity call asciitobin ; get the number into DX:AX sub cx,cx ; adjust the number to no decimals call DPLAdjust mov word ptr TheCapacity,ax mov word ptr TheCapacity+2,dx ret NullCapacity: mov word ptr TheCapacity,0 mov word ptr TheCapacity+2,0 ret ParseCapacity endp ;============================================ ParseSuffix proc near ; Parse the suffix part of the command. ; on entry SufAddr:SufLen points to Suffix characters. ; e.g. +!-? ; on exit OkMask is calculated. ; if suffix is null, default OkMask is used. ; If there are invalid characters in the suffix, we abort. ; NOTE WE NEED BP pointing to an MCT enntry mov si,SufAddr mov cx,SufLen jcxz UseDefaultSuffix mov OkMask,0 ; presume nothing allowed to start Suffloop: lodsb ; examine 1 char in suffix cmp al,'+' jne NotPlus test [Flags+BP],Plus ; test if + suffix allowed jz SuffErr or OkMask,Bang+Plus ; record ok to be >= level jmp SuffNext NotPlus: cmp al,'-' jne NotMinus test [Flags+BP],Minus ; test if - suffix allowed jz SuffErr or OkMask,Bang+Minus ; record ok to be <= level jmp Short SuffNext NotMinus: cmp al,'!' jne NotBang test [Flags+BP],Bang ; test if ! suffix allowed jz SuffErr or OkMask,Bang ; record ok to be = level jmp Short SuffNext NotBang: jmp Short SuffErr SuffNext: loop suffloop ret UseDefaultSuffix: mov al,[Defaults+BP] mov Okmask,al ; ! or +! for NEED ret ; ! or -! for AVOID SuffErr: jmp Abort ParseSuffix endp ;============================================ ParseVersion proc near ; Analyses the Version portion of the current command. ; On Entry VerAddr:VerLen points to string containing the version ; capacity e.g. 3.1 in PCDOS3.1 ; On exit, the number be converted to binary and stored in TheVersion ; 3.1 will be stored as 310. mov si,VerAddr mov cx,VerLen jcxz NullVersion mov CommaSep,0 ; fudge so there is no commasep ; this allows Decisep2, the comma, to ; start acting like a decimal. ; This is mainly of interest to Norwegians ; where the roles of , and . are reversed. call asciitobin ; get the number into DX:AX mov bl,DeciSep2 ; put Commasep back to standard comma mov Commasep,BL mov cx,2 ; adjust to 2 decimal places call DPLAdjust SaveVersion: mov word ptr TheVersion,ax ; ignore high order part ret NullVersion: mov word ptr TheVersion,0 ret ParseVersion endp ;============================================ SafeVector proc near ; Ensures that a vector exists and his hooked up to something ; before we call the corresponding interrupt. ; Many oddball machines are missing vectors. ; on entry AL has the vector number ; on exit carry flag set if problems, carry clear if ok ; all registers preserved ; see page 393 Ray Duncan's Advanced MS DOS push es push bx push ax mov ah,35h ; get vector int 21h ; ES:BX points to interrupt handler mov ax,ES or ax,BX jz BadVector cmp byte ptr ES:[bx],0cfh ; is it an iret? je BadVector GoodVector: clc jmp VectorDone badVector: stc VectorDone: pop ax pop bx pop es ret SafeVector endp ; ========================================================== Say$ proc near ; Used by Say and saystr ; on entry dx contains addr of $ delimited string ; displays on screen using BIOS push ax mov AH,09h int 21h pop ax ret Say$ endp ;============================================================== SayBigDec proc NEAR ; call with unsigned number in DX:AX ; converts it to ASCII and displays it on the screen ; If it is 0 shows as 0 ; If it is .02 shows as 0.02 ; If it is 1000000 shows as 1,000,000 ; leading 0's are suppressed. ; Field is exactly wide enough to hold the number. ; No leading or trailing spaces. DISPLAYED IN DECIMAL ; Uses global variables DeciSep (decimal point) ; CommaSep (comma) ; DPL (how many decimal places -1=none) ; ; Method is to repeatedly divide by 10. The remainder at ; each stage is one digit of the result, working right to left. ; Preserves all registers data segment PAD db 15 dup (0) ; where build the output string PADend db '$' ; terminator for Say data ends push ax push bx push cx push dx push di ; preserve di mov bx,10d lea di,padEnd ; point at $ trailing the PAD ; work right to left building digits at PAD mov cx,DPL ; places to right of decimal jcxz PlopDecimal ; 0 means just decimal point, no digits to right test cx,cx ; -1 means no decimal point. jl DecimalsDone DecimalsLoop: ; loop once for each digit to right of decimal ; number so far is in dx:ax call DivBy10 ; result in is dx:ax and remainder in bx add bl,'0' ; convert digit to ASCII dec di ; work right to left mov [di],bl ; save digit loop DecimalsLoop PlopDecimal: mov bl,Decisep ; plop in a decimal point dec di mov [di],bl DecimalsDone: LeftDigitsLoop: ; loop till all digits to left of decimal done mov cx,3 ; do groups of three digits, then a comma CommaGroupLoop: ; number so far is in dx:ax call DivBy10 ; result in is dx:ax and remainder in bx add bl,'0' ; convert digit to ASCII dec di ; work right to left mov [di],bl ; save digit mov bx,ax or bx,dx jz LeftDigitsDone ; if rest of number is 0, we are done loop CommaGroupLoop PlopComma: mov bl,Commasep ; plop in a comma dec di mov [di],bl jmp LeftDigitsLoop LeftDigitsDone: ; Number is ready mov dx,di ; start of ascii string, terminated by $ mov AH,09h ; BIOS put string terminated by $ int 21h pop di ; restore caller's registers pop dx pop cx pop bx pop ax ret SaybigDec endp ;============================================ SayBytesCapacity proc near ; input is dx:ax capacity. ; used to display phrases like '400,000 bytes free EMS RAM' ; Call must display the terminating phrase. test ax,ax jnz AreFreeBytes test dx,dx jnz AreFreeBytes saystr 'no bytes free ' ret AreFreeBytes: mov dpl,-1 call SayBigDec saystr ' bytes free ' ret SaybytesCapacity endp ;=========================================== SayCr proc near ; Emit a CrLf, to start a new line String segment ACrLf db 13,10,'$' String ends say ACrLf ret SayCr endp ;============================================================== SayHexByte proc NEAR ; call with hex number 00..FF in AL ; converts it to ASCII and displays it on the screen ; If it is 0 shows as 00. Leading 0's are not suppressed. ; Field is always 2 chars wide. ; No leading and no trailing space. DISPLAYED IN HEX ; Can call repeatedly to get a SayHexWord etc. ; Preserves all registers data segment even hexPAD db '00$' ; where numeric output built by SayHex data ends push ax ; preserve regs push bx push cx push dx mov dl,al ; save input ; Do first (leftmost digit) mov cl,4 shr al,cl and al,0fh ; get first digit cmp al,9 jg HexChar1 add al,'0' ; convert digit to ASCII jmp StoreChar1 HexChar1: add al,'A'-0AH ; convert to uppercase A..H StoreChar1: mov hexPad,al ; Do second (rightmost digit) mov al,dl and al,0fh ; get last digit cmp al,9 jg HexChar2 add al,'0' ; convert digit to ASCII jmp StoreChar2 HexChar2: add al,'A'-0AH ; convert to uppercase A..H StoreChar2: mov hexPad+1,al ; Number is ready lea dx,Hexpad mov AH,09h ; BIOS put string terminated by $ int 21h pop dx pop cx pop bx pop ax ret SayHexByte endp ;================================= SayHexWord proc near ; Displays hex word in ax, followed by space ; Trashes no registers xchg ah,al call SayHexByte ; high byte first xchg ah,al call SayHexByte ; low byte last saystr ' ' ret SayHexWord endp ;============================================ SayIfActive proc near ; input 0 or 1 is ax. ; Used to generate messages like 'DESQiew is active' ; or 'DESQview is inactive.' Caller must do the first word himself. test ax,ax jnz IsActive IsInactive: saystr ' is inactive.' ret IsActive: saystr ' is active.' ret SayIfActive endp ;============================================ SayIfDetected proc near ; input is ax, 0 or 1 ; used to display phrases like 'BTRIEVE was detected.' test ax,ax jnz WasDetected saystr ' not detected.' ret WasDetected: saystr ' was detected.' ret SayIfDetected endp ;============================================ SayLZDec proc NEAR ; on entry DX:AX contains integer, cx is count of digits desired ; uses leading zeros. Displays string on screen. ; e.g. 1 shows as 01 if CX=2 jcxz SayLZDecDone ; nothing to do push ax push cx push dx push di lea di,PadEnd DecLoop: ; loop once for each digit to right of decimal ; number so far is in dx:ax call DivBy10 ; result in is dx:ax and remainder in bx add bl,'0' ; convert digit to ASCII dec di ; work right to left mov [di],bl ; save digit loop DecLoop mov dx,di ; start of ascii string, terminated by $ mov AH,09h ; BIOS put string terminated by $ int 21h pop di pop dx pop cx pop ax SayLZDecDone: ret SayLZDec endp ;================================= SayVersion proc near ; input version in ax. dx=0 ; used to display phrases like 'MSDOS version 4.01 detected.' or ; 'MSDOS not present.' ; Caller must display the first word. test ax,ax jnz SomeVersion saystr ' not detected.' ret SomeVersion: saystr ' version ' mov dpl,2 call SaybigDec saystr ' detected.' ret SayVersion endp ;============================================ Trisect proc near ; break parm into 3 pieces /RAM:400K+! /TIME:23:30- ; RAM 400K +! ; TIME 23:30 - ; on entry si points to parm, cx is its length, guaranteed >0 ; / has already been pruned off. ; on exit: ; ComAddr:ComLen points to RAM (never allowed to be null) ; CapAddr:CapLen points to 400K (possibly null) ; SufAddr:SufLen points to +! (possibly null) ; AlpAddr:AlpLen points to RAM ; VerAddr:VerLen is null ; ; for /MSDOS3.1+ ; MSDOS 3.1 + ; ComAddr:ComLen points to MSDOS3.1 (never allowed to be null) ; CapAddr:CapLen is null ; SufAddr:SufLen points to + ; AlpAddr:AlpLen points to MSDOS ; VerAddr:VerLen points to 3.1 push cx mov ComLen,0 mov CapLen,0 mov SufLen,0 mov ComAddr,si add si,cx ; scan backwards to split off suffix ; scan back until hit non +-! dec si ; point to last char in string std ScanSuff: lodsb ; scan suffix backwards cmp al,'+' je NextSuff cmp al,'-' je NextSuff cmp al,'!' je NextSuff jmp Short Suffdone NextSuff: inc SufLen loop ScanSuff cld jmp TriSectTrouble ; fell out bottom -- 100% suffix oops! SuffDone: cld inc si ; went two too far inc si mov SufAddr,si ; Now find command by scanning from the ; front end till hit : + - ! mov si,ComAddr pop cx push cx comloop: lodsb cmp al,':' je FoundEndCom cmp al,'+' je FoundEndCom cmp al,'-' je FoundEndCom cmp al,'!' je FoundEndCom inc ComLen loop comloop ; fell out bottom, 100% command. ; this is perfectly ok. FoundEndCom: ; at this point ComAddr:ComLen and SuffAddr:SufLen ; are computed. Whatever lies between must be ; CapAddr:CapLen mov ax,ComAddr add ax,ComLen mov CapAddr,ax pop cx sub cx,ComLen sub cx,SufLen mov CapLen,cx ; at this point CapAddr:Caplen should either ; be null, or should point to colon. ; prune off the colon if present jcxz CapLenOk mov si,CapAddr lodsb cmp al,':' jne TrisectTrouble inc CapAddr ; chop off the colon dec CapLen CapLenOk: ; further split ComAddr:ComLen into ; AlpAddr:AlpLen and VerAddr:VerLen ; Work backwards looking for anything ; not [0 .. 9] [ , . ] mov cx,ComLen mov si,ComAddr mov AlpAddr,si add si,cx dec si std VerLoop: lodsb cmp al,DeciSep ; is it a decimal point? je StillVer cmp al,DeciSep2 ; is it a decimal point? je StillVer cmp al,CommaSep ; is it a comma je StillVer cmp al,'0' jb VerDone cmp al,'9' ja VerDone StillVer: loop VerLoop ; fall out bottom ok -- all numeric command. VerDone: cld ; went two too far inc si inc si ; now point to start of ver mov VerAddr,si ; calc Veraddr:Verlen sub si,AlpAddr ; AlpAddr:AlpLen mov AlpLen,si mov cx,ComLen sub cx,si mov VerLen,cx ret TrisectTrouble: jmp Abort Trisect endp ;============================================ DPLAdjust proc near ; Adjusts the result of the most recent ASCIITOBIN so that it ; has a standard number of decimal places. ; On entry dx:ax contains an unsigned number ; DPL contains number of decimal places it has ; CX contains number of decimal places you want it to have ; ; On exit DX:AX is adjusted ; DPL is trashed ; treat DPL = -1 as 0 cmp DPL,-1 jne StdDPL mov DPL,0 StdDPL: DPLLoop: ; Decimal points might be off. We want 2 ; places past the decimal. We might have too ; many or two few cmp DPL,CX ; actual = desired decimal places? je DPLAdjusted ja TooManyDecs TooFewDecs: ; too few places past decimal call MultBy10 inc DPL ; add one decimal place jmp Short DPLLoop TooManyDecs: call DivBy10 ; remove one decimal place dec DPL jmp Short DPLLoop DPLAdjusted: ret DPLAdjust endp ;============================================