• Please review our updated Terms and Rules here

Adding parameters

per

Veteran Member
Joined
Jan 21, 2008
Messages
3,052
Location
Western Norway
I have now taken the decission to add parameters to my programs. However, I once heard that DOS 2.1 and 3.3 treats parameters different. I really can't remember if it was what I actually heard, or if I just thought I heard it.

However, Here is the prgoram I've been adding parameters (Now you don't get the ugly feeling that your XT just crashed when you run it).

Feel free to use my subroutines.
 

Attachments

  • Animatorv2_2.zip
    12.5 KB · Views: 1
Last edited:
That looks great! I'll add it to my collection of DOS "eye candy".

What do you mean by "parameters"? Do you mean switches? Typically, DOS uses "/", or "-", or sometimes even nothing.

BTW, your README is not DOS friendly. It looks like you're using a non-standard 88 character screen whereas the normal would be 80. Where did you ever get one of those!! Text lines are probably best at around 60 or so in order to make them easy to read. :)
 
Sorry, sorry, I've written the readme in Notepad, and it doesn't got any Characters-per-line-counter stuff. I'll see what I can do to fix it.

Yes, by parameter, I mean swiches. The DOS manual refferes to them as parameters, and that was why I'm using that.


*Edit*
The readme should be fine now. I have even added a batch file for printing it to the screen.
 
Last edited:
There was a function in pre-DOS 5 (37H) that allowed one to get or set the switch character (/ or -). The idea was that you could use either the Unix or DOS conventions for command flags.

Unfortunately, many many programs ignored the function and hard-wired "/" in as the switch character. By DOS 5, function 37H didn't even work.

At one time, Microsoft considered DOS and Xenix to be headed toward a unification. At least that was the plan when DOS 2.0 came out.

At any rate, just use "/" as your switch character.

As far as other differences in argument handling, there are none.
 
Ok. My routine checs for both, so I guess I'm safe. I tought more like if the parameters where stored elsewhere than CS:0081h -> CS:0080h+b[CS:0080h].
 
That's odd. I thought the program written parses the commands as given and just does a case or match on the argument/parameter passed. I'm at work so I can't look at the code/documentation, but I'm interested in figuring out what it's for. Is this because earlier versions of DOS didn't pass the arguments at all?

Is it still possible to do echo %1 %2, etc?
 
That's odd. I thought the program written parses the commands as given and just does a case or match on the argument/parameter passed. I'm at work so I can't look at the code/documentation, but I'm interested in figuring out what it's for. Is this because earlier versions of DOS didn't pass the arguments at all?

Is it still possible to do echo %1 %2, etc?

The routine is actually rather short, so I'll paste it here:
Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Get parameter by input.                                      ;
;                                                             ;
;Input:                                                       ;
;DL = Parameter value in ASCII                                ;
;                                                             ;
;Output:                                                      ;
;BX = offset to parameter byte, zeroed if Parameter not found.;
;ZF = set if parameter found, else, reset                     ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
GtCPar:	push cx		;save cx
	mov bx,80h	;Get numbers of characters (PSP:80h)
	xor cx,cx	;-//-
	mov cl,b[bx]	;-//-
	jcxz GCEnd0	;End if no param.
GCLoop:	inc bx		;Point to next character
	cmp b[bx],'-'	;param. usually starts with a '-'
	jz GCVlid	;Get param.
	cmp b[bx],'/'	;param. usually starts with a '/'
	jz GCVlid	;Get param.
	inc bx		;Prefix bx
GCCont:	dec bx		;take bx back
	loop GCLoop	;get next character
GCEnd0:	mov bx,00h	;prefix bx
	cmp bx,0FH	;Reset ZF
GCEnd:	pop cx		;Return cx
	ret		;return

GCVlid:	inc bx		;ajust pointer
	cmp b[bx],dl	;Check if match
	jnz GCCont	;No, continue then	
	jmp GCEnd	;End since found

You simply pass the parameter ASCII value in DL and call the routine. ZF determine if it is found or not, and BX points to it if found. Pretty straight-forward to use.

However, I would really like to figure some way I could optimize my HEX-to-BCD routine. The one I currently have is darn slow, and waaay too big.
 
Instead of assembling the ASCII Hex in a register and passing it to your Hex-to-BCD routine, why not a more general (and iterative) solution? Just pass a pointer to the first character of the ASCII string. Quit when you find something that's not ASCII; otherwise, just assemble the BCD number iteratively. I might even be tempted to first assemble it in binary (very fast), then convert to BCD using Chinese remainder. That way, you'd have two more general-purpose routines instead of a single specialized one.

A good programmer tries not to write a routine that can be used only once.
 
Instead of assembling the ASCII Hex in a register and passing it to your Hex-to-BCD routine, why not a more general (and iterative) solution? Just pass a pointer to the first character of the ASCII string. Quit when you find something that's not ASCII; otherwise, just assemble the BCD number iteratively. I might even be tempted to first assemble it in binary (very fast), then convert to BCD using Chinese remainder. That way, you'd have two more general-purpose routines instead of a single specialized one.

A good programmer tries not to write a routine that can be used only once.

The code I wrote above is not my Hex-to-BCD routine.

How the Hex-to-BCD currently works is that the input register is used as a loop counter, and adds 1 and ajust to BCD if something reaches Ah for each loop. When you convert large stuff like the memory amount (typically about 500000) on an 8088 takes about 15 seconds, and that's a terrible lot of an unexplained pause in a program. Users might get scared and think that their computer crashed!

Here is the set of my converting routines, including the usable "PrintDX" routine that takes use of them:
Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Print the value in DX.                                ;
;Print the value of DX to the current cursor possition.;
;                                                      ;
;Input:                                                ;
;DX = Any value                                        ;
;                                                      ;
;Note, The following subroutines are used:             ;
;'HEXtoBCD'                                            ;
;'BCDtoASCII'                                          ;
;Int 10h                                               ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PrintDX:
	push	ax
	push	bx
	push	dx
	push	cx
	call	PrnDX
	pop	cx
	pop	dx
	pop	bx
	pop	ax
	ret

PrnDX:	mov	ax,dx		;Save AX
	xor	dx,dx		;Clear BX
	push	dx		;Save DX
	call	HEXtoBCD	;convert to BCD
	pop	dx		;Restore DX
	push	ax		;save AX
	mov	ax,bx		;Set up for routine
	call	PrnDX0		;Print 4 upper diggits
	pop	ax		;Restore AX
PrnDX0:	push	ax		;Save 4 lowest digits
	mov	al,ah		;Print high digit first
	call	PrnDX1		;print
	pop	ax		;get low digit
PrnDX1:	call	BCDtoASCII	;to ASCII
	push	ax		;save digits
	mov	al,ah		;print high digit first
	call	PrnDX2		;Print
	pop	ax		;get back digits
PrnDX2:	mov	ah,0Eh		;then print low digit
	int	10h		;call video IO
	ret			;Return





;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;HEX to BCD.                                    ;
;converts a HEX number in DX and BX to          ;
;a BCD number in AX and BX.                     ;
;                                               ;
;input:                                         ;
;AX = Low HEX input                             ;
;DX = High HEX input                            ;
;                                               ;
;output:                                        ;
;AX = Low BCD output (4 digits, one per nibble) ;
;BX = High BCD output (4 digits, one per nibble);
;OF set if output greater than 99999999         ;
;                                               ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
HEXtoBCD:
	push	cx
	push	dx
	mov	cx,ax		;Get low input
	push	dx		;Save high input
	sub	ax,ax		;prepare ax
	sub	bx,bx		;and bx
	jcxz	HEXBCD2		;Skip if zero (output is already zeroed)

HEXBCD0:			;Add first the 
	inc	ax		;increase low BCD output.
				;	Digit 1, ones, low nibble of AL
	mov	dx,ax		;make a working copy
	and	dx,0Fh		;mask off nibble
	cmp	dx,0Ah		;digit=10d or above?
	jb	HEXBCD1		;no, try next number
	add	ax,06h		;convert to BCD
				;	Digit 2, tens, high nibble of AL
	mov	dx,ax
	and	dx,0F0h
	cmp	dx,0A0h
	jb	HEXBCD1
	add	ax,060h
				;	Digit 3, hundreds, low nibble of AH
	mov	dx,ax
	and	dx,0F00h
	cmp	dx,0A00h
	jb	HEXBCD1
	add	ax,0600h
				;	Digit 4, thousands, high nibble of AH
	mov	dx,ax
	and	dx,0F000h
	cmp	dx,0A000h
	jb	HEXBCD1
	add	ax,06000h

	inc	bx		;Number > than 9999; Increase high BCD output then.
				;	Digit 5, tens of thousands, high nibble of DH
	mov	dx,bx
	and	dx,0Fh
	cmp	dx,0Ah
	jb	HEXBCD1
	add	bx,06h
				;	Digit 6, hundred of thousands, high nibble of DL
	mov	dx,bx
	and	dx,0F0h
	cmp	dx,0A0h
	jb	HEXBCD1
	add	bx,060h
				;	Digit 7, millions, low nibble of DH
	mov	dx,bx
	and	dx,0F00h
	cmp	dx,0A00h
	jb	HEXBCD1
	add	bx,0600h
				;	Digit 8, tens of millions, high nibble of DH
	mov	dx,bx
	and	dx,0F000h
	cmp	dx,0A000h
	jb	HEXBCD1
	add	bx,06000h
HEXBCD1:
	loop	HEXBCD0		;Add one more

HEXBCD2:
	pop	cx		;Get high input
	jcxz	HEXBCD3		;Skip if number < 64K
	jmp	HEXBCD4		;Else, add more

HEXBCD3:
	jmp	HEXBCD7		;Return

HEXBCD4:
	push	cx		;Save high input loop counter
	sub	cx,cx		;Add 64K for each number in high input
HEXBCD5:
	inc	ax
				;	Digit 1, ones, low nibble of AL
	mov	dx,ax
	and	dx,0Fh
	cmp	dx,0Ah
	jb	HEXBCD6
	add	ax,06h
				;	Digit 2, tens, high nibble of AL
	mov	dx,ax
	and	dx,0F0h
	cmp	dx,0A0h
	jb	HEXBCD6
	add	ax,060h
				;	Digit 3, hundreds, low nibble of AH
	mov	dx,ax
	and	dx,0F00h
	cmp	dx,0A00h
	jb	HEXBCD6
	add	ax,0600h
				;	Digit 4, thousands, high nibble of AH
	mov	dx,ax
	and	dx,0F000h
	cmp	dx,0A000h
	jb	HEXBCD6
	add	ax,06000h

	inc	bx
				;	Digit 5, tens of thousands, high nibble of DH
	mov	dx,bx
	and	dx,0Fh
	cmp	dx,0Ah
	jb	HEXBCD6
	add	bx,06h
				;	Digit 6, hundred of thousands, high nibble of DL
	mov	dx,bx
	and	dx,0F0h
	cmp	dx,0A0h
	jb	HEXBCD6
	add	bx,060h
				;	Digit 7, millions, low nibble of DH
	mov	dx,bx
	and	dx,0F00h
	cmp	dx,0A00h
	jb	HEXBCD6
	add	bx,0600h
				;	Digit 8, tens of millions, high nibble of DH
	mov	dx,bx
	and	dx,0F000h
	cmp	dx,0A000h
	jb	HEXBCD6
	add	bx,06000h
HEXBCD6:
	loop	HEXBCD5		;Try next number
	pop	cx		;Get high input loop counter
	loop	HEXBCD4		;Loop if more to add

HEXBCD7:
	pop	dx
	pop	cx
	ret






;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;BCD to ASCII.                 ;
;converts a BCD number in AL to;
;two ASCII numbers in AX.      ;
;                              ;
;input:                        ;
;AL = BCD input                ;
;                              ;
;output:                       ;
;AX = ASCII output             ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
BCDtoASCII:	push	cx
		mov	cl,04h
		sub	ah,ah		;clear ah
		shl	ax,cl		;move upper digit into ah
		shr	al,cl		;and return lower digit to al
		add	ax,3030h	;ajust to ASCII
		pop	cx
		ret

As you can see, Big and slow (except for the two other routines. The PrintDX is actually pretty optimized, though).
 
here's the hex2dec routine I've used for years. I can't do numbers as big as yours, but the concept is pretty good.
Not sure where this came from-I don't think I wrote it. And now that I look at it, it'll need some mods to the rotate functions to run on an 8088/8086. (sorry)
Code:
;----------------------------------------------------------------------------
;Hex2dec    convert a 16bit hex number in AX to a decimal number in AX
;	    maximum value for AX=270Fh (9999d)
;============================================================================

hex2dec proc

	push 	bx                  ; save that CX
	push	cx
	push	dx
	push 	si                  ; save that SI
	push	di
	mov 	si, 10           ; SI will be our divisor
	xor	di, di
	xor	cx, cx
L1:
	xor 	dx, dx                ; clean up the DX
	div 	si                   ; divide by 10
	     
	add	di, dx
	ror	di, 4
	inc	cl
	or 	ax, ax          ; end of the number?
	jne 	L1             	; no? Keep chuggin' away

L2:
	rol	di, 4
	loop	L2


	mov	ax, di
	pop	di
	pop 	si                   
	pop	dx
	pop	cx
	pop 	bx                   
	ret            
hex2dec endp

You will also probably like this page:
http://www.df.lth.se/~john_e/fr_gems.html
 
here's the hex2dec routine I've used for years. I can't do numbers as big as yours, but the concept is pretty good.
Not sure where this came from-I don't think I wrote it. And now that I look at it, it'll need some mods to the rotate functions to run on an 8088/8086. (sorry)
Code:
;----------------------------------------------------------------------------
;Hex2dec    convert a 16bit hex number in AX to a decimal number in AX
;	    maximum value for AX=270Fh (9999d)
;============================================================================

hex2dec proc

	push 	bx                  ; save that CX
	push	cx
	push	dx
	push 	si                  ; save that SI
	push	di
	mov 	si, 10           ; SI will be our divisor
	xor	di, di
	xor	cx, cx
L1:
	xor 	dx, dx                ; clean up the DX
	div 	si                   ; divide by 10
	     
	add	di, dx
	ror	di, 4
	inc	cl
	or 	ax, ax          ; end of the number?
	jne 	L1             	; no? Keep chuggin' away

L2:
	rol	di, 4
	loop	L2


	mov	ax, di
	pop	di
	pop 	si                   
	pop	dx
	pop	cx
	pop 	bx                   
	ret            
hex2dec endp

You will also probably like this page:
http://www.df.lth.se/~john_e/fr_gems.html
I'm using it for displaying amount of free memory, so I need one that can go at least up to 655360, and my can handle up to 99999999. If only sombody could mod that one you got. BTW, the method seems pretty similar to the method in the one used in the IBM PC/XT BIOS.
 
"Chinese" remainder theorem. (I could just give you the code, but what fun would that be?)

Given a binary number x, develop a base b representation in d(n) digits:

for i= 0 to n do
d(i) = x mod b
x = int( x/b);
end

Thus you develop one digit per iteration, starting with the least significant digit (hence: the "Chinese" bit). You may object and say that the 8088 32-bit divide only yields a 16 bit quotient, but you can divide your large number up into two smaller ones, say x/10,000 and x mod 10,000. So, in no more than 10 iterations, you can develop 10 digits.

A slight peeve with your commentary here. "Hex" refers to the character representation of a binary number. Thus, a hex dump of a binary file. Don't say "hex" unless you actually mean the characters "0"..."A" "B"... If it's just a binary quantity, call it that.

As far as commentary, what's worked for me over the decades is the old newspaper reporting trick: Say what you're going to do in a block comment, say what you're doing (as a running commentary); and then, if what you've done is complicated, say what you've done.

The original kernel code for NT 3.x is an excellent example of good commentary practice. Sadly, updates to the code haven't kept that tradition up.
 
Last edited:
Back
Top