; =============================================================================
;
;                        Litos8 console display driver
;
; =============================================================================

; -----------------------------------------------------------------------------
; Exported user functions (22):
;	DispConChar - display console character
;	DispConSpc - display console space character
;	DispConNewLine - display console new line (CR/LF characters)
;	DispConText - display console text
;	DispConTextZ - display console ANSIIZ text
;	DispConTextStr - display console text string
;	DispCon2Dig - display console 2 digits
;	DispCon2DigSpc - display console 2 digits with space
;	DispConByte - display console unsigned Byte
;	DispConSByte - display console signed Byte
;	DispConWord - display console unsigned Word
;	DispConSWord - display console signed Word
;	DispConDWord - display console unsigned DWord
;	DispConSDWord - display console signed DWord
;	DispConHByte - display console HEX Byte
;	DispConHWord - display console HEX Word
;	DispConHDWord - display console HEX DWord
;	DispConBByte - display console BIN Byte
;	DispConBWord - display console BIN Word
;	DispConBDWord - display console BIN DWord
;	DispConDate - display console date
;	DispConTime - display console time
; -----------------------------------------------------------------------------

		CODE_SECTION

; -----------------------------------------------------------------------------
;                          Display console character
; -----------------------------------------------------------------------------
; INPUT:	AL = character
; NOTES:	Supported controll characters BS, TAB, LF, FF, CR.
; -----------------------------------------------------------------------------

; ------------- push registers

DispConChar:	push	ax		; push AX
		push	dx		; push DX

; ------------- prepare current position -> DX

		mov	dx,[CursorPos]	; DX <- current position

; ------------- BS, back space

		cmp	al,BS		; back space?
		jne	short DispConChar2 ; no back space
		or	dl,dl		; column 0?
		jnz	short DispConChar1 ; not column 0
		or	dh,dh		; already row 0?
		jz	short DispConChar9 ; already column 0
		dec	dh		; DH <- one row up
		mov	dl,[VideoCols]	; DL <- number of columns
DispConChar1:	dec	dl		; decrease cursor column
		jmp	short DispConChar8 ; set new cursor position

; ------------- TAB, tabulator

DispConChar2:	cmp	al,TAB		; tabulator?
		jne	short DispConChar3 ; no tabulator
		add	dl,8		; DL <- shift by tabulator
		and	dl,~7		; align column to tabulator position
		jmp	short DispConChar6 ; check row overflow

; ------------- LF, line feed

DispConChar3:	cmp	al,LF		; line feed?
		je	short DispConChar7 ; line feed

; ------------- FF, form feed, clear screen

		cmp	al,FF		; form feed?
		jne	short DispConChar4 ; no form feed
		call	DispClear	; clear display
		jmp	short DispConChar9

; ------------- CR, carriage return

DispConChar4:	cmp	al,CR		; carriage return?
		jne	short DispConChar5 ; no carrage return
		mov	dl,0		; DL <- 0, row begin
		jmp	short DispConChar8 ; set cursor position

; ------------- valid character

DispConChar5:	mov	ah,[DefColor]	; AH <- default color
		call	DispChar	; display character
		inc	dl		; increase column

; ------------- scroll screen on overflow

DispConChar6:	cmp	dl,[VideoCols]	; check column overflow
		jb	short DispConChar8 ; no overflow
		mov	dl,0		; new column
DispConChar7:	inc	dh		; increase row
		cmp	dh,[VideoRows]	; check row overflow
		jb	short DispConChar8 ; no overflow
		dec	dh		; decrease row
		call	DispScroll	; scroll screen

; ------------- set new cursor position

DispConChar8:	call	SetCursor	; set new cursor position

; ------------- pop registers

DispConChar9:	pop	dx		; pop DX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                            Display console space
; -----------------------------------------------------------------------------

DispConSpc:	push	ax		; push AX
		mov	al," "		; AL <- space character
		call	DispConChar	; display character
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                       Display console newline (CR/LF)
; -----------------------------------------------------------------------------

DispConNewLine:	push	ax		; push AX
		mov	al,CR		; AL <- CR character
		call	DispConChar	; display character
		mov	al,LF		; AL <- LF character
		call	DispConChar	; display character
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                            Display console text
; -----------------------------------------------------------------------------
; INPUT:	SI = pointer to text
;		CX = text length
; -----------------------------------------------------------------------------

; ------------- push registers

DispConText:	push	ax		; push AX
		push	cx		; push CX
		push	si		; push SI

; ------------- display text

		jcxz	DispConText8	; no text
DispConText2:	lodsb			; AL <- character
		call	DispConChar	; display console character
		loop	DispConText2	; next character

; ------------- pop registers

DispConText8:	pop	si		; pop SI
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                          Display console ASCIIZ text
; -----------------------------------------------------------------------------
; INPUT:	SI = pointer to ASCIIZ text (zero terminated)
; -----------------------------------------------------------------------------

; ------------- push registers

DispConTextZ:	push	ax		; push AX
		push	si		; push SI

; ------------- display text

DispConTextZ2:	lodsb			; AL <- character
		or	al,al		; end of text?
		jz	short DispConTextZ8 ; end of text
		call	DispConChar	; display console character
		jmp	short DispConTextZ2 ; next character

; ------------- pop registers

DispConTextZ8:	pop	si		; pop SI
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                        Display console text string
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT structure
; -----------------------------------------------------------------------------

; ------------- push registers

DispConTextStr:	push	ax		; push AX
		push	cx		; push CX
		push	si		; push SI

; ------------- display text

		mov	cx,[bx+TEXT_Length] ; CX <- text length
		jcxz	DispConTextStr8	; no text
		lea	si,[bx+TEXT_Text] ; SI <- start of text
DispConTextStr2:lodsb			; AL <- character
		call	DispConChar	; display console character
		loop	DispConTextStr2	; next character

; ------------- pop registers

DispConTextStr8:pop	si		; pop SI
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                        Display console 2 digits
; -----------------------------------------------------------------------------
; INPUT:	AL = data byte (0..99)
; -----------------------------------------------------------------------------

DispCon2Dig:	push	ax		; push AX
		aam			; AL <- units, AH <- tens
		xchg	al,ah		; AL <- tens, AH <- units
		add	ax,"00"		; AL,AH <- convert to ASCII character
		call	DispConChar	; display character
		mov	al,ah		; AL <- units
		call	DispConChar	; display character
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                    Display console 2 digits with space
; -----------------------------------------------------------------------------
; INPUT:	AL = data byte (0..99)
; -----------------------------------------------------------------------------

DispCon2DigSpc:	push	ax		; push AX
		aam			; AL <- units, AH <- tens
		xchg	al,ah		; AL <- tens, AH <- units
		add	ax,"00"		; AL,AH <- convert to ASCII character
		cmp	al,"0"		; unsignificant zero?
		jne	short DispCon2DigSpc2 ; valir digit
		mov	al," "		; AL <- space
DispCon2DigSpc2:call	DispConChar	; display character
		mov	al,ah		; AL <- units
		call	DispConChar	; display character
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                        Display console unsigned Byte
; -----------------------------------------------------------------------------
; INPUT:	AL = unsigned data byte
; -----------------------------------------------------------------------------

; ------------- push registers

DispConByte:	push	ax		; push AX

; ------------- hundrends

		cmp	al,100		; check 100
		jb	short DispConByte2 ; number is < 100
		push	cx		; push CX
		mov	cl,100		; CL <- divider
		mov	ah,0		; AX = number
		div	cl		; AL <- hundreds, AH <- units
		add	al,"0"		; AL <- convert to ASCII character
		call	DispConChar	; display character
		mov	al,ah		; AL <- units
		pop	cx		; pop CX
		jmp	short DispConByte3

; ------------- tens

DispConByte2:	cmp	al,10		; check 10
		jb	short DispConByte4 ; number is < 10
DispConByte3:	aam			; AL <- units, AH <- tens
		xchg	al,ah		; AL <- tens, AH <- units
		add	al,"0"		; AL <- convert to ASCII character
		call	DispConChar	; display character
		mov	al,ah		; AL <- units

; ------------- units

DispConByte4:	add	al,"0"		; AL <- convert to ASCII character
		call	DispConChar	; display character

; ------------- pop registers

		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                        Display console signed Byte
; -----------------------------------------------------------------------------
; INPUT:	AL = signed data byte
; -----------------------------------------------------------------------------

DispConSByte:	or	al,al		; negative number?
		jns	short DispConByte ; number is positive
		push	ax		; push AX
		mov	ah,al		; AH <- character to display
		mov	al,"-"		; AL <- sign character
		call	DispConChar	; display sign character
		mov	al,ah		; AL <- number to display
		neg	al		; AL <- change to positive number
		call	DispConByte	; display number
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                        Display console unsigned Word
; -----------------------------------------------------------------------------
; INPUT:	AX = unsigned data word
; -----------------------------------------------------------------------------

; ------------- small number (in byte)

DispConWord:	or	ah,ah		; small number ?
		jz	short DispConByte ; small number, in byte

; ------------- push registers

		push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX

; ------------- decode number into stack

		xor	cx,cx		; CX <- character counter
		mov	bx,10		; BX <- 10, divider
DispConWord2:	xor	dx,dx		; DX <- 0
		div	bx		; divide one digit
		add	dl,"0"		; DL <- convert to ASCII character
		push	dx		; push digit into stack
		inc	cx		; increase digit counter
		or	ax,ax		; already zero ?
		jnz	short DispConWord2 ; decode next digit

; ------------- display number

DispConWord4:	pop	ax		; pop digit from the stack
		call	DispConChar	; display digit
		loop	DispConWord4	; next digit

; ------------- pop registers

		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                        Display console signed Word
; -----------------------------------------------------------------------------
; INPUT:	AX = signed data word
; -----------------------------------------------------------------------------

DispConSWord:	or	ax,ax		; negative number?
		jns	short DispConWord ; number is positive
		push	ax		; push AX
		mov	al,"-"		; AL <- sign character
		call	DispConChar	; display sign character
		pop	ax		; pop AX
		neg	ax		; AX <- change to positive number
		call	DispConWord	; display number
		neg	ax		; AX <- original number
		ret

; -----------------------------------------------------------------------------
;                        Display console unsigned DWord
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = unsigned data dword
; -----------------------------------------------------------------------------

; ------------- small number (in word)

DispConDWord:	or	dx,dx		; small number?
		jz	short DispConWord ; small number, on word

; ------------- push registers

		push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI

; ------------- decode number into stack

		xor	cx,cx		; CX <- character counter
		mov	bx,10		; BX <- 10, divider
DispConDWord2:	xchg	ax,si		; SI <- number LOW
		xchg	ax,dx		; AX <- number high
		xor	dx,dx		; DX <- 0
		div	bx		; divide / 10
		xchg	ax,si		; SI <- new high, AX <- low
		div	bx		; divide / 10
		add	dl,"0"		; DL <- convert to ASCII character
		push	dx		; push digit into stack
		inc	cx		; increase digit counter
		mov	dx,si		; DX <- new high
		or	si,ax		; check zero result
		jnz	short DispConDWord2 ; decode next digit

; ------------- display number

DispConDWord4:	pop	ax		; pop digit from the stack
		call	DispConChar	; display digit
		loop	DispConDWord4	; next digit

; ------------- pop registers

		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                        Display console signed DWord
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = signed data dword
; -----------------------------------------------------------------------------

; ------------- check negative number

DispConSDWord:	or	dx,dx		; negative number?
		jns	short DispConDWord ; number is positive

; ------------- display negative sign

		push	ax		; push AX
		mov	al,"-"		; AL <- sign character
		call	DispConChar	; display sign character
		pop	ax		; pop AX

; ------------- dispay absolute value of the number

		push	ax		; push AX
		push	dx		; push DX
		neg	ax		; AX <- negate number LOW
		adc	dx,byte 0	; DX <- carry
		neg	dx		; DX <- negate number HIGH
		call	DispConDWord	; display number
		pop	dx		; pop DX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                        Display console HEX DWord
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = data dword
; -----------------------------------------------------------------------------

DispConHDWord:	xchg	ax,dx		; AX <- high word, DX <- low word
		call	DispConHWord	; display high word
		xchg	ax,dx		; AX <- low word, DX <- high word

; --- DispConHWord must follow!

; -----------------------------------------------------------------------------
;                        Display console HEX Word
; -----------------------------------------------------------------------------
; INPUT:	AX = data word
; -----------------------------------------------------------------------------

DispConHWord:	xchg	al,ah		; AL <- high byte, AH <- low byte
		call	DispConHByte	; display high byte
		xchg	al,ah		; AL <- low byte, AH <- high byte

; --- DispConHByte must follow!

; -----------------------------------------------------------------------------
;                        Display console HEX Byte
; -----------------------------------------------------------------------------
; INPUT:	AL = data byte
; -----------------------------------------------------------------------------

; alternate method:
;		add	al,90h
;		daa
;		adc	al,40h
;		daa

; ------------- display high nibble

DispConHByte:	push	ax		; push AX
		shr	al,1
		shr	al,1
		shr	al,1
		shr	al,1		; AL <- high nibble
		cmp	al,10		; hex character A..F ?
		sbb	al,69h
		das			; AL <- hex digit
		call	DispConChar	; display character
		pop	ax		; pop AX

; ------------- display low nibble

		push	ax		; push AX
		and	al,0fh		; AL <- low nibble
		cmp	al,10		; hex character A..F ?
		sbb	al,69h
		das			; AL <- hex digit
		call	DispConChar	; display character
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                        Display console BIN DWord
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = data dword
; -----------------------------------------------------------------------------

DispConBDWord:	xchg	ax,dx		; AX <- high word, DX <- low word
		call	DispConBWord	; display high word
		xchg	ax,dx		; AX <- low word, DX <- high word

; --- DispConBWord must follow!

; -----------------------------------------------------------------------------
;                        Display console BIN Word
; -----------------------------------------------------------------------------
; INPUT:	AX = data word
; -----------------------------------------------------------------------------

DispConBWord:	xchg	al,ah		; AL <- high byte, AH <- low byte
		call	DispConBByte	; display high byte
		xchg	al,ah		; AL <- low byte, AH <- high byte

; --- DispConBByte must follow!

; -----------------------------------------------------------------------------
;                        Display console BIN Byte
; -----------------------------------------------------------------------------
; INPUT:	AL = data byte
; -----------------------------------------------------------------------------

; ------------- push characters

DispConBByte:	push	ax		; push AX
		push	cx		; push CX

; ------------- display bits

		mov	ah,al		; AH <- data byte
		mov	cx,8		; CX <- number of bits
DispConBByte2:	shl	ah,1		; check highest bit
		mov	al,"0"		; AL <- bit "0"
		adc	al,0		; change to bit "1"
		call	DispConChar	; display character
		loop	DispConBByte2	; dispaly next bit

; ------------- pop registers

		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                           Display console date
; -----------------------------------------------------------------------------
; INPUT:	AH = date format (DATEFORM_EURO,...)
;		DL = day
;		DH = month
;		SI = year
; -----------------------------------------------------------------------------

; ------------- push registers

DispConDate:	push	ax		; push AX

; ------------- Europe format day.month.year

		cmp	ah,DATEFORM_EURO ; Europe format?
		jne	short DispConDate2 ; no

		mov	al,dl		; AL <- day
		call	DispConByte	; display day
		mov	al,"."		; separator
		call	DispConChar	; display separator

		mov	al,dh		; AL <- month
		call	DispConByte	; display month
		mov	al,"."		; separator
		call	DispConChar	; display separator

		mov	ax,si		; AX <- year
		call	DispConWord	; display year
		jmp	short DispConDate8

; ------------- USA format month/day/year

DispConDate2:	cmp	ah,DATEFORM_USA ; USA format?
		jne	short DispConDate4 ; no

		mov	al,dh		; AL <- month
		call	DispConByte	; display month
		mov	al,"/"		; separator
		call	DispConChar	; display separator

		mov	al,dl		; AL <- day
		call	DispConByte	; display day
		mov	al,"/"		; separator
		call	DispConChar	; display separator

		mov	ax,si		; AX <- year
		call	DispConWord	; display year
		jmp	short DispConDate8

; ------------- Japan format year-month-day

DispConDate4:	mov	ax,si		; AX <- year
		call	DispConWord	; display year
		mov	al,"-"		; separator
		call	DispConChar	; display separator

		mov	al,dh		; AL <- month
		call	DispCon2Dig	; display month
		mov	al,"-"		; separator
		call	DispConChar	; display separator

		mov	al,dl		; AL <- day
		call	DispCon2Dig	; display day

; ------------- pop registers

DispConDate8:	pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                         Display console time
; -----------------------------------------------------------------------------
; INPUT:	BH = second
;		CL = minute
;		CH = hour
; -----------------------------------------------------------------------------

; ------------- push registers

DispConTime:	push	ax		; push AX

; ------------- display hour

		mov	al,ch		; AL <- hour
		call	DispCon2DigSpc	; display hour
		mov	al,":"		; separator
		call	DispConChar	; display separator

; ------------- display minute

		mov	al,cl		; AL <- minute
		call	DispCon2Dig	; display minute
		mov	al,":"		; separator
		call	DispConChar	; display separator

; ------------- display second

		mov	al,bh		; AL <- second
		call	DispCon2Dig	; display second

; ------------- pop registers

		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                             CON device check
; -----------------------------------------------------------------------------
; INPUT:	BX = device index (0..15)
; OUTPUT:	CY = invalid device
; -----------------------------------------------------------------------------

CONCheck:	clc			; clear error flag
		ret

; -----------------------------------------------------------------------------
;                           Read from CON device
; -----------------------------------------------------------------------------
; INPUT:	BX = device index (0..15)
;		CX = number of bytes
;		DI = destination buffer
; OUTPUT:	CY = error (CX > 0)
;		CX = remaining bytes (or CX = 0 if no error NC)
; -----------------------------------------------------------------------------

CONRead:	push	ax		; push AX
		jcxz	CONRead4	; no data required

CONRead2:	call	GetCharWait	; get character
		cmp	al,EOF		; end of file?
		stc			; set error flag
		je	CONRead6	; end of text
		stosb			; store character
		loop	CONRead2	; get next character
CONRead4:	clc			; clear error flag

CONRead6:	pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                           Write to CON device
; -----------------------------------------------------------------------------
; INPUT:	BX = device index (0..15)
;		CX = number of bytes
;		DI = source buffer
; OUTPUT:	CY = error (CX > 0)
;		CX = remaining bytes (or CX = 0 if no error NC)
; -----------------------------------------------------------------------------

CONWrite:	push	si		; push SI
		mov	si,di		; SI <- source buffer
		call	DispConText	; display text to console
		xor	cx,cx		; CX <- 0, no data left
		pop	si		; pop SI
		ret
