	.model	small
	.code
	.data

	extrn	DsSave:word

	.code
	public	Blank, ScanP, ScanB, Out16, Hex, HexIn
	public	GetEol, HexChk, Address, GetHex, GetHex1, Tab
	public	Backup,PrintMes

	extrn	OutCh:near, PErr:near, Error:near

BacMes		db	8," ",8+80H

;Output one space

BLANK:
	MOV	AL," "
	jmp	OutCh

;Output the number of blanks in CX

TAB:
	CALL	BLANK
	LOOP	TAB
	RET

;Scan for parameters of a command

SCANP:
	CALL	SCANB		;Get first non-blank
	CMP	AL,","		;One comma between params OK
	JNE	EOLCHK		;If not comma, we found param
	INC	SI		;Skip over comma

;Scan command line for next non-blank character

SCANB:
	LODSB
	CMP	AL," "
	JZ	SCANB		;Skip over blanks
	cmp	al,9
	jz	ScanB
	DEC	SI		;Back up to first non-blank
EOLCHK:
	CMP	AL,13
	RET

;Print out 16-bit value in DX in hex

OUT16:
	MOV	AL,DH		;High-order byte first
	CALL	HEX
	MOV	AL,DL		;Then low-order byte

;Output byte in AL as two hex digits

HEX:
	MOV	AH,AL		;Save for second digit
;Shift high digit into low 4 bits
	PUSH	CX
	MOV	CL,4
	SHR	AL,CL
	POP	CX

	CALL	DIGIT		;Output first digit
HIDIG:
	MOV	AL,AH		;Now do digit saved in AH
DIGIT:
	AND	AL,0FH		;Mask to 4 bits
;Trick 6-byte hex conversion works on 8086 too.
	ADD	AL,90H
	DAA
	ADC	AL,40H
	DAA
	jmp	OutCh

;Check if next character in the input buffer is a hex digit
;and convert it to binary if it is. Carry set if not.

HEXIN:
	MOV	AL,[SI]

;Check if AL has a hex digit and convert it to binary if it
;is. Carry set if not.

HEXCHK:
	SUB	AL,"0"		;Kill ASCII numeric bias
	JC	RET2
	CMP	AL,10
	CMC
	JNC	RET2		;OK if 0-9
	AND	AL,5FH		;Convert to upper case
	SUB	AL,7		;Kill A-F bias
	CMP	AL,10
	JC	RET2
	CMP	AL,16
	CMC
Ret2:	RET

;Get an address in Segment:Offset format. Segment may be ommitted
;and a default (kept in BP) will be used, or it may be a segment
;register (DS, ES, SS, CS). Return with segment in AX, Offset in DX.

ADDRESS:
	CALL	SCANP
	CMP	byte ptr [SI+1],"S"	;Is second character "S"?
	JZ	SEGREG
	MOV	CX,4
	CALL	GETHEX		;Get number--may be segment or offset
	MOV	AX,BP		;Get default segment
	CMP	byte ptr [SI],":"	;Segment specification?
	JNZ	RET3
	PUSH	DX		;Save segment while we get offset
GETDISP:
	INC	SI		;Skip over ":"
	MOV	CX,4
	CALL	GETHEX
	POP	AX		;Bring segment back
Ret3:	RET

SEGREG:
	LODSB			;First letter of segment register
	MOV	DI,offset DGroup:SEGLET-1
	MOV	CX,4
CSSCAN:
	INC	DI
	CMP	AL,CS:[DI]
	LOOPNZ	CSSCAN
	JNZ	PERR
	INC	SI		;Skip second letter ("S")
	SHL	CX,1
	MOV	BX,CX
	CMP	byte ptr [SI],":"
	JNZ	ErrorJ
	PUSH	[BX+DSSave]
	jmp	GETDISP

SEGLET	DB	"CSED"


;Get the next parameter, which must be a hex number.
;CX is maximum number of digits the number may have.

GETHEX:
	CALL	SCANP		;Scan to next parameter
GETHEX1:
	XOR	DX,DX		;Initialize the number
	MOV	AH,DH
	CALL	HEXIN		;Get a hex digit
	JC	ErrorJ		;Must be one valid digit
	MOV	DL,AL		;First 4 bits in position
GETLP:
	INC	SI		;Next char in buffer
	DEC	CX		;Digit count
	CALL	HEXIN		;Get another hex digit?
	JC	RET2		;All done if no more digits
	JCXZ	ErrorJ		;Too many digits?
	SHL	DX,1		;Multiply by 16
	SHL	DX,1
	SHL	DX,1
	SHL	DX,1
	OR	DL,AL		;and combine new digit
	jmp	GETLP		;Get more digits

ErrorJ:	jmp	Error

;Make sure there is nothing more on the line except for
;blanks and carriage return. If there is, it is an
;unrecognized parameter and an error.

GETEOL:
	PUSH	AX
	CALL	SCANB		;Skip blanks
	POP	AX
	JNZ	ErrorJ		;Better be a RETURN
	RET

;Physical backspace - blank, backspace, blank

Backup:
	mov	si,offset DGroup:BacMes

;Print ASCII message. Last char has bit 7 set

PrintMes:
	push	cs
	pop	ds
PrintLp:
	lodsb			;Get char to print
	call	OutCh
	shl	al,1		;High bit set?
	jnc	PrintLp
	push	es
	pop	ds
	ret

	end
