; =============================================================================
;
;                          Litos8 display frame
;
; =============================================================================

; -----------------------------------------------------------------------------
; Exported user functions (7):
;	DispFrameChar - display frame character
;	DispFrameH - display horizontal frame line
;	DispFrameV - display vertical frame line
;	DispFrameHE - display horizontal frame line with edges
;	DispFrameVE - display vertical frame line with edges
;	DispFrame - display frame
;	DispTable - display table frame
; -----------------------------------------------------------------------------

		CODE_SECTION

; -----------------------------------------------------------------------------
;                          Display frame character
; -----------------------------------------------------------------------------
; INPUT:	AL = mask (FRAME_H,...FRAME_B)
;		AH = color attributes
;		DL = column
;		DH = row
; -----------------------------------------------------------------------------

; ------------- push registers

DispFrameChar:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX

; ------------- prepare frame mask -> BX

		and	al,FRAME_MASK	; mask frame bits
		xor	bx,bx		; BX <- 0
		mov	bl,al		; BX <- mask
		mov	al,[bx+FrameCharTab] ; AL <- frame character
		call	DispChar	; display character

; ------------- pop registers

		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                     Display horizontal frame line
; -----------------------------------------------------------------------------
; INPUT:	AL = mask (0 or FRAME_H)
;		AH = color attributes
;		CL = width (0...)
;		DL = column
;		DH = row
; -----------------------------------------------------------------------------

; ------------- push registers

DispFrameH:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX

; ------------- horizontal line

		mov	ch,0		; CH <- 0
		jcxz	DispFrameH8	; invalid width
		xor	bx,bx		; BX <- 0
		and	al,FRAME_H	; horizontal double flag
		or	al,FRAME_L+FRAME_R ; left and right
		mov	bl,al		; BX <- flags
		mov	al,[bx+FrameCharTab] ; AL <- frame character
DispFrameH2:	call	DispChar	; display character
		inc	dl		; DL <- next column
		loop	DispFrameH2	; next character

; ------------- pop registers

DispFrameH8:	pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                       Display vertical frame line
; -----------------------------------------------------------------------------
; INPUT:	AL = mask (0 or FRAME_V)
;		AH = color attributes
;		CH = height (0...)
;		DL = column
;		DH = row
; -----------------------------------------------------------------------------

; ------------- push registers

DispFrameV:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX

; ------------- vertical line

		mov	cl,ch		; CL <- height
		mov	ch,0		; CH <- 0
		jcxz	DispFrameV8	; invalid height
		xor	bx,bx		; BX <- 0
		and	al,FRAME_V	; vertical double flag
		or	al,FRAME_T+FRAME_B ; top and bottom
		mov	bl,al		; BX <- flags
		mov	al,[bx+FrameCharTab] ; AL <- frame character
DispFrameV2:	call	DispChar	; display character
		inc	dh		; DH <- next row
		loop	DispFrameV2	; next character

; ------------- pop registers

DispFrameV8:	pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                    Display horizontal frame line with edges
; -----------------------------------------------------------------------------
; INPUT:	AL = mask (FRAME_H,...FRAME_B)
;		AH = color attributes
;		CL = width (0...)
;		DL = column
;		DH = row
; -----------------------------------------------------------------------------

; ------------- push registers

DispFrameHE:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI

; ------------- prepare width -> CX

		mov	ch,0		; CH <- 0
		jcxz	DispFrameHE8	; invalid width

; ------------- prepare frame mask -> BX, SI

		and	al,FRAME_MASK	; AL <- frame mask
		xor	bx,bx		; BX <- 0
		mov	bl,al		; BX <- mask
		mov	si,bx		; SI <- save frame mask

; ------------- There is only 1 character

		dec	cx		; width - 1
		jz	short DispFrameHE6 ; there is only 1 character

; ------------- left edge

		or	bl,FRAME_R	; BX <- right flag
		mov	al,[bx+FrameCharTab] ; AL <- frame character
		call	DispChar	; display character
		inc	dl		; DL <- next column

; ------------- horizontal line

		dec	cx		; width - 1
		jz	short DispFrameHE4 ; there are only edges
		mov	bx,si		; BX <- frame mask
		and	bl,FRAME_H	; only horizontal double flag
		or	bl,FRAME_L+FRAME_R ; BX <- left and right flags
		mov	al,[bx+FrameCharTab] ; AL <- frame character
DispFrameHE2:	call	DispChar	; display character
		inc	dl		; DL <- next position
		loop	DispFrameHE2	; next character

; ------------- right edge

DispFrameHE4:	mov	bx,si		; BX <- frame mask
		or	bl,FRAME_L	; BX <- left flag
DispFrameHE6:	mov	al,[bx+FrameCharTab] ; AL <- frame character
		call	DispChar	; display character

; ------------- pop registers

DispFrameHE8:	pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                               Display frame
; -----------------------------------------------------------------------------
; INPUT:	AL = flags (FRAME_H,...FRAME_FILL)
;		AH = color attributes
;		CL = width (0...)
;		CH = height (0...)
;		DL = column
;		DH = row
; NOTES:	Use AL=0 for single frame or FRAME_DBL for double frame.
; -----------------------------------------------------------------------------

; ------------- check size

DispFrame:	cmp	cl,0		; check width
		je	short DispFrame9 ; invalid width
		cmp	ch,0		; check height
		je	short DispFrame9 ; invalid height

; ------------- only horizontal line

		cmp	ch,1		; height = 1?
		je	short DispFrameHE ; use only horizontal line

; ------------- only vertical line

		cmp	cl,1		; width = 1?
		je	short DispFrameVE ; use only vertical line

; ------------- push registers

		push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX

; ------------- top line (with edges)

		push	ax		; push AX
		or	al,FRAME_B	; bottom flag
		call	DispFrameHE	; display top line
		pop	ax		; pop AX
		inc	dh		; DH <- new row

; ------------- left and right line (without edges)

		sub	ch,2		; CH <- height without edges
		jz	short DispFrame4 ; invalid height
		call	DispFrameV	; display left line
		add	dl,cl		; DL <- right column + 1
		dec	dl		; DL <- right column
		call	DispFrameV	; display right line
		inc	dl		; DL <- right column + 1
		sub	dl,cl		; DL <- left column

; ------------- bottom line (with edges)

DispFrame4:	add	dh,ch		; DH <- bottom row
		or	al,FRAME_T	; add top neighbor
		call	DispFrameHE	; display bottom line

; ------------- fill frame inside

		sub	cl,2		; CL <- width without edges
		jz	short DispFrame6 ; no inner area
		or	ch,ch		; check height
		jz	short DispFrame6 ; no inner area
		test	al,FRAME_FILL	; fill frame?
		jz	short DispFrame6 ; no
		sub	dh,ch		; DH <- inner row
		inc	dx		; DL <- inner column
		mov	al,32		; AL <- space character
		call	DispFill	; fill inner area

; ------------- pop registers

DispFrame6:	pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
DispFrame9:	ret

; -----------------------------------------------------------------------------
;                   Display vertical frame line with edges
; -----------------------------------------------------------------------------
; INPUT:	AL = mask (FRAME_H,...FRAME_B)
;		AH = color attributes
;		CH = height (0...)
;		DL = column
;		DH = row
; -----------------------------------------------------------------------------

; ------------- push registers

DispFrameVE:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI

; ------------- prepare height -> CX

		mov	cl,ch		; CL <- height
		mov	ch,0		; CH <- 0
		jcxz	DispFrameVE8	; invalid height

; ------------- prepare frame mask -> BX, SI

		and	al,FRAME_MASK	; AL <- frame mask
		xor	bx,bx		; BX <- 0
		mov	bl,al		; BX <- mask
		mov	si,bx		; SI <- save frame mask

; ------------- There is only 1 character

		dec	cx		; height - 1
		jz	short DispFrameVE6 ; there is only 1 character

; ------------- top edge

		or	bl,FRAME_B	; BX <- bottom flag
		mov	al,[bx+FrameCharTab] ; AL <- frame character
		call	DispChar	; display character
		inc	dh		; DH <- next row

; ------------- vertical line

		dec	cx		; height - 1
		jz	short DispFrameVE4 ; there are only edges
		mov	bx,si		; BX <- frame mask
		and	bl,FRAME_V	; only vertical double flag
		or	bl,FRAME_T+FRAME_B ; BX <- top and bottom flags
		mov	al,[bx+FrameCharTab] ; AL <- frame character
DispFrameVE2:	call	DispChar	; display character
		inc	dh		; DH <- next row
		loop	DispFrameVE2	; next character

; ------------- bottom edge

DispFrameVE4:	mov	bx,si		; BX <- frame mask
		or	bl,FRAME_T	; BX <- top flag
DispFrameVE6:	mov	al,[bx+FrameCharTab] ; AL <- frame character
		call	DispChar	; display character

; ------------- pop registers

DispFrameVE8:	pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                             Display table frame
; -----------------------------------------------------------------------------
; INPUT:	AL = flags (0 or FRAME_FILL)
;		AH = color attributes
;		BX = table template
;			1 byte: number of columns NC (1..)
;				NC bytes: columns internal width,B7=double line
;			1 byte: number or rows NR (1..)
;				NR bytes: rows internal height, B7=double line
;		DL = column
;		DH = row
; NOTES:	First column and row sets double flag to end line, too.
; -----------------------------------------------------------------------------

; ------------- push registers

DispTable:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI

; ------------- prepare row pointer -> SI

		xor	cx,cx		; CX <- 0
		mov	cl,[bx]		; CX <- number of columns
		add	cx,bx		; CX <- end of columns
		inc	cx		; CX <- start of rows
		mov	si,cx		; SI <- start of rows

; ------------- prepare row counter -> CH

		mov	ch,[si]		; CH <- number of rows
		inc	si		; SI <- first row
		inc	ch		; CH <- number of rows + 1

; ------------- prepare vertical flags -> AL

		and	al,FRAME_FILL	; AL <- fill flag
		or	al,FRAME_B	; AL <- no top lines
DispTable2:	dec	ch		; row counter
		jnz	short DispTable3 ; not last row

		mov	cl,[bx]		; CX <- number of columns
		add	cx,bx		; CX <- end of columns
		inc	cx		; CX <- start of rows
		mov	si,cx		; SI <- start of rows
		inc	si		; SI <- first row
		mov	ch,0		; CH <- 0
		and	al,FRAME_FILL+FRAME_T ; delete bottom flag

DispTable3:	push	ax		; push AX, flags

; ------------- horizontal line of one row

		push	cx		; push CX
		push	dx		; push DX

		mov	ch,[si]		; CH <- row height
		and	al,FRAME_FILL+FRAME_B+FRAME_T ; delete unused flags
		rol	ch,1		; CH <- horizontal double flag
		and	ch,FRAME_H	; CH <- horizontal double flag
		or	al,ch		; AL <- set horizontal flag
		mov	di,bx		; DI <- columns
		mov	ch,[di]		; CH <- number of columns
		or	al,FRAME_R	; AL <- add right flag

; ------------- one cross character on horizontal line

DispTable4:   	inc	di		; DI <- next column
		mov	cl,[di]		; CL <- column width
		rol	cl,1
		rol	cl,1		; CL <- FRAME_V flag
		and	al,FRAME_FILL+FRAME_B+FRAME_T+FRAME_H+FRAME_L+FRAME_R
		and	cl,FRAME_V	; CL <- FRAME_V flag
		or	al,cl		; AL <- set FRAME_V flag
		call	DispFrameChar	; display frame character
		inc	dl		; DL <- next column

; ------------- horizontal line of one column

		mov	cl,[di]		; CL <- column width
		and	cl,7fh		; CL <- column width
		call	DispFrameH	; display horizontal line
		add	dl,cl		; DL <- next column
		or	al,FRAME_L	; AL <- add left flag
		dec	ch		; columns counter
		jnz	short DispTable4 ; next row

; ------------- last cross on horizontal line

		mov	cl,[bx+1]	; CL <- width of first column
		rol	cl,1
		rol	cl,1		; CL <- FRAME_V flag
		and	al,FRAME_FILL+FRAME_B+FRAME_T+FRAME_H+FRAME_L
		and	cl,FRAME_V	; CL <- FRAME_V flag
		or	al,cl		; AL <- set FRAME_V flag
		call	DispFrameChar	; display frame character

		pop	dx		; pop DX
		pop	cx		; pop CX
		inc	dh		; DH <- next row

; ------------- stop looping rows

		or	ch,ch		; last row?
		jz	short DispTable8 ; last row

; ------------- vertical lines of one row

		push	cx		; push CX
		push	dx		; push DX
		mov	ch,[si]		; CH <- row height
		and	ch,7fh		; CH <- row height
		mov	di,bx		; DI <- columns
		mov	cl,[di]		; CL <- number of columns

DispTable6:	inc	di		; DI <- next column
		push	ax		; push AX
		mov	al,[di]		; AL <- column width
		rol	al,1
		rol	al,1		; AL <- FRAME_V flag (B1)
		call	DispFrameV	; display vertical line
		pop	ax		; pop AX
		inc	dl		; DL <- next column

; ------------- fill table inside

		test	al,FRAME_FILL	; fill table?
		jz	short DispTable7 ; no fill table

		push	ax		; push AX
		push	cx		; push CX
		mov	cl,[di]		; CL <- column width
		and	cl,7fh		; CL <- column width
		mov	al,32		; AL <- space character
		call	DispFill	; fill table
		pop	cx		; pop CX
		pop	ax		; pop AX

; ------------- shift to next column

DispTable7:	push	ax		; push AX
		mov	al,[di]		; AL <- column width
		and	al,7fh		; AL <- column width
		add	dl,al		; DL <- shift column
		pop	ax		; pop AX
		dec	cl		; column counter
		jnz	short DispTable6 ; next column

; ------------- last vertical line

		mov	al,[bx+1]	; AL <- first column
		rol	al,1
		rol	al,1		; AL <- FRAME_V flag (B1)
		call	DispFrameV	; display last vertival line
		pop	dx		; pop DX
		add	dh,ch		; DH <- next row
		pop	cx		; pop CX

; ------------- next row

		pop	ax		; pop AX, flags
		inc	si		; SI <- next row
		or	al,FRAME_T	; add top line flags
		jmp	DispTable2	; next row

; ------------- pop registers

DispTable8:	pop	ax		; destroy AX, flags

		pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                            Constant data
; -----------------------------------------------------------------------------

		CONST_SECTION

; ------------- frame characters (IBM PC extension)
; IBM PC code page 437: http://en.wikipedia.org/wiki/Code_page_437
;
; B0: horizontal double
; B1: vertikal double
; B2: left
; B3: right
; B4: top
; B5: bottom
;			 -|   =|  -||  =||
FrameCharTab:	db	 32,  32,  32,  32	; no lines
		db	 32,  32,  32,  32	; left
		db	 32,  32,  32,  32	; right
FrameCharTab2:	db	196, 205, 196, 205	; left, right
		db	 32,  32,  32,  32	; top
		db	217, 190, 189, 188	; left, top
		db	192, 212, 211, 200	; right, top
		db	193, 207, 208, 202	; left, right, top
		db	 32,  32,  32,  32	; bottom
		db	191, 184, 183, 187	; left, bottom
		db	218, 213, 214, 201	; right, bottom
		db	194, 209, 210, 203	; left, right, bottom
		db	179, 179, 186, 186	; top, bottom
		db	180, 181, 182, 185	; left, top, bottom
		db	195, 198, 199, 204	; right, top, bottom
		db	197, 216, 215, 206	; left, right, top, bottom
