; ****************************************************************************
; TRDOS386.ASM (TRDOS 386 Kernel) - v2.0.0 - video.s
; ----------------------------------------------------------------------------
; Last Update: 09/12/2017
; ----------------------------------------------------------------------------
; Beginning: 16/01/2016
; ----------------------------------------------------------------------------
; Assembler: NASM version 2.11 (trdos386.s)
; ----------------------------------------------------------------------------
; Turkish Rational DOS
; Operating System Project v2.0 by ERDOGAN TAN (Beginning: 04/01/2016)
;
; Derived from 'Retro UNIX 386 Kernel - v0.2.1.0' source code by Erdogan Tan
; video.inc (13/08/2015)
;
; Derived from 'IBM PC-AT' BIOS source code (1985) 
; ****************************************************************************

; Retro UNIX 386 v1 Kernel - VIDEO.INC
; Last Modification: 13/08/2015
;		  (Video Data is in 'VIDATA.INC')
;
; ///////// VIDEO (CGA) FUNCTIONS ///////////////

; 16/01/2016 (32 bit modifications, TRDOS386 - TRDOS v2.0, video.s)
; INT 31H (TRDOS 386) = INT 10H (IBM PC/AT REAL MODE)

; IBM PC-AT BIOS Source Code
; TITLE VIDEO1 --- 06/10/85  VIDEO DISPLAY BIOS

_int10h:
	; 23/03/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	pushfd
	push 	cs
	call 	VIDEO_IO_1
	retn

;--- INT 10 H -------------------------------------------------------------------
; VIDEO_IO									:	
;	THESE ROUTINES PROVIDE THE CRT DISPLAY INTERFACE			:
;	THE FOLLOWING FUNCTIONS ARE PROVIDED:					:
;										:
;    (AH)= 00H	SET MODE (AL) CONTAINS MODE VALUE				:
;		(AL) = 00H  40X25 BW MODE (POWER ON DEFAULT)			:
;		(AL) = 01H  40X25 COLOR						:
;		(AL) = 02H  80X25 BW						:
;		(AL) = 03H  80X25 COLOR						:
;		              GRAPHICS MODES					:
;		(AL) = 04H  320X200 COLOR					:
;		(AL) = 05H  320X200 BW MODE					:
;		(AL) = 06H  640X200 BW MODE					:
;		(AL) = 07H   80X25 MONOCHROME (USED INTERNAL TO VIDEO ONLY)	:
;		*** NOTES -BW MODES OPERATE SAME AS COLOR MODES, BUT COLOR	:
;		           BURST IS NOT ENABLED					:
;		          -CURSOR IS NOT DISPLAYED IN GRAPHICS MODE		:
;    (AH)= 01H	SET CURSOR TYPE							:
;		(CH) = BITS 4-0 = START LINE FOR CURSOR				:
;		       ** HARDWARE WILL ALWAYS CAUSE BLINK			:
;		       ** SETTING BIT 5 OR 6 WILL CAUSE ERRATIC BLINKING	:
;		          OR NO CURSOR AT ALL					:
;		(CL) = BITS 4-0 = END LINE FOR CURSOR				:
;    (AH)= 02H	SET CURSOR POSITION						:
;		(DH,DL) = ROW,COLUMN  (00H,00H) IS UPPER LEFT			:
;		(BH) = A PAGE NUMBER (MUST BE 00H FOR GRAPHICS MODES)		:
;    (AH)= 03H	READ CURSOR POSITION						:
;		(BH) = PAGE NUMBER (MUST BE 00H FOR GRAPHICS MODES)		:
;		ON EXIT (DH,DL) = ROW,COLUMN OF CURRENT CURSOR			:
;		        (CH,CL) = CURSOR MODE CURRENTLY SET			:
;    (AH)= 04H	READ LIGHT PEN POSITION						:
;		ON EXIT:							:
;		(AH) = 00H -- LIGHT PEN SWITCH NOT DOWN/NOT TRIGGERED		:
;		(AH) = 01H -- VALID LIGHT PEN VALUE IN REGISTERS		:
;		        (DH,DL) = ROW,COLUMN OF CHARACTER LP POSITION		:
;		        (CH) = RASTER LINE (0-199)				:
;		        (BX) = PIXEL COLUMN (0-319,639)				:
;    (AH)= 05H	SELECT ACTIVE DISPLAY PAGE (VALID ONLY FOR ALPHA MODES)		:
;		(AL) = NEW PAGE VALUE (0-7 FOR MODES 0&1, 0-3 FOR MODES 2&3)	:
;    (AH)= 06H	SCROLL ACTIVE PAGE UP						:
;		(AL) = NUMBER OF LINES. ( LINES BLANKED AT BOTTOM OF WINDOW )	:
;		        (AL) = 00H MEANS BLANK ENTIRE WINDOW			:
;		(CH,CL) = ROW,COLUMN OF UPPER LEFT CORNER OF SCROLL		:
;		(DH,DL) = ROW,COLUMN OF LOWER RIGHT CORNER OF SCROLL		:
;		(BH) = ATTRIBUTE TO BE USED ON BLANK LINE			:
;    (AH)= 07H	SCROLL ACTIVE PAGE DOWN						:
;		(AL) = NUMBER OF LINES, INPUT LINES BLANKED AT TOP OF WINDOW	:
;		        (AL) = 00H MEANS BLANK ENTIRE WINDOW			:
;		(CH,CL) = ROW,COLUMN OF UPPER LEFT CORNER OF SCROLL		:
;		(DH,DL) = ROW,COLUMN OF LOWER RIGHT CORNER OF SCROLL		:
;		(BH) = ATTRIBUTE TO BE USED ON BLANK LINE			:
;										:
;   CHARACTER HANDLING ROUTINES							:
;										:
;    (AH)= 08H	READ ATTRIBUTE/CHARACTER AT CURRENT CURSOR POSITION		:
;		(BH) = DISPLAY PAGE (VALID FOR ALPHA MODES ONLY)		:
;		ON EXIT:							:
;		(AL) = CHAR READ						:
;		(AH) = ATTRIBUTE OF CHARACTER READ (ALPHA MODES ONLY)		:
;    (AH)= 09H	WRITE ATTRIBUTE/CHARACTER AT CURRENT CURSOR POSITION		:
;		(BH) = DISPLAY PAGE (VALID FOR ALPHA MODES ONLY)		:
;		(CX) = COUNT OF CHARACTERS TO WRITE				:
;		(AL) = CHAR TO WRITE						:
;		(BL) = ATTRIBUTE OF CHARACTER (ALPHA)/COLOR OF CHAR (GRAPHICS)	:
;		         SEE NOTE ON WRITE DOT FOR BIT 7 OF BL = 1.		:
;    (AH) = 0AH	WRITE CHARACTER ONLY AT CURRENT CURSOR POSITION			:
;		(BH) = DISPLAY PAGE (VALID FOR ALPHA MODES ONLY)		:
;		(CX) = COUNT OF CHARACTERS TO WRITE				:
;		(AL) = CHAR TO WRITE						:
;		       NOTE: USE FUNCTION (AH)= 09H IN GRAPHICS MODES		:
;	FOR READ/WRITE CHARACTER INTERFACE WHILE IN GRAPHICS MODE, THE		:
;		CHARACTERS ARE FORMED FROM A CHARACTER GENERATOR IMAGE		:
;		MAINTAINED IN THE SYSTEM ROM. ONLY THE 1ST 128 CHARS		:
;		ARE CONTAINED THERE. TO READ/WRITE THE SECOND 128 CHARS,	:
;		THE USER MUST INITIALIZE THE POINTER AT INTERRUPT 1FH		:
;		(LOCATION 0007CH) TO POINT TO THE 1K BYTE TABLE CONTAINING	:
;		THE CODE POINTS FOR THE SECOND 128 CHARS (128-255).		:
;	FOR WRITE CHARACTER INTERFACE IN GRAPHICS MODE, THE REPLICATION FACTOR	:
;		CONTAINED IN (CX) ON ENTRY WILL PRODUCE VALID RESULTS ONLY	:
;		FOR CHARACTERS CONTAINED ON THE SAME ROW. CONTINUATION TO	:
;		SUCCEEDING LINES WILL NOT PRODUCE CORRECTLY.			:
;										:
;    GRAPHICS INTERFACE								:
;    (AH)= 0BH	SET COLOR PALETTE						:
;		(BH) = PALETTE COLOR ID BEING SET (0-127)			:
;		(BL) = COLOR VALUE TO BE USED WITH THAT COLOR ID		:
;		       NOTE: FOR THE CURRENT COLOR CARD, THIS ENTRY POINT HAS	:
;		               MEANING ONLY FOR 320X200 GRAPHICS.		:
;		       COLOR ID = 0 SELECTS THE BACKGROUND COLOR (0-15)		:
;		       COLOR ID = 1 SELECTS THE PALETTE TO BE USED:		:
;		               0 = GREEN(1)/RED(2)/YELLOW(3)			:
;		               1 = CYAN(1)/MAGENTA(2)/WHITE(3)			:
;		       IN 40X25 OR 80X25 ALPHA MODES, THE VALUE SET FOR 	:
;		               PALETTE COLOR 0 INDICATES THE BORDER COLOR	:
;		               TO BE USED (VALUES 0-31, WHERE 16-31 SELECT	:
;		               THE HIGH INTENSITY BACKGROUND SET.		:
;    (AH)= 0CH	WRITE DOT							:
;		(DX) = ROW NUMBER						:
;		(CX) = COLUMN NUMBER						:
;		(AL) = COLOR VALUE						:
;		        IF BIT 7 OF AL = 1, THEN THE COLOR VALUE IS EXCLUSIVE	:
;		        ORed WITH THE CURRENT CONTENTS OF THE DOT		:
;    (AH)= ODH	READ DOT							:
;		(DX) = ROW NUMBER						:
;		(CX) = COLUMN NUMBER						:
;		(AL) = RETURNS THE DOT READ					:
;										:
;    ASCII TELETYPE ROUTINE FOR OUTPUT						:
;										:
;    (AH)= 0EH	WRITE TELETYPE TO ACTIVE PAGE					:
;		(AL) = CHAR TO WRITE						:
;		(BL) = FOREGROUND COLOR IN GRAPHICS MODE			:
;		NOTE -- SCREEN WIDTH IS CONTROLLED BY PREVIOUS MODE SET		:
;    (AH)= 0FH	CURRENT VIDEO STATE						:
;		RETURNS THE CURRENT VIDEO STATE					:
;		(AL) = MODE CURRENTLY SET ( SEE (AH)=00H FOR EXPLANATION)	:
;		(AH) = NUMBER OR CHARACTER COLUMNS ON SCREEN			:
;		(BH) = CURRENT ACTIVE DISPLAY PAGE				:
;    (AH)= 10H	RESERVED							:
;    (AH)= 11H	RESERVED							:
;    (AH)= 12H	RESERVED							:
;    (AH)= 13H	WRITE STRING							:
;			ES:BP  -  POINTER T0 STRING TO BE WRITTEN		:
;			CX     -  LENGTH OF CHARACTER STRING TO WRITTEN		:
;			DX     -  CURSOR POSITION FOR STRING TO BE WRITTEN	:
;			BH     -  PAGE NUMBER					:
;		(AL)= 00H	WRITE CHARACTER STRING				:
;			BL     -  ATTRIBUTE					:
;			STRING IS  <CHAR,CHAR, ... ,CHAR>			:
;			CURSOR NOT MOVED					:
;		(AL)= 01H	WRITE CHARACTER STRING AND MOVE CURSOR		:
;			BL     -  ATTRIBUTE					:
;			STRING IS  <CHAR,CHAR, ... ,CHAR>			:
;			CURSOR MOVED						:
;		(AL)= 02H	WRITE CHARACTER AND ATTRIBUTE STRING		:
;			       (VALID FOR ALPHA MODES ONLY)			:
;			STRING IS <CHAR,ATTR,CHAR,ATTR ..  ,CHAR,ATTR>		:
;			CURSOR IS NOT MOVED					:
;		(AL)= 03H WRITE CHARACTER AND ATTRIBUTE STRING AND MOVE CURSOR	:
;			       (VALID FOR ALPHA MODES ONLY)			:
;			STRING IS <CHAR,ATTR,CHAR,ATTR ..  ,CHAR,ATTR>		:
;			CURSOR IS MOVED						:
;		 NOTE:  CARRIAGE RETURN, LINE FEED, BACKSPACE, AND BELL ARE	:
;		        TREATED AS COMMANDS RATHER THAN PRINTABLE CHARACTERS.	:
;										:
;	BX,CX,DX,SI,DI,BP,SP,DS,ES,SS PRESERVED DURING CALLS EXCEPT FOR		:
;	BX,CX,DX RETURN VALUES ON FUNCTIONS 03H,04H,0DH AND 0FH. ON ALL CALLS	:
;	AX IS MODIFIED.								:
;--------------------------------------------------------------------------------

M1:	dd	SET_MODE	; TABLE OF ROUTINES WITHIN VIDEO I/O
	dd	SET_CTYPE
	dd	SET_CPOS
	dd	READ_CURSOR
	;dd	VIDEO_RETURN	; READ_LPEN
	dd	set_mode_ncm	; Set mode without clearing video memory
	dd	ACT_DISP_PAGE
	dd	SCROLL_UP
	dd	SCROLL_DOWN
	dd	READ_AC_CURRENT
	dd	WRITE_AC_CURRENT
	dd	WRITE_C_CURRENT
	dd	SET_COLOR
	dd	WRITE_DOT
	dd	READ_DOT
	dd	WRITE_TTY
	dd	VIDEO_STATE
	dd	vga_pal_funcs ; 10/08/2016 (TRDOS 386)
	dd	font_setup    ; 10/07/2016 (TRDOS 386)
	dd	VIDEO_RETURN	; RESERVED
	dd	WRITE_STRING  ; 23/06/2016 (TRDOS 386)
M1L	EQU	$ - M1

; 14/01/2017
; 02/01/2017
; 04/07/2016
; 12/05/2016
; 29/04/2016 - TRDOS 386 (TRDOS v2.0)
int31h:  ; Video BIOS

; BH = Video page number
; BL = Color/Attribute
; AH = Function number
; AL = Character

VIDEO_IO_1:
	;sti				; INTERRUPTS BACK ON
	cld				; SET DIRECTION FORWARD
	cmp	ah, M1L/4		; TEST FOR WITHIN TABLE RANGE
	jnb	short M4		; BRANCH TO EXIT IF NOT A VALID COMMAND

	push	es
	push	ds			; SAVE WORK AND PARAMETER REGISTERS
	push	edx
	push	ecx
	push	ebx
	push	esi
	push	edi
	push	ebp

	mov	si, KDATA 		; POINT DS: TO DATA SEGMENT
	mov	ds, si
	mov	es, si
	mov	edi, 0B8000h		; GET offset FOR COLOR CARD
	mov	[video_eax], eax ; 12/05/2016 
	; 23/03/2016
	shl	ah, 2  ; dword		; TIMES 2 FOR WORD TABLE LOOKUP
	movzx	esi, ah			; MOVE OFFSET INTO LOOK UP REGISTER (SI)
	;mov	ah, [CRT_MODE]		; MOVE CURRENT MODE INTO (AH) REGISTER

	;;15/01/2017
	; 14/01/2017
	; 02/01/2017
	;;mov	byte [intflg], 31h  ; video interrupt
	sti
	;

	JMP	dword [esi+M1]		; GO TO SELECTED FUNCTION

M4:					; COMMAND NOT VALID
	iretd				; DO NOTHING IF NOT IN VALID RANGE

VIDEO_STATE:
	; 26/06/2016
	; 12/05/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)

;---------------------------------------------------
; VIDEO STATE
;  RETURNS THE CURRENT VIDEO STATE IN AX
;  AH = NUMBER OF COLUMNS ON THE SCREEN
;  AL = CURRENT VIDEO MODE
;  BH = CURRENT ACTIVE PAGE
;---------------------------------------------------

	mov	ah, [CRT_COLS]	; GET NUMBER OF COLUMNS
	mov	al, [CRT_MODE]	; CURRENT MODE
	;movzx	esi, al
	;mov	ah, [esi+M6] 
	; BH = active page
	mov	bh, [ACTIVE_PAGE] ; GET CURRENT ACTIVE PAGE
	cli	; 02/01/2017
	pop	ebp		; RECOVER REGISTERS
	pop	edi
	pop	esi
	pop	ecx		; DISCARD SAVED BX
	jmp	short M15	; RETURN TO CALLER

set_mode_ncm:
	; 04/07/2016 - TRDOS 386 (TRDOS v2.0)
	; set mode without clearing the video memory
	; (ony for graphics modes)
	cmp	al, 7 ; IBM PC CGA modes
	jna	short SET_MODE ; normal function (clear)
	; do not clear memory
	mov	[noclearmem], al ; > 0
	call	_set_mode
	mov	byte [noclearmem], 0
        jmp     short VIDEO_RETURN

	; 10/08/2016
	; 08/08/2016
	; 30/07/2016
	; 29/07/2016
	; 27/07/2016
	; 26/07/2016
	; 25/07/2016
	; 23/07/2016
	; 18/07/2016
	; 02/07/2016
	; 26/06/2016
	; 24/06/2016
	; 29/05/2016 - TRDOS 386 (TRDOS v2.0)
SET_MODE:
	; For 32 bit TRDOS and Retro UNIX 386:
	;	valid video mode: 03h only!
	;	(VGA modes will be selected with another routine)
	;
	; set_txt_mode ; 80*25 (16 fore colors, 8 back colors)

;------------------------------------------------------
; SET MODE					      :
;	THIS ROUTINE INITIALIZES THE ATTACHMENT TO    :
;	THE SELECTED MODE, THE SCREEN IS BLANKED.     :
; INPUT						      :
;	(AL) - MODE SELECTED (RANGE 0-7)	      :
; OUTPUT					      :
;	NONE					      :
;------------------------------------------------------

	call	_set_mode ; 24/06/2016 (set_txt_mode)

; 12/05/2016
; 16/01/2016 (TRDOS 386 = TRDOS v2.0)

;-----	NORMAL RETURN FROM ALL VIDEO RETURNS

VIDEO_RETURN:
	mov	eax, [video_eax] ; 12/05/2016
_video_return:
	cli ; 02/01/2017
	pop	ebp
	pop	edi
	pop	esi
	pop	ebx
M15:	; VIDEO_RETURN_C
	;;15/01/2017
	; 02/01/2017
	;;mov	byte [intflg], 0
	;
	pop	ecx
	pop	edx
	pop	ds
	pop	es	; RECOVER SEGMENTS
	iretd		; ALL DONE

set_txt_mode:
	; 29/07/2016
	; 27/06/2016
	mov	al, 3

; 10/08/2016
; 08/08/2016
; 30/07/2016
; 29/07/2016
; 27/07/2016
; 26/07/2016
; 25/07/2016
; 23/07/2016
; 18/07/2016
; 07/07/2016
; 04/07/2016
; 03/07/2016
; 02/07/2016
; 26/06/2016
; 24/06/2016 (set_txt_mode -> _set_mode)
; 17/06/2016
; 29/05/2016
; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
_set_mode:
	; 24/06/2016
	cmp	[CRT_MODE], al  ; current mode = requested mode ?
	jne	short _sm_0
	cmp	al, 3		; text, 80*25 color, default mode
				; for TRDOS 386 MainProg
        jne     short _sm_2	;  multiscreen is only for mode 3

	; If '_set_mode' procedure is called for video mode 3
	;     while video mode is 3, video page will be cleared
	;     and cursor position of video page will be reset.	  
	
	; 29/07/2016
	or	byte [p_crt_mode], 80h ; clear page indicator
        jmp     short _sm_3
_sm_0:  
	cmp	byte [CRT_MODE], 3
        jne     short _sm_1

	; If '_set_mode' procedure is called for a video mode
	;     except video mode 3, while current video mode
	;     is 3; all video pages of mode 3 will be copied 
	;     to 98000h address as backup, before mode change.	

_sm_save_pm:
	; 03/07/2016
	; save video pages
	mov	esi, 0B8000h
	mov	edi, 98000h ; 30/07/2016
	mov	ecx, (0B8000h-0B0000h)/4
	rep	movsd

	mov	byte [p_crt_mode], 3 ; previous mode, backup sign
	;mov	cl, [ACTIVE_PAGE]
	;mov	[p_crt_page], cl

	; save cursor positions
	mov	esi, CURSOR_POSN
	mov	edi, cursor_pposn    ; cursor positions backup
	mov	cl, 4
	rep	movsd

	; 29/07/2016
	;mov	[ACTIVE_PAGE], cl ; 0
	xchg	cl, [ACTIVE_PAGE]
	mov	[p_crt_page], cl     ; previous page (for mode 3)	
	; [ACTIVE_PAGE] = 0 
	jmp	short _sm_2

_sm_1:
	cmp	al, 3		; text, 80*25 color, default mode
				; for TRDOS 386 MainProg
	jne	short _sm_2  ;  multiscreen is only for mode 3

	; If '_set_mode' procedure is called for video mode 3
	;     while video mode is not 3 and if there is video
	;     page backup for video mode 3, all (of 8) mode 3
	;     video pages will be restored from 98000h.

	cmp	byte [p_crt_mode], 3 ; previous mode, backup sign
	jne	short _sm_2 ; there is no (multiscreen) video pages
			 ; to be restored
	mov	cl, [p_crt_page]
	mov	[ACTIVE_PAGE], cl

_sm_2:
	mov	[CRT_MODE], al  ; save mode in global variable
_sm_3:
	; 30/07/2016
	; 26/07/2016
	; 25/07/2016
	; set_mode_vga:
	; 18/07/2016
	; 14/07/2016
	; 09/07/2016
	; 04/07/2016
	; 03/07/2016 (TRDOS 386 = TRDOS v2.0)
	; /// video mode 13h ///
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'vgatables.h'
	;
	; Oracle VirtualBox 5.0.24 VGABios Source Code
	; ('vgabios.c', 'vgatables.h', 'vgafonts.h', 'vgarom.asm')
	;
	mov	ah, al
	mov	ecx, vga_mode_count 	
	mov	esi, vga_modes
	xor	ebx, ebx
_sm_4:
	lodsb
	cmp	ah, al
	je	short _sm_5
	inc	bl
	loop	_sm_4

	; UNIMPLEMENTED VIDEO MODE !
	xor	eax, eax
        mov     [video_eax], eax ; 0 
	retn

;-----	eBX POINTS TO CORRECT ROW OF INITIALIZATION TABLE	

_sm_5: 	; 25/07/2016
	mov	esi, ebx
	add	esi, vga_memmodel
	mov	al, [esi]
	mov	[VGA_MTYPE], al

	mov	edi, ebx
	add	edi, vga_dac_s 	
	shl	bl, 2 ; byte -> dword
	add	ebx, vga_mode_tbl_ptr

	;mov	dword [VGA_BASE], 0B8000h
	;cmp	ah, 0Dh ; [CRT_MODE]
	;jb	short M9 
	;mov	dword [VGA_BASE], 0A0000h
;M9:
	mov	esi, [ebx]
	mov	ebx, esi
	add	esi, vga_p_cm_pos ; ebx + 20
	mov	ax, [esi]       ; get the cursor mode from the table
	mov	[CURSOR_MODE], ax ; save cursor mode (initial value)
	; al = 6, ah = 7
	; al = 0Dh, ah = 0Eh ; 25/07/2016
	call	cursor_shape_fix
	; al = 14, ah = 15  (If [CHAR_HEIGHT] = 16)
	mov	[esi], ax

	push	esi ; *	

	mov	ah, [VGA_MODESET_CTL]
	and	ah, 8 ; default palette loading ?
	jnz	short _sm_6
	mov	dx, 3C6h ; VGAREG_PEL_MASK (DAC mask register)
	mov	al, 0FFh ; PEL mask
	out	dx, al
	mov	ah, [edi] ; DAC model (selection number)
	call	load_dac_palette
	; ecx = 0
	test	byte [VGA_MODESET_CTL], 2 ; gray scale summing
	jz	short _sm_6
	push	ebx
	sub	ebx, ebx ; sub bl, bl
        mov     cx, 256 
	call	gray_scale_summing
	pop	ebx	
_sm_6:
	; Reset Attribute Ctl flip-flop
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
 	in	al, dx
	; Set Attribute Ctl
	mov	esi, ebx ; addr of params tbl for selected mode
	add	esi, 35  ; actl regs
	xor	ah, ah ; 0
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
_sm_7:
	mov	al, ah
	out	dx, al ; index
	lodsb
	; DX = 3C0h = VGAREG_ACTL_WRITE_DATA
	out	dx, al ; value
	inc	ah
	cmp	ah, 20 ; number of actl registers
	jb	short _sm_7
	;
	mov	al, ah ; 20
	out	dx, al ; index
	sub	al, al ; 0
	out	dx, al ; value
	;
	; Set Sequencer Ctl
	mov	esi, ebx ; addr of params tbl for selected mode
	add	esi, 5 ; sequ regs
	;
	mov	dx, 3C4h  ; VGAREG_SEQU_ADDRESS
	out	dx, al ; 0
	inc	dx ; 3C5h ; VGAREG_SEQU_DATA
	mov	al, 3
	out	dx, al
	mov	ah, 1	
_sm_8:
	mov	al, ah
	;mov	dx, 3C4h ; VGAREG_SEQU_ADDRESS
	dec	dx
	out	dx, al ; index
	lodsb
	inc	dx ; 3C5h ; VGAREG_SEQU_DATA
	out	dx, al
	cmp	ah, 4 ; number of sequ regs
	jnb	short _sm_9		
	inc	ah 
	jmp	short _sm_8
_sm_9:
	; Set Grafx Ctl
	mov	esi, ebx ; addr of params tbl for selected mode
	add	esi, 55 ; grdc regs
	xor	ah, ah ; 0
_sm_10:
	mov	al, ah
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	out	dx, al	
	lodsb
	inc	dx ; 3CFh ; VGAREG_GRDC_DATA
	out	dx, al
	inc	ah
	cmp	ah, 9 ; number of grdc regs
	jb	short _sm_10
	;
	; Disable CRTC write protection
	mov	dx, 3D4h  ; VGAREG_VGA_CRTC_ADDRESS
	;mov	al, 11h
	;our	dx, al
	;inc	dx
	;sub	al, al
	;out	dx, al
	mov	ax, 11h
	out	dx, ax
	mov	esi, ebx ; addr of params tbl for selected mode
	add	esi, 10 ; crtc regs
	; ah = 0
_sm_11:
	mov	al, ah
	; dx = 3D4h = VGAREG_VGA_CRTC_ADDRESS
	out	dx, al ; index
	lodsb
	inc	dx  ; VGAREG_VGA_CRTC_ADDRESS + 1
	out	dx, al ; value
	cmp	ah, 24 ; number of crtc registers - 1
	jnb	short _sm_12
	inc	ah
	dec	dx ; 3D4h
	jmp	short _sm_11
_sm_12:
	; Set the misc register
	mov	dx, 3CCh ; VGAREG_READ_MISC_OUTPUT
	mov	al, [ebx+9] ; misc reg
	out	dx, al
	;
	; Enable video
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	mov	al, 20h  
        out     dx, al   ; set bit 5 to 1
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	;
	cmp	byte [noclearmem], 0
        ja      short _sm_15

	; 29/07/2016
	xor	eax, eax
	mov	ecx, 4000h ; 16K words (32K)
	cmp     byte [VGA_MTYPE], 2  ; CTEXT, MTEXT, CGA
	ja	short _sm_14    ; no ? (0A0000h)
	mov	edi, 0B8000h
	je	short _sm_13	; CGA graphics mode
	; 08/08/2016
	mov	[VGA_INT43H], eax ; 0 ; default font 
	mov	ax, 0720h	; CGA text mode
_sm_13:
	rep	stosw
	jmp	short _sm_15		

_sm_14:
	mov	edi, 0A0000h
	; ecx = 16384 dwords  (64K)
	mov	dx, 3C4h ; VGAREG_SEQU_ADDRESS
	mov	al, 2
	out	dx, al
	;mov	dx, 3C5h ; VGAREG_SEQU_DATA
	inc	dx
	in	al, dx ; mmask
	push	ax
	mov	al, 0Fh ; all planes
	out	dx, al
	xor	al, al ; 0
	rep	stosd	; ecx = 163684 (64K)
	pop	ax
	out	dx, al  ; mmask
_sm_15:
	; ebx = addr of params tbl for selected mode
	; 10/08/2016
	mov	ax, [ebx] ; num of columns, 'twidth'
	mov	[CRT_COLS], al
	;; 26/07/2016
	;; CRTC_ADDRESS = 3D4h (always)
	;mov	ah, [ebx+1] ; num of rows, 'theightm1'
	inc	ah ; 09/07/2016
	mov	[VGA_ROWS], ah
	; 10/08/2016
	mov	al, [ebx+2]
	mov	[CHAR_HEIGHT], al 
	; 29/07/2016
	; length of regen buffer in bytes
	mov	cx, [ebx+3] ; 'slength_l'
	mov	[CRT_LEN], cx
	;
	; 27/07/2016
	xor	ah, ah
	mov	al, [ACTIVE_PAGE] ; may be > 0 for mode 3
	;mul	word [CRT_LEN] ; 4096 for mode 3
	mul	cx ; 29/07/2016
	mov	[CRT_START], ax
	;
	mov	al, 60h
	cmp	byte [noclearmem], 0
	jna	short _sm_16
	add	al, 80h
_sm_16:
	mov	[VGA_VIDEO_CTL], al
	mov	byte [VGA_SWITCHES], 0F9h
	and	byte [VGA_MODESET_CTL], 7Fh

	pop	esi ; *

	; 26/07/2016
	; 07/07/2016
	mov	cx, [CURSOR_MODE] ; restore cursor mode (initial value)
	xchg	cx, [esi] ; cl = start line, ch = end line
			  ; reset to initial value
	xchg 	ch, cl  ; ch = start line, cl = end line  
	mov	[CURSOR_MODE], cx ; save (fixed) cursor mode

	; 27/07/2016
	cmp	byte [VGA_MTYPE], 2 ; CTEXT, MTEXT
	jnb	short _sm_17

	; Set cursor shape
	;mov	cx, 0607h
	;call	_set_ctype

	; 29/07/2016
	mov	ah, 10	; 6845 register for cursor set
	call	m16	; output cx register
	
	; 25/07/2016
        cmp     byte [CRT_MODE], 03h
	jne	short _sm_17
	; 26/07/2016

	mov	al, [ACTIVE_PAGE]
	jmp	short _sm_18
_sm_17:
	; Set cursor pos for page 0..7
	sub	ax, ax ; eax = 0
	mov	edi, CURSOR_POSN
	stosd	
	stosd
	stosd
	stosd
	;; Set active page 0
	;mov	[ACTIVE_PAGE], al ; 0
_sm_18:
	; 29/07/2016
	cmp	byte [VGA_MTYPE], 2 ; CTEXT, MTEXT
        jnb	_sm_23

	;cmp	byte [CHAR_HEIGHT], 16
	;je	short _sm_19

 	;; copy and activate 8x16 font
	
	; 26/07/2016
	mov	al, 04h
	;sub	bl, bl
	; AX = 1104H ; Load ROM 8x16 Character Set
	; (BL = font block to load (EGA: 0-3; VGA: 0-7))
	call	load_text_8_16_pat

	; video_func_1103h:
	; biosfn_set_text_block_specifier:
	; BL = font block selector code	
	; NOTE: TRDOS 386 only uses and sets font block 0
	; (It is as BL = 0 for TRDOS 386)
	mov	dx, 3C4h ; VGAREG_SEQU_ADDRESS
	;mov	ah, bl
	sub	ah, ah ; 0
	mov	al, 03h
	out	dx, ax
_sm_19:
	; 29/07/2016
	; 26/07/2016
	; 24/06/2016
	;mov	edi, 0B8000h
	;mov	cx, 4000h ; 16K words (32K)
	;
	xor	al, al
        cmp     byte [p_crt_mode], al ; 0 
        ja      short _sm_20 ; 3h, 80h or 83h

	; 30/07/2016
	; 24/06/2016
	; TRDOS 386 (TRDOS v2) 'set mode' modification
	; (for multiscreen feature):
	; If '_set_mode' procedure is called for video mode 3
	;     while video mode is 3, video page will be cleared
	;     and cursor position of video page will be reset.	  
	; If '_set_mode' procedure is called for a video mode
	;     except video mode 3, while current video mode
	;     is 3; all video pages of mode 3 will be copied 
	;     to 98000h address as backup, before mode change.	 	
	; If '_set_mode' procedure is called for video mode 3
	;     while video mode is not 3 and if there is video
	;     page backup for video mode 3, all (of 8) mode 3
	;     video pages will be restored from 98000h.

	mov	[ACTIVE_PAGE], al ; 0
	;mov	ax, 0720h
	;;mov	cx, 4000h ; 16K words (32K)
	;;mov	edi, 0B8000h
	;rep	stosw
	;sub	al, al
	jmp	short _sm_23
_sm_20:
	; Previous video mode is 3
	; New video mode is 3 while current video mode is not 3
	; (multi screen) video pages will be restored from 0B0000h

	movzx	ebx, byte [ACTIVE_PAGE]
	shl	bl, 1 ; * 2
	add	ebx, CURSOR_POSN

	; 29/07/2016
	test    byte [p_crt_mode], 7Fh ; 83h or 3h
	jz	short _sm_21	; do not restore video pages

	;; restore video pages
	mov	esi, 98000h ; 30/07/2016 
	mov	edi, 0B8000h
	mov	cx, 2000h ; 8K dwords (32K)
	rep	movsd

	; restore cursor positions
	mov	esi, cursor_pposn
	mov	edi, CURSOR_POSN
	;mov	ecx, 4	; restore all cursor positions (16 bytes)
	mov	cl, 4
	rep 	movsd

        test    byte [p_crt_mode], 80h
	jz	short _sm_22 ; do not clear current video pages
_sm_21:
	; clear video page
	mov	cx, [CRT_LEN] ; 4096 
	shr	cx, 1 ; 2072
	mov	ax, 0720h
	mov	edi, 0B8000h ; [crt_base]
	add	di, [CRT_START]
	rep	stosw	; FILL THE REGEN BUFFER WITH BLANKS
	;
	mov	[ebx], cx ; reset cursor position
_sm_22:
	mov	[p_crt_mode], al ; 0
_sm_23:
	; al = video page number
	; [CRT_LEN] = length of regen buffer in bytes
	call	_set_active_page

;-----	NORMAL RETURN FROM ALL VIDEO RETURNS
	retn

cursor_shape_fix:
	; 07/07/2016
	; (Cursor start and cursor end line values -6,7-
	; will be fixed depending on character height)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', ' biosfn_set_cursor_shape (CH,CL)'
	;
	; INPUT ->
	;	AL = cursor start line (=6)
	;	AH = cursor end line (=7)
	; OUTPUT ->
	;	AL = cursor start line (=14)
	;	AH = cursor end line (=15)
	;
	;; if((modeset_ctl&0x01)&&(cheight>8)&&(CL<8)&&(CH<0x20))

	;test	byte [VGA_MODESET_CTL], 1 ; VGA active
	;jz	short csf_3
	cmp	byte [CHAR_HEIGHT], 8
	jna	short csf_3
	cmp	ah, 8
	jnb	short csf_3
	cmp	al, 20h
	jnb	short csf_3
	;
	push	ax
	; {
   	; if(CL!=(CH+1))	
	inc	al
	cmp	ah, al   ; ah != al + 1
        je      short csf_1
	; CH = ((CH+1) * cheight / 8) -1;
	mov	ah, [CHAR_HEIGHT]
	mul	ah
	shr	al, 3 ; / 8
	dec	al ; - 1
	jmp	short csf_2 
csf_1: 	
 	; }
   	; else		; ah = al + 1
    	; {
	inc	ah	; ah = ah + 1   
	; CH = ((CL+1) * cheight / 8) - 2;
	mov	al, [CHAR_HEIGHT]
	mul	ah
	shr	al, 3 ; / 8
	sub	al, 2 ; - 2
	; al = 14 (if [CHAR_HEIGHT] = 16)
csf_2:
	mov	[esp], al
	mov	ah, [esp+1]
	; CL = ((CL+1) * cheight / 8) - 1;
	inc	ah 
	mov	al, [CHAR_HEIGHT]
	mul	ah
	shr	al, 3 ; / 8
	dec	al ; - 1
	mov	[esp+1], al
	; ah = 15 (if [CHAR_HEIGHT] = 16)
	;
	pop	ax
csf_3:
	retn

SET_CTYPE:
	; 12/09/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	cmp	byte [CRT_MODE], 7
	ja	VIDEO_RETURN ; 12/09/2016
	call	_set_ctype
        jmp     VIDEO_RETURN

_set_ctype:
	; 02/09/2014 (Retro UNIX 386 v1)
	;
	; VIDEO.ASM - 06/10/85  VIDEO DISPLAY BIOS

	; (CH) = BITS 4-0 = START LINE FOR CURSOR
	;  ** HARDWARE WILL ALWAYS CAUSE BLINK
	;  ** SETTING BIT 5 OR 6 WILL CAUSE ERRATIC BLINKING
	;     OR NO CURSOR AT ALL
	; (CL) = BITS 4-0 = END LINE FOR CURSOR

;------------------------------------------------
; SET_CTYPE
;	THIS ROUTINE SETS THE CURSOR VALUE
; INPUT
;	(CX) HAS CURSOR VALUE CH-START LINE, CL-STOP LINE
; OUTPUT	
;	NONE
;------------------------------------------------

	; 07/07/2016
	; Fixing cursor start and stop line depending on
	; current character height (=16)
	; (Note: Default/initial values are 6 and 7.
	; If set values are 6 (start) & 7 (stop) and 
	; [CHAR_HEIGHT] = 16 :
	; After fixing, start line will be 14, stop line
	; will be 15.)
	mov	ax, cx
	xchg	al, ah
	; AL = start line, AH = stop line
	call	cursor_shape_fix
	; AL = start line (fixed), AH = stop line (fixed)
	mov	cx, ax
	xchg	ch, cl
	; CH = start line (fixed), CL = stop line (fixed)
	;
	mov	ah, 10	; 6845 register for cursor set
	mov	[CURSOR_MODE], cx ; save in data area
	;call	m16	; output cx register
	;retn
        jmp     m16

SET_CPOS:
	; 12/09/2016
	; 07/07/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	cmp	bh, 7 ; video page > 7 ; 07/07/2016
	ja	VIDEO_RETURN
	;
	cmp	byte [CRT_MODE], 7
	ja	short vga_set_cpos ; 12/09/2016	
	call	_set_cpos
        jmp     VIDEO_RETURN

vga_set_cpos:
	; 12/09/2016
	; 09/07/2016
	; set cursor position
	; NOTE: Hardware cursor position will not be set
	;   in any VGA modes (>7)
	;   But, cursor position will be saved into
	;   [CURSOR_POSN].
	;   TRDOS 386 (TRDOS v2.0) uses only one page
	;   (page 0) for all graphics modes.

	mov	[CURSOR_POSN], dx ; save cursor pos for pg 0
	; 04/08/2016
	;mov	bh, [ACTIVE_PAGE] ; = 0
	;call	_set_cpos
	jmp     VIDEO_RETURN

READ_CURSOR:
	; 12/09/2016
	; 07/07/2016
	; 12/05/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	;
	; VIDEO.ASM - 06/10/85  VIDEO DISPLAY BIOS

;------------------------------------------------------
; READ_CURSOR
;	THIS ROUTINE READS THE CURRENT CURSOR VALUE FROM THE
;	845, FORMATS IT, AND SENDS IT BACK TO THE CALLER
; INPUT
;	BH - PAGE OF CURSOR
; OUTPUT
;	DX - ROW, COLUMN OF THE CURRENT CURSOR POSITION
;	CX - CURRENT CURSOR MODE
;------------------------------------------------------

	; BH = Video page number (0 to 7)

	; 07/07/2016
	cmp	bh, 7 ; video page > 7 (invalid)
	jna	short read_cursor_1
	; invalid video page (input) 
	xor	ecx, ecx ; 0
	xor	edx, edx ; 0
	jmp	short read_cursor_2
read_cursor_1:
	; 12/09/2016
	cmp	byte [CRT_MODE], 7 ; vga mode
	ja	short vga_get_cpos
	;
	call	get_cpos
	movzx	ecx, word [CURSOR_MODE]
read_cursor_2:
	pop	ebp
	pop	edi
	pop	esi
	pop	ebx
	pop	eax  ; DISCARD SAVED CX AND DX
	pop	eax
	mov	eax, [video_eax] ; 12/05/2016
	;;15/01/2017
	;;mov	byte [intflg], 0 ; 07/01/2017
	pop	ds
	pop	es
	iretd

get_cpos:
	; 12/05/2016
	; 16/01/2016
	; BH = Video page number (0 to 7)
	;
	shl	bh, 1 ; WORD OFFSET
	movzx	esi, bh 
	movzx	edx, word [esi+CURSOR_POSN]
	retn

vga_get_cpos:
	; 12/09/2016
	; get cursor position (vga)
	movzx	edx, word [CURSOR_POSN] ; cursor pos for pg 0
	xor	ecx, ecx ; Cursor Mode = 0 (invalid)
	jmp     short read_cursor_2

ACT_DISP_PAGE:
	; 07/07/2016
	; 26/06/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	;
	; VIDEO.ASM - 06/10/85  VIDEO DISPLAY BIOS
	;
;-----------------------------------------------------
; ACT_DISP_PAGE
;	THIS ROUTINE SETS THE ACTIVE DISPLAY PAGE, ALLOWING
;	THE FULL USE OF THE MEMORY SET ASIDE FOR THE VIDEO ATTACHMENT
; INPUT
;	AL HAS THE NEW ACTIVE DISPLAY PAGE
; OUTPUT
;	THE 6845 IS RESET TO DISPLAY THAT PAGE
;-----------------------------------------------------
	; 07/07/2016
	cmp	al, 7	; > 7 = invalid video page number
	ja	VIDEO_RETURN
        cmp     byte [CRT_MODE], 3
	je	short adp_1
	and 	al, al
        jnz     VIDEO_RETURN
	;sub	al, al ; 0 ; force to page 0
adp_1:	
	call	set_active_page
        jmp     VIDEO_RETURN

set_active_page:   ; tty_sw
	; 09/12/2017
	; 26/07/2016
	; 26/06/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	; 30/06/2015
	; 04/03/2014  (act_disp_page --> tty_sw)
	; 10/12/2013
	; 04/12/2013
	;
	mov	[ACTIVE_PAGE], al ; save active page value ; [ptty]
_set_active_page:
	; 27/06/2015
	movzx	ebx, al
	;
	;cbw	; 07/09/2014 (ah=0)
	sub	ah, ah ; 09/12/2017
	mul	word [CRT_LEN]  ; get saved length of regen buffer
				; display page times regen length
	; 10/12/2013
	mov	[CRT_START], ax ; save start address for later
	mov	cx, ax ; start address to cx
_M16:
	;sar	cx, 1
	shr	cx, 1	; divide by 2 for 6845 handling
	mov	ah, 12	; 6845 register for start address
	call	m16
	;sal	bx, 1
	; 01/09/2014
	shl	bl, 1	; *2 for word offset
	add	ebx, CURSOR_POSN
	mov	dx, [ebx] ; get cursor for this page
	; 16/01/2016
	;call	m18
	;retn
	jmp	m18

position:
	; 24/06/2016
	; 12/05/2016 - TRDOS 386 (TRDOS v2.0)
	; 27/06/2015
	; 02/09/2014
	; 30/08/2014 (Retro UNIX 386 v1)
	; 04/12/2013 (Retro UNIX 8086 v1)
	;
	; VIDEO.ASM - 06/10/85  VIDEO DISPLAY BIOS
	;
;-----------------------------------------
; POSITION
;	THIS SERVICE ROUTINE CALCULATES THE REGEN BUFFER ADDRESS
;	OF A CHARACTER IN THE ALPHA MODE
; INPUT
;	AX = ROW, COLUMN POSITION
; OUTPUT
;	AX = OFFSET OF CHAR POSITION IN REGEN BUFFER
;-----------------------------------------

	; DX = ROW, COLUMN POSITION
	movzx	eax, byte [CRT_COLS] ; 24/06/2016
	mul	dh	 ; row value
	xor	dh, dh   ; 0	
	add	ax, dx	 ; add column value to the result
	shl	ax, 1	; * 2 for attribute bytes
		; EAX = AX = OFFSET OF CHAR POSITION IN REGEN BUFFER 
	retn

find_position:
	; 24/06/2016
	; 12/05/2016 - TRDOS 386 (TRDOS v2.0)
	; 27/06/2015
	; 07/09/2014
	; 02/09/2014
	; 30/08/2014 (Retro UNIX 386 v1)
	; VIDEO.ASM - 06/10/85  VIDEO DISPLAY BIOS

	movzx	ecx, bh ; video page number
	mov	esi, ecx
	shl	si, 1
	mov	dx, [esi+CURSOR_POSN]
	jz	short p21
	xor	si, si
p20:
	add	si, [CRT_LEN] ; 24/06/2016
	;add	si, 80*25*2 ; add length of buffer for one page		
	loop	p20
p21:
	and	dx, dx
	jz	short p22
	call 	position ; determine location in regen in page
	add	esi, eax ; add location to start of regen page
p22:	
	;mov	dx, [addr_6845] ; get base address of active display			
	;mov	dx, 03D4h ; I/O address of color card
	;add	dx, 6	; point at status port
	mov	dx, 03DAh ; status port
	; cx = 0
	retn

SCROLL_UP:
	; 07/07/2016
	; 26/06/2016
	; 12/05/2016
	; 30/01/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	; 07/09/2014
	; 02/09/2014
	; 01/09/2014 (Retro UNIX 386 v1 - beginning)
	; 04/04/2014
	; 04/12/2013
	;
	; VIDEO.ASM - 06/10/85  VIDEO DISPLAY BIOS
	;
;----------------------------------------------
; SCROLL UP
;	THIS ROUTINE MOVES A BLOCK OF CHARACTERS UP
;	ON THE SCREEN
; INPUT
;	(AH) = CURRENT CRT MODE
;	(AL) = NUMBER OF ROWS TO SCROLL
;	(CX) = ROW/COLUMN OF UPPER LEFT CORNER
;	(DX) = ROW/COLUMN OF LOWER RIGHT CORNER
;	(BH) = ATTRIBUTE TO BE USED ON BLANKED LINE
;	(DS) = DATA SEGMENT
;	(ES) = REGEN BUFFER SEGMENT
; OUTPUT
;	NONE -- THE REGEN BUFFER IS MODIFIED
;--------------------------------------------

	; 07/07/2016
	cmp	ch, dh
	ja	VIDEO_RETURN
	cmp	cl, dl
	ja	VIDEO_RETURN
	;
	call	_scroll_up
        jmp     VIDEO_RETURN

_scroll_up:  ; from 'write_tty'
	;
	; cl = left upper column
	; ch = left upper row
	; dl = right lower column
	; dh = right lower row
	;
	; al = line count 
	; bl = attribute to be used on blanked line	
	; bh = video page number (0 to 7)

	call	test_line_count ; 16/01/2016

	mov	ah, [CRT_MODE] ; current video mode	
	;cmp	ah, 4
 	;jb	short n0
	;cmp	byte [CRT_MODE], 4
        cmp	ah, 4 ; 07/07/2016
	jnb     GRAPHICS_UP ; 26/06/2016

	;cmp	ah, 7 ; TEST FOR BW CARD
	;jne	GRAPHICS_UP
n0:
	; 07/07/2016
	cmp	bh, 7 ; video page number
	jna	short n1
	mov	bh, [ACTIVE_PAGE]
n1:
	mov	ah, bl ; attribute
	push	ax ; *
	;mov 	esi, [CRT_BASE]
        mov     esi, 0B8000h  
        cmp     bh, [ACTIVE_PAGE]
	jne	short n2
	;
        mov     ax, [CRT_START]
        add     si, ax
        jmp     short n4
n2:
        and     bh, bh
	jz	short n4
	mov	al, bh
n3:
        add	si, [CRT_LEN]
        dec	al
	jnz	short n3
n4:	
	call	scroll_position ; 16/01/2016
        jz      short n6 

        add     esi, ecx ; from address for scroll
	mov	ch, dh  ; #rows in block
	sub	ch, al	; #rows to be moved
n5:
	call	n10 ; 16/01/2016
	
        push	ecx
	movzx	ecx, byte [CRT_COLS] 
	add	cl, cl
        add	esi, ecx  ; next line
        add	edi, ecx
	pop	ecx

	dec	ch	 ; count of lines to move
	jnz	short n5 ; row loop
	; ch = 0
	mov	dh, al	 ; #rows	
n6:
	; attribute in ah
	mov	al, ' '	 ; fill with blanks
n7:
	call	n11 ; 16/01/2016

	mov	cl, [CRT_COLS]
	add	cl, cl
        add	edi, ecx

	dec	dh
	jnz	short n7
n16:
	cmp	bh, [ACTIVE_PAGE]
	jne	short n8
	
	;cmp	byte [CRT_MODE], 7	; is this the black and white card
	;je	short n8		; if so, skip the mode reset

	mov	al, [CRT_MODE_SET] ; get the value of mode set
	mov	dx, 03D8h ; always set color card port
	out	dx, al
n8:
	retn

test_line_count:
	; 12/05/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	; 07/09/2014 (scroll_up)
	or	al, al
	jz	short al_set2
	push	dx
	sub	dh, ch  ; subtract upper row from lower row number
	inc	dh	; adjust difference by 1
	cmp	dh, al 	; line count = amount of rows in window?
	jne	short al_set1 ; if not the we're all set
	xor	al, al	; otherwise set al to zero
al_set1:
	pop	dx
al_set2:
	retn

scroll_position:
	; 26/06/2016
	; 30/01/2016
        ; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	; 07/09/2014 (scroll_up)

	push	dx
	mov	dx, cx	; now, upper left position in DX
	call	position
	add	esi, eax
	mov	edi, esi
	pop	dx	; lower right position in DX
	sub	dx, cx
	inc	dh	; dh = #rows 
	inc	dl	; dl = #cols in block
	pop	ecx 	; return address
	pop	ax	; * ; al = line count, ah = attribute
	push	ecx	; return address
	movzx	ecx, ax
	mov	ah, [CRT_COLS]
	mul	ah	; determine offset to from address
	add	ax, ax  ; *2 for attribute byte
	;
	push	ax	; offset 
	push	dx
	;
	; 04/04/2014
	mov	dx, 3DAh ; guaranteed to be color card here	
n9:                      ; wait_display_enable
        in      al, dx   ; get port
	test	al, RVRT ; wait for vertical retrace	
	jz	short n9 ; wait_display_enable
	mov	al, 25h
	mov	dl, 0D8h ; address control port
	out	dx, al	; turn off video during vertical retrace
	pop	dx	; #rows, #cols
       	pop	ax	; offset
	xchg	ax, cx	; 
	; ecx = offset, al = line count, ah = attribute
	;
	or	al, al
	retn
n10:
	; Move rows
	mov	cl, dl	; get # of cols to move
	push	esi
	push	edi	; save start address
n10r:
	movsw		; move that line on screen
	dec	cl
        jnz     short n10r
	pop	edi
	pop	esi	; recover addresses
	retn
n11:
	; Clear rows
                	; dh =  #rows
        mov	cl, dl	; get # of cols to clear
        push    edi     ; save address
n11r:
        stosw           ; store fill character
	dec	cl
        jnz     short n11r
        pop     edi     ; recover address
	retn

SCROLL_DOWN:
	; 07/07/2016
	; 27/06/2016
	; 26/06/2016
	; 12/05/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	;
	; VIDEO.ASM - 06/10/85  VIDEO DISPLAY BIOS

;------------------------------------------
; SCROLL DOWN
;	THIS ROUTINE MOVES THE CHARACTERS WITHIN A DEFINED
;	BLOCK DOWN ON THE SCREEN, FILLING THE TOP LINES
;	WITH A DEFINED CHARACTER
; INPUT
;	(AH) = CURRENT CRT MODE
;	(AL) = NUMBER OF LINES TO SCROLL
;	(CX) = UPPER LEFT CORNER OF RECION
;	(DX) = LOWER RIGHT CORNER OF REGION
;	(BH) = FILL CHARACTER
;	(DS) = DATA SEGMENT
;	(ES) = REGEN SEGMENT
; OUTPUT
;	NONE -- SCREEN IS SCROLLED
;------------------------------------------

	; 07/07/2016
	cmp	ch, dh
	ja	VIDEO_RETURN
	cmp	cl, dl
	ja	VIDEO_RETURN
	;
	call	_scroll_down
        jmp     VIDEO_RETURN

_scroll_down: ; 27/06/2016

	; cl = left upper column
	; ch = left upper row
	; dl = right lower column
	; dh = right lower row
	;
	; al = line count 
	; bl = attribute to be used on blanked line	
	; bh = video page number (0 to 7)

	; !!!!
	std		; DIRECTION FOR SCROLL DOWN
	; !!!!
	call	test_line_count ; 16/01/2016
	
	mov	ah, [CRT_MODE] ; current video mode	
	;cmp	ah, 4
 	;jb	short _n0
	;cmp	byte [CRT_MODE], 4
        cmp	ah, 4 ; 07/07/2016
	jnb     GRAPHICS_DOWN ; 26/06/2016

	;cmp	ah, 7 ; TEST FOR BW CARD
	;jne	GRAPHICS_DOWN
_n0:
	; 07/07/2016
	cmp	bh, 7 ; video page number
	jna	short n12
	mov	bh, [ACTIVE_PAGE]
	;
n12:			; CONTINUE_DOWN
	mov	ah, bl
	push	ax	; * ; save attribute in ah
	mov	ax, dx	; LOWER RIGHT CORNER
	call	scroll_position	; GET REGEN LOCATION
	jz	short n14
	sub	esi, ecx  ; SI IS FROM ADDRESS
	mov	ch, dh  ; #rows in block
	sub	ch, al	; #rows to be moved
n13:
	call	n10	; MOVE ONE ROW

	push	ecx
	mov	cl, [CRT_COLS] 
	add	cl, cl
        sub	esi, ecx  ; next line
        sub	edi, ecx
        pop	ecx
	
	dec	ch	 ; count of lines to move
	jnz	short n13 ; row loop
	; ch = 0
	mov	dh, al	 ; #rows
n14:
	; attribute in ah
	mov	al, ' '	 ; fill with blanks
n15:
	call	n11 ; 16/01/2016

	mov	cl, [CRT_COLS]
	add	cl, cl
        sub	edi, ecx
        
	dec	dh
	jnz	short n15
	;
	jmp	n16 ; 27/06/2016

;	cmp	bh, [ACTIVE_PAGE]
;	jne	short n16
;
;	;cmp	byte [CRT_MODE], 7	; is this the black and white card
;	;je	short n16		; if so, skip the mode reset
;
;	mov	al, [CRT_MODE_SET] ; get the value of mode set
;	mov	dx, 03D8h ; always set color card port
;	out	dx, al
;n16:
;	; !!!!
;	cld		; Clear direction flag !
;	; !!!!
;	retn

READ_AC_CURRENT:
	; 08/07/2016
	; 26/06/2016
	; 12/05/2016
	; 18/01/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	;
	; VIDEO.ASM - 06/10/85  VIDEO DISPLAY BIOS
	;
	; 08/07/2016
        cmp     byte [CRT_MODE], 7 ; 6!?
	jna	short read_ac_c
	xor	eax, eax
        jmp     _video_return 
read_ac_c:
	call	_read_ac_current
	; 12/05/2016
        ;jmp     VIDEO_RETURN
	jmp	_video_return

;------------------------------------------------------------------------
; READ_AC_CURRENT							:
;	THIS ROUTINE READS THE ATTRIBUTE AND CHARACTER AT THE CURRENT	:
;	CURSOR POSITION AND RETURNS THEM TO THE CALLER			:
; INPUT									:
;	(AH) = CURRENT CRT MODE						:
;	(BH) = DISPLAY PAGE ( ALPHA MODES ONLY )			:
;	(DS) = DATA SEGMENT						:
;	(ES) = REGEN SEGMENT						:
; OUTPUT								:
;	(AL) = CHARACTER READ						:
;	(AH) = ATTRIBUTE READ						:
;------------------------------------------------------------------------

_read_ac_current:
	; 26/06/2016
	; 12/05/2016 
	; 18/01/2016

	;mov	ah, [CRT_MODE] ; current video mode	
	;cmp	ah, 4
 	;jb	short p10
	cmp	byte [CRT_MODE], 4
	jnb     GRAPHICS_READ ; 26/06/2016

	;cmp	ah, 7 ; TEST FOR BW CARD
	;jne	GRAPHICS_READ
p10:
	call	find_position	; GET REGEN LOCATION AND PORT ADDRESS
	;
	; esi = regen location
	; dx = status port
	;
	mov	ah, [CRT_MODE]	
	sub	ah, 2
	shr	ah, 1
	jnz	short p13

	; WAIT FOR HORIZONTAL RETRACE OR VERTICAL RETRACE IF COLOR 80
p11:
	sti		; enable interrupts first
	cmp     bh, [ACTIVE_PAGE]
	jne	short p13 
	cli 		; block interrupts for single loop
	in	al, dx	; get status from the adapter
	test	al, RHRZ ; is horizontal retrace low
	jnz	short p11 ; wait until it is
p12:			;  wait for either retrace high
	in	al, dx ; get status again
	test	al, RVRT+RHRZ ; is horizontal or vertical retrace high
	jz	short p12 ; wait until either retrace active
	sti
p13:
	add	esi, 0B8000h 
	mov	ax, [esi]

	retn	; 18/01/2016

WRITE_AC_CURRENT:
	; 08/07/2016
	; 26/06/2016
	; 24/06/2016
	; 12/05/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	;
	; VIDEO.ASM - 06/10/85  VIDEO DISPLAY BIOS
	;
;----------------------------------------------------------------
; WRITE_AC_CURRENT						:
;	THTS ROUTINE WRITES THE ATTRIBUTE AND CHARACTER		:
;	AT THE CURRENT CURSOR POSITION				:
; INPUT								:
;	(AH) = CURRENT CRT MODE					:
;	(BH) = DISPLAY PAGE					:
;	(CX) = COUNT OF CHARACTERS TO WRITE			:
;	(AL) = CHAR TO WRITE					:
;	(BL) = ATTRIBUTE OF CHAR TO WRITE			:
;	(DS) = DATA SEGMENT					:
;	(ES) = REGEN SEGMENT					:
; OUTPUT							:
;	DISPLAY REGEN BUFFER UPDATED				:
;----------------------------------------------------------------

	; 08/07/2016
	cmp	byte [CRT_MODE], 7 ; 6!?
	jna	short write_ac_c

	call	vga_write_char_attr
	jmp     VIDEO_RETURN	

write_ac_c:
	call	_write_c_current

	movzx	esi, bh ; video page number (0 to 7)	
	mov	[esi+chr_attrib], bl ; color/attribute

        jmp     VIDEO_RETURN

WRITE_C_CURRENT:
	; 08/07/2016
	; 26/06/2016
	; 12/05/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	;
	; VIDEO.ASM - 06/10/85  VIDEO DISPLAY BIOS
	;
;----------------------------------------------------------------
; WRITE_C_CURRENT						:
;	THIS ROUTINE WRITES THE CHARACTER AT			:
;	THE CURRENT CURSOR POSITION, ATTRIBUTE UNCHANGED	:
; INPUT								:
;	(AH) = CURRENT CRT MODE					:
;	(BH) = DISPLAY PAGE					:
;	(CX) = COUNT OF CHARACTERS TO WRITE			:
;	(AL) = CHAR TO WRITE					:
;	(DS) = DATA SEGMENT					:
;	(ES) = REGEN SEGMENT					:
; OUTPUT							:
;	DISPLAY REGEN BUFFER UPDATED				:
;----------------------------------------------------------------

	; 08/07/2016
	cmp	byte [CRT_MODE], 7 ; 6!?
	jna	short write_c_c

	call	vga_write_char_only
	jmp     VIDEO_RETURN	

write_c_c:
	;and	bh, 7 ; video page number (<= 7)
	movzx	esi, bh	
	mov	bl, [esi+chr_attrib]

	call	_write_c_current
        jmp     VIDEO_RETURN

_write_c_current:  ; from 'write_tty'
	; 26/06/2016
	; 24/06/2016
	; 12/05/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	; 30/08/2014 (Retro UNIX 386 v1)
	; 18/01/2014
	; 04/12/2013
	;
	; VIDEO.ASM - 06/10/85  VIDEO DISPLAY BIOS

	;mov	ah, [CRT_MODE] ; current video mode	
	;cmp	ah, 4
 	;jb	short p40
	cmp	byte [CRT_MODE], 4
        jnb     GRAPHICS_WRITE ; 26/06/2016

	;cmp	ah, 7 ; TEST FOR BW CARD
	;jne	GRAPHICS_WRITE
p40:
	; al = character
	; bl = color/attribute
	; bh = video page
	; cx = count of characters to write
	push	dx
	mov	ah, bl  ; color/attribute (12/05/2016)
	push	ax	; save character & attribute/color
	push	cx
	call 	find_position  ; get regen location and port address
	pop	cx
	; esi = regen location
	; dx = status port
	;
	add	esi, 0B8000h ; 30/08/2014 (crt_base) 
	;
	mov	ah, [CRT_MODE]	
	sub	ah, 2
	shr	ah, 1
	jnz	short p44    ; 26/06/2016

	; WAIT FOR HORIZONTAL RETRACE OR VERTICAL RETRACE IF COLOR 80
p41:
	sti		; enable interrupts first
	cmp     bh, [ACTIVE_PAGE]
	jne	short p44 
	cli 		; block interrupts for single loop
	in	al, dx	; get status from the adapter
	test	al, RVRT ; check for vertical retrace first
	jnz	short p43 ; Do fast write now if vertical retrace
	test	al, RHRZ ; is horizontal retrace low
	jnz	short p41 ; wait until it is
p42:			;  wait for either retrace high
	in	al, dx ; get status again
	test	al, RVRT+RHRZ ; is horizontal or vertical retrace high
	jz	short p42 ; wait until either retrace active
p43:	
	sti
p44:
	mov	ax, [esp] ; restore the character (al) & attribute (ah)
	mov	[esi], ax

	dec	cx
	jz	short p45

	inc	esi
	inc	esi
	jmp	short p41
p45:	
	pop	ax
	pop	dx
	retn

; 09/07/2016
; 26/06/2016
; 24/06/2016
; 12/05/2016
; 18/01/2016
; 16/01/2016 - TRDOS 386 (TRDOS v2.0)
; 30/06/2015
; 27/06/2015
; 11/03/2015
; 02/09/2014
; 30/08/2014
; VIDEO FUNCTIONS
; (write_tty - Retro UNIX 8086 v1 - U9.ASM, 01/02/2014)

WRITE_TTY:
	; 09/12/2017
	; 09/07/2016
	; 01/07/2016
	; 26/06/2016
	; 24/06/2016
	; 13/05/2016
	; 12/05/2016
	; 30/01/2016
	; 18/01/2016
	; 16/01/2016 (TRDOS 386 = TRDOS v2.0)
	; 13/08/2015
	; 02/09/2014
	; 30/08/2014 (Retro UNIX 386 v1 - beginning)
	; 01/02/2014 (Retro UNIX 8086 v1 - last update)
	; 03/12/2013 (Retro UNIX 8086 v1 - beginning)	
	; (Modified registers: EAX, EBX, ECX, EDX, ESI, EDI)
	;
	; INPUT -> AL = Character to be written
	;	   BL = Color (Forecolor, Backcolor)
	;	   BH = Video Page (0 to 7)

	; 09/07/2016
        cmp     byte [CRT_MODE], 7
	jna 	short write_tty_cga

	call	vga_write_teletype
	jmp	VIDEO_RETURN

write_tty_cga:
	; 13/05/2016
	;call	_write_tty
	; 01/07/2016
	call	_write_tty_m3
	jmp	VIDEO_RETURN

RVRT	equ	00001000b	; VIDEO VERTICAL RETRACE BIT
RHRZ	equ	00000001b	; VIDEO HORIZONTAL RETRACE BIT

; Derived from "WRITE_TTY" procedure of IBM "pc-at" rombios source code
; (06/10/1985), 'video.asm', INT 10H, VIDEO_IO
;
; 06/10/85  VIDEO DISPLAY BIOS
;
;--- WRITE_TTY ------------------------------------------------------------------
;										:
;   THIS INTERFACE PROVIDES A TELETYPE LIKE INTERFACE TO THE			:
;   VIDEO CARDS. THE INPUT CHARACTER IS WRITTEN TO THE CURRENT			:
;   CURSOR POSITION, AND THE CURSOR IS MOVED TO THE NEXT POSITION.		:
;   IF THE CURSOR LEAVES THE LAST COLUMN OF THE FIELD, THE COLUMN		:
;   IS SET TO ZERO, AND THE ROW VALUE IS INCREMENTED. IF THE ROW		:
;   ROW VALUE LEAVES THE FIELD, THE CURSOR IS PLACED ON THE LAST ROW,		:
;   FIRST COLUMN, AND THE ENTIRE SCREEN IS SCROLLED UP ONE LINE.		:
;   WHEN THE SCREEN IS SCROLLED UP, THE ATTRIBUTE FOR FILLING THE		:
;   NEWLY BLANKED LINE IS READ FROM THE CURSOR POSITION ON THE PREVIOUS		:
;   LINE BEFORE THE SCROLL, IN CHARACTER MODE. IN GRAPHICS MODE,		:
;   THE 0 COLOR IS USED.							:
;   ENTRY --									:
;     (AH) = CURRENT CRT MODE							:
;     (AL) = CHARACTER TO BE WRITTEN						:
;	    NOTE THAT BACK SPACE, CARRIAGE RETURN, BELL AND LINE FEED ARE	:
;	    HANDLED AS COMMANDS RATHER THAN AS DISPLAY GRAPHICS CHARACTERS	:
;     (BL) = FOREGROUND COLOR FOR CHAR WRITE IF CURRENTLY IN A GRAPHICS MODE	:
;   EXIT -- 									:
;     ALL REGISTERS SAVED							:
;--------------------------------------------------------------------------------

; 09/12/2017
; 08/07/2016
; 26/06/2016
; 24/06/2016
_write_tty:	; 13/05/2016
	cli
	;
	; 01/09/2014
	cmp	byte [CRT_MODE], 3
	je	short _write_tty_m3
	;
set_mode_3:
	push	ebx
	push	eax
	call	_set_mode
	pop	eax
	pop	ebx
	;
_write_tty_m3: ; 24/06/2016 (m3 -> _write_tty_m3)
	movzx 	esi, bh ; 12/05/2016
	shl	si, 1
	add	esi, CURSOR_POSN
	mov	dx, [esi]
	;
	; dx now has the current cursor position
	;
	cmp	al, 0Dh	; CR	; is it carriage return or control character
	jbe	short u8
	;
	; write the char to the screen
u0:	
	; al = character
	; bl = attribute/color
	; bh = video page number (0 to 7)
	;
	mov	cx, 1  ; 24/06/2016
	; cx = count of characters to write
	;
	call	_write_c_current ; 16/01/2015
	;
	; position the cursor for next char
	inc	dl		; next column
	cmp	dl, [CRT_COLS]  ; test for column overflow 
        jne     _set_cpos
	mov	dl, 0		; column = 0
u10:				; (line feed found)
	cmp	dh, 25-1 	; check for last row
	jb 	short u6
	;
	; scroll required
u1:	
	; SET CURSOR POSITION (04/12/2013)
	call	_set_cpos
	;
	; determine value to fill with during scroll
u2:
	; bh = video page number
	;
	call	_read_ac_current ; 18/01/2016
	;
	; al = character, ah = attribute
	; bh = video page number 	
u3:
	;;mov	ax, 0601h 	; scroll one line
	;;sub	cx, cx		; upper left corner
	;;mov	dh, 25-1 	; lower right row
	;;;mov	dl, [CRT_COLS]
	;mov	dl, 80		; lower right column	
	;;dec	dl
	;;mov	dl, 79

	;;call	scroll_up	; 04/12/2013
	;;; 11/03/2015
	; 02/09/2014
	;;;mov	cx, [crt_ulc] ; Upper left corner  (0000h)
	;;;mov	dx, [crt_lrc] ; Lower right corner (184Fh)
	; 11/03/2015
	sub	cx, cx
	mov	dx, 184Fh ; dl = 79 (column), dh = 24 (row)
	;
	mov	al, 1		; scroll 1 line up
		; ah = attribute
	;mov	bl, al ; 12/05/2016
	jmp	_scroll_up	; 16/01/2016
;u4:
	;;int	10h		; video-call return
				; scroll up the screen
				; tty return
;u5:
	;retn			; return to the caller

u6:				; set-cursor-inc
	inc	dh		; next row
				; set cursor
;u7:					
	;;mov	ah, 02h
	;;jmp	short u4 	; establish the new cursor
	;call	_set_cpos
	;jmp 	short u5
	jmp     _set_cpos

	; check for control characters
u8:
	je	short u9
	cmp	al, 0Ah		; is it a line feed (0Ah)
	je	short u10
	cmp	al, 07h 	; is it a bell
	je	short u11
	cmp	al, 08h		; is it a backspace
	;jne	short u0
	je	short bs	; 12/12/2013
	; 12/12/2013 (tab stop)
	cmp	al, 09h		; is it a tab stop
	jne	short u0
	mov	al, dl
	;cbw
	xor	ah, ah ; 09/12/2017
	mov	cl, 8
	div	cl
	sub	cl, ah
ts:
	; 02/09/2014
	; 01/09/2014
	mov	al, 20h
tsloop:
	push	cx
	push	ax
	;mov	bh, [ACTIVE_PAGE]
	call	_write_tty_m3 ; 24/06/2016 (m3 -> _write_tty_m3)
	pop	ax  ; ah = attribute/color
	pop	cx
	dec	cl
	jnz	short tsloop
	retn
bs:	
	; back space found

	or	dl, dl 		; is it already at start of line
	;je	short u7 	; set_cursor
	jz	short _set_cpos
	dec	dx     		; no -- just move it back
	;jmp	short u7
	jmp	short _set_cpos

	; carriage return found
u9:
	mov	dl, 0 		; move to first column
	;jmp	short u7
	;jmp	short _set_cpos ; 30/01/2016

	; line feed found
;u10:
;	cmp	dh, 25-1 	; bottom of screen
;	jne	short u6 	; no, just set the cursor
;       jmp     u1              ; yes, scroll the screen

_set_cpos:
	; 12/05/2016 - TRDOS 386 (TRDOS v2.0)
	; 27/06/2015
	; 01/09/2014
	; 30/08/2014 (Retro UNIX 386 v1)
	;
	; 04/12/2013 - 12/12/2013 (Retro UNIX 8086 v1) 
	;
	; VIDEO.ASM - 06/10/85  VIDEO DISPLAY BIOS
	;
;----------------------------------------------
; SET_CPOS
;	THIS ROUTINE SETS THE CURRENT CURSOR POSITION TO THE
;	NEW X-Y VALUES PASSED
; INPUT
;	DX - ROW,COLUMN OF NEW CURSOR
;	BH - DISPLAY PAGE OF CURSOR
; OUTPUT
;	CURSOR ID SET AT 6845 IF DISPLAY PAGE IS CURRENT DISPLAY
;----------------------------------------------
	;
	mov	esi, CURSOR_POSN
        movzx   eax, bh	; BH = video page number
;	or	al, al
;	jz	short _set_cpos_0
        shl     al, 1   ; word offset
        add     esi, eax
;_set_cpos_0:
	mov	[esi], dx ; save the pointer
	cmp	[ACTIVE_PAGE], bh
	jne	short m17
	;call	m18	; CURSOR SET
;m17:			; SET_CPOS_RETURN
	; 01/09/2014
;	retn
		; DX  = row/column
m18:
	call	position ; determine location in regen buffer	
	mov	cx, [CRT_START]
	add	cx, ax  ; add char position in regen buffer
			; to the start address (offset) for this page
	shr	cx, 1	; divide by 2 for char only count
	mov	ah, 14	; register number for cursor
	;call	m16	; output value to the 6845	
	;retn

	;-----	THIS ROUTINE OUTPUTS THE CX REGISTER
	;	TO THE 6845 REGISTERS NAMED IN (AH)
m16:
	cli
	;mov	dx, [addr_6845] ; address register
	mov 	dx, 03D4h ; I/O address of color card
	mov	al, ah	; get value
	out	dx, al	; register set
	inc	dx	; data register
	jmp	$+2	; i/o delay
	mov	al, ch	; data
	out	dx, al	
	dec	dx	
	mov	al, ah
	inc	al	; point to other data register
	out	dx, al	; set for second register
	inc	dx
	jmp	$+2	; i/o delay
	mov	al, cl	; second data value
	out	dx, al
	sti
m17:
	retn

beeper: 
	; 04/08/2016
	; 12/05/2016 - TRDOS 386 (TRDOS v2.0)
	; 30/08/2014 (Retro UNIX 386 v1)
	; 18/01/2014
	; 03/12/2013
	; bell found
u11:
	sti
	cmp	bh, [ACTIVE_PAGE]
	jne	short u12	; Do not sound the beep 
				; if it is not written on the active page
beeper_gfx: ; 04/08/2016
	mov	cx, 1331 	; divisor for 896 hz tone
	mov	bl, 31		; set count for 31/64 second for beep
	;call	beep		; sound the pod bell
	;jmp	short u5 	; tty_return
	;retn
	
TIMER	equ 	040h   		; 8254 TIMER - BASE ADDRESS
PORT_B	equ	061h		; PORT B READ/WRITE DIAGNOSTIC REGISTER
GATE2	equ	00000001b	; TIMER 2 INPUT CATE CLOCK BIT
SPK2	equ	00000010b	; SPEAKER OUTPUT DATA ENABLE BIT

beep:
	; 07/02/2015
	; 30/08/2014 (Retro UNIX 386 v1)
	; 18/01/2014
	; 03/12/2013
	;
	; TEST4.ASM - 06/10/85  POST AND BIOS UTILITY ROUTINES
	;
	; ROUTINE TO SOUND THE BEEPER USING TIMER 2 FOR TONE
	;
	; ENTRY:
	;    (BL) = DURATION COUNTER ( 1 FOR 1/64 SECOND )
	;    (CX) = FREQUENCY DIVISOR (1193180/FREQUENCY) (1331 FOR 886 HZ)
	; EXIT:				:
	;    (AX),(BL),(CX) MODIFIED.

	pushf  ; 18/01/2014	; save interrupt status
	cli			; block interrupts during update
	mov	al, 10110110b	; select timer 2, lsb, msb binary
	out	TIMER+3, al 	; write timer mode register
	jmp	$+2		; I/O delay
	mov	al, cl		; divisor for hz (low)
	out	TIMER+2,AL	; write timer 2 count - lsb
	jmp	$+2		; I/O delay
	mov	al, ch		; divisor for hz (high)
	out	TIMER+2, al	; write timer 2 count - msb
	in	al, PORT_B	; get current setting of port
	mov	ah, al		; save that setting
	or	al, GATE2+SPK2	; gate timer 2 and turn speaker on
	out	PORT_B, al	; and restore interrupt status
	;popf	; 18/01/2014
	sti
g7:				; 1/64 second per count (bl)
	mov	ecx, 1035	; delay count for 1/64 of a second	
	call	waitf		; go to beep delay 1/64 count
	dec	bl		; (bl) length count expired?
	jnz	short g7	; no - continue beeping speaker
	;
	;pushf			; save interrupt status
	cli  	; 18/01/2014	; block interrupts during update
	in	al, PORT_B	; get current port value
        ;or      al, not (GATE2+SPK2) ; isolate current speaker bits in case
        or      al, ~(GATE2+SPK2)
        and	ah, al		; someone turned them off during beep
	mov	al, ah		; recover value of port
        ;or      al, not (GATE2+SPK2) ; force speaker data off
	or 	al, ~(GATE2+SPK2) ; isolate current speaker bits in case
	out	PORT_B, al	; and stop speaker timer
	;popf			; restore interrupt flag state
	sti
	mov	ecx, 1035	; force 1/64 second delay (short)
	call	waitf		; minimum delay between all beeps
	;pushf			; save interrupt status
	cli			; block interrupts during update
	in	al, PORT_B	; get current port value in case	
	and	al, GATE2+SPK2	; someone turned them on
	or	al, ah		; recover value of port_b
	out	PORT_B, al	; restore speaker status
	popf			; restore interrupt flag state
u12:	
	retn

REFRESH_BIT equ	00010000b 	; REFRESH TEST BIT

WAITF:
waitf:
	; 30/08/2014 (Retro UNIX 386 v1)
	; 03/12/2013
	;
;	push ax			; save work register (ah)	
;waitf1:
				; use timer 1 output bits
;	in	al, PORT_B	; read current counter output status
;	and	al, REFRESH_BIT	; mask for refresh determine bit
;	cmp	al, ah		; did it just change
;	je	short waitf1	; wait for a change in output line
;	;
;	mov	ah, al		; save new lflag state
;	loop	waitf1		; decrement half cycles till count end		
;	;
;	pop	ax		; restore (ah)
;	retn			; return (cx)=0

; 06/02/2015 (unix386.s <-- dsectrm2.s)
; 17/12/2014 (dsectrm2.s)
; WAITF
; /// IBM PC-XT Model 286 System BIOS Source Code - Test 4 - 06/10/85 ///
;
;---WAITF-----------------------------------------------------------------------
;	FIXED TIME WAIT ROUTINE (HARDWARE CONTROLLED - NOT PROCESSOR)
; ENTRY:
;	(CX) =	COUNT OF 15.085737 MICROSECOND INTERVALS TO WAIT
;	      	MEMORY REFRESH TIMER 1 OUTPUT USED AS REFERENCE
; EXIT:
;	       	AFTER (CX) TIME COUNT (PLUS OR MINUS 16 MICROSECONDS)
;	(CX) = 0	
;-------------------------------------------------------------------------------

; Refresh period: 30 micro seconds (15-80 us)
; (16/12/2014 - AWARDBIOS 1999 - ATORGS.ASM, WAIT_REFRESH)

;WAITF:					; DELAY FOR (CX)*15.085737 US
	PUSH	AX			; SAVE WORK REGISTER (AH)
	; 16/12/2014
	;shr	cx, 1			; convert to count of 30 micro seconds
	shr	ecx, 1	; 21/02/2015
;17/12/2014	
;WAITF1:
;	IN	AL, PORT_B   ;061h	; READ CURRENT COUNTER OUTPUT STATUS
;	AND	AL, REFRESH_BIT	;00010000b ; MASK FOR REFRESH DETERMINE BIT
;	CMP	AL, AH			; DID IT JUST CHANGE
;	JE	short WAITF1		; WAIT FOR A CHANGE IN OUTPUT LINE
;	MOV	AH, AL			; SAVE NEW FLAG STATE
;	LOOP	WAITF1			; DECREMENT HALF CYCLES TILL COUNT END		
	;
	; 17/12/2014
	;
	; Modification from 'WAIT_REFRESH' procedure of AWARD BIOS - 1999
	;
;WAIT_REFRESH:  Uses port 61, bit 4 to have CPU speed independent waiting.
;   	INPUT:  CX = number of refresh periods to wait
;     	       (refresh periods = 1 per 30 microseconds on most machines)
WR_STATE_0:
	IN	AL,PORT_B		; IN AL,SYS1
	TEST	AL,010H
	JZ	SHORT WR_STATE_0
WR_STATE_1:
	IN	AL,PORT_B		; IN AL,SYS1
	TEST	AL,010H
	JNZ	SHORT WR_STATE_1
        LOOP    WR_STATE_0
	;
	POP	AX			; RESTORE (AH)
	RETn				; (CX) = 0

; 09/07/2016
; 01/07/2016
; 24/06/2016
; 23/06/2016 - TRDOS 386 (TRDOS v2.0)
; VIDEO1.ASM - 24/03/1985 (IBM PC-AT BIOS source code)
;-------------------------------------------------------------------------------
; WRITE_STRING								       :
;	THIS ROUTINE WRITES A STRING OF CHARACTERS TO THE CRT.		       :
; INPUT 								       :
;	(AL) = WRITE STRING COMMAND  0 - 3				       :
;	(BH) = DISPLAY PAGE (ACTIVE PAGE)				       :
;	(CX) = COUNT OF CHARACTERS TO WRITE, IF (CX) = 0 THEN RETURN	       :
;	(DX) = CURSOR POSITION FOR START OF STRING WRITE		       :
;	(BL) = ATTRIBUTE OF CHARACTER TO WRITE IF (AL) = 0  OR	(AL) = 1       :
;	(eBP) = SOURCE STRING OFFSET					       :
; OUTPUT								       :
;	NONE								       :
;-------------------------------------------------------------------------------

; AL = 00h: Assign all characters the attribute in BL; do not update cursor
; AL = 01h: Assign all characters the attribute in BL; update cursor
; AL = 02h: Use attributes in string; do not update cursor
; AL = 03h: Use attributes in string; update cursor

WRITE_STRING:
	; 12/09/2016
	; 09/07/2016
	;cmp	byte [CRT_MODE], 7 ; 6?!
	;ja	VIDEO_RETURN		; not a valid function for VGA modes
	;
	mov	[w_str_cmd], al		; save (AL) command
	CMP	AL, 4			; TEST FOR INVALID WRITE STRING OPTION
	JNB	VIDEO_RETURN		; IF OPTION INVALID THEN RETURN

        ;JCXZ   VIDEO_RETURN            ; IF ZERO LENGTH STRING THEN RETURN

        jcxz    P55			; 01/07/2016


	; 01/07/2016
	;and	ecx, 0FFFFh
	; ECX = byte count
	;push	ecx
	mov	esi, ebp ; user buffer
	mov	edi, Cluster_Buffer  ; system buffer
	call	transfer_from_user_buffer
	;pop	ecx
	jc	VIDEO_RETURN
	; ecx = transfer (byte) count = character count
	mov	ebp, Cluster_Buffer
	; 12/09/2016
	cmp	byte [CRT_MODE], 7 ; 6?!
	ja	vga_write_string
	;
	movzx	esi, bh			; GET CURRENT CURSOR PAGE
	SAL	SI,1			; CONVERT TO PAGE OFFSET  (SI= PAGE)
	; *****
	PUSH	word [eSI+CURSOR_POSN]	; SAVE CURRENT CURSOR POSITION IN STACK

	;MOV	AX,0200H		; SET NEW CURSOR POSITION
	;INT	10H
P50next:	
	push	ebx ; ****
	push	ecx ; ***
	push	esi ; **
	push	edx ; *
	call	_set_cpos
P50:
	MOV	AL, [eBP]		; GET CHARACTER FROM INPUT STRING
	INC	eBP			; BUMP POINTER TO CHARACTER

;-----	TEST FOR SPECIAL CHARACTER'S

	CMP	AL, 08H			; IS IT A BACKSPACE
	JE	short P51		; BACK_SPACE
	CMP	AL, 0Dh ; CR		; IS IT CARRIAGE RETURN
	JE	short P51		; CAR_RET
	CMP	AL, 0Ah ; LF		; IS IT A LINE FEED
	JE	short P51		; LINE_FEED
	CMP	AL, 07h			; IS IT A BELL
	JNE	short P52		; IF NOT THEN DO WRITE CHARACTER
P51:
	;MOV	AH,0EH			; TTY_CHARACTER_WRITE
	;INT	10H			; WRITE TTY CHARACTER TO THE CRT
	
	call	_write_tty_m3

	pop	edx ; *
	pop	esi ; **

	MOV	DX, [eSI+CURSOR_POSN]	; GET CURRENT CURSOR POSITION
	JMP	SHORT P54		; SET CURSOR POSITION AND CONTINUE
P55:
	JMP	VIDEO_RETURN
P52:
	MOV	CX, 1			; SET CHARACTER WRITE AMOUNT TO ONE
	CMP	byte [w_str_cmd], 2	; IS THE ATTRIBUTE IN THE STRING
	JB	short P53		; IF NOT THEN SKIP
	MOV	BL, [eBP]		; ELSE GET NEW ATTRIBUTE
	INC	eBP			; BUMP STRING POINTER
P53:
	;MOV	AH,09H			; GOT_CHARACTER
	;INT	10H			; WRITE CHARACTER TO THE CRT

	call	_write_c_current
	
	pop	edx ; *	
	
	movzx	esi, bh ; video page number (0 to 7)	
	mov	[esi+chr_attrib], bl ; color/attribute

	INC	DL			; INCREMENT COLUMN COUNTER
	CMP	DL, [CRT_COLS]		; IF COLS ARE WITHIN RANGE FOR THIS MODE
	JB	short P54		;    THEN GO TO COLUMNS SET
	INC	DH			; BUMP ROW COUNTER BY ONE
	SUB	DL, DL			; SET COLUMN COUNTER TO ZERO
	CMP	DH, 25			; IF ROWS ARE LESS THAN 25 THEN
	JB	short P54		; GO TO ROWS_COLUMNS_SET

	MOV	AX,0E0AH		; ELSE SCROLL SCREEN
	;INT	10H			; RESET ROW COUNTER TO 24

	call	_write_tty_m3
	
	mov	dx, 1800h		; Column = 0, Row = 24
	pop	esi ; **
P54:
					; ROW_COLUMNS_SET
	;MOV	AX,0200H		; SET NEW CURSOR POSITION COMMAND
	;INT	10H			; ESTABLISH NEW CURSOR POSITION

	pop	ecx ; ***
	pop	ebx ; ****

	;LOOP	P50			; DO IT ONCE MORE UNTIL (CX) = ZERO
	dec	cx
	jnz	short P50next

	POP	DX  ; *****		; RESTORE OLD CURSOR COORDINATES
	
	test	byte [w_str_cmd], 1	; IF CURSOR WAS NOT TO BE MOVED
	JNZ	VIDEO_RETURN		; THEN EXIT WITHOUT RESETTING OLD VALUE
	
	;MOV	AX,0200H		; ELSE RESTORE OLD CURSOR POSITION
	;INT	10H
					; DONE - EXIT WRITE STRING
	call	_set_cpos
	JMP	VIDEO_RETURN		; RETURN TO CALLER

vga_write_string:
	; 12/09/2016 - TRDOS 386 (TRDOS v2.0)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', ' biosfn_write_string'

	; INPUT 								  :
	;	(AL) = WRITE STRING COMMAND  0 - 3				  :
	;	(BH) = DISPLAY PAGE (ACTIVE PAGE)				  :
	;	(CX) = COUNT OF CHARACTERS TO WRITE, IF (CX) = 0 THEN RETURN	  :
	;	(DX) = CURSOR POSITION FOR START OF STRING WRITE		  :
	;	(BL) = ATTRIBUTE OF CHARACTER TO WRITE IF (AL) = 0  OR	(AL) = 1  :
	;	(eBP) = SOURCE STRING OFFSET					  :
	; OUTPUT								  :
	;	NONE								  :
	;-------------------------------------------------------------------------;

	; AL = 00h: Assign all characters the attribute in BL; do not update cursor
	; AL = 01h: Assign all characters the attribute in BL; update cursor
	; AL = 02h: Use attributes in string; do not update cursor
	; AL = 03h: Use attributes in string; update cursor
	
	; biosfn_write_string(GET_AL(),GET_BH(),GET_BL(),CX,GET_DH(),GET_DL(),ES,BP);
	; static void biosfn_write_string (flag,page,attr,count,row,col,seg,offset) 

	; // Read curs info for the page
 	; biosfn_get_cursor_pos(page,&dummy,&oldcurs);
	; bh = video page = 0
	;movzx	esi, word [CURSOR_POSN] ; current cursor position for video page 0
	
	; // if row=0xff special case : use current cursor position
	; if(row==0xff)
	;  {col=oldcurs&0x00ff;
	;   row=(oldcurs&0xff00)>>8;
	;  }

	;mov	al, [w_str_cmd]

	cmp	dh, 0FFh
	je	short vga_wstr_1 ; user current cursor position
vga_wstr_0:
	; set cursor position
	mov	[CURSOR_POSN], dx ; save cursor pos for pg 0
vga_wstr_1:
	push	word [CURSOR_POSN] ; *

	; ebp = string offset in system buffer (user buffer was copied to)	
	
	; while(count--!=0)
	;  {
	;   car=read_byte(seg,offset++);
	;   if((flag&0x02)!=0)
	;    attr=read_byte(seg,offset++);
	;    biosfn_write_teletype(car,page,attr,WITH_ATTR);
	;  }

	;push	eax ; **
	;test	al, 2
	test	byte [w_str_cmd], 2
	jnz	short vga_wstr_3
	mov	[ccolor], bl
vga_wstr_2:
	push	ecx
	mov	al, [ebp]
	call	vga_write_teletype
	pop	ecx
	dec	cx
	jz	short vga_wstr_4
	inc	ebp
	mov	bl, [ccolor]
	jmp	short vga_wstr_2
vga_wstr_3:
	push	ecx
	mov	al, [ebp]
	inc	ebp
	mov	bl, [ebp]
	call	vga_write_teletype
	pop	ecx
	dec	cx
	jz	short vga_wstr_4
	inc	ebp
	jmp	short vga_wstr_3
vga_wstr_4:
	; // Set back curs pos 
	; if((flag&0x01)==0)
	;  biosfn_set_cursor_pos(page,oldcurs);
	; }
	;pop	eax ; **
	pop	dx ; word [CURSOR_POSN] ; *
	;test	al, 1
	test	byte [w_str_cmd], 1
	jnz	VIDEO_RETURN
	mov 	[CURSOR_POSN], dx
	JMP	VIDEO_RETURN

; 07/07/2016
; 27/06/2016 - TRDOS 386 (TRDOS v2.0)
; VIDEO1.ASM - 24/03/1985 (IBM PC-AT BIOS source code)
;------------------------------------------------------
;  SCROLL UP
;   THIS ROUTINE SCROLLS UP THE INFORMATION ON THE CRT
; ENTRY ---
;  CH,CL = UPPER LEFT CORNER OF REGION TO SCROLL
;  DH,DL = LOWER RIGHT CORNER OF REGION TO SCROLL
;   BOTH OF THE ABOVE ARE IN CHARACTER POSITIONS
;  BH = FILL VALUE FOR BLANKED LINES
;  AL = # LINES TO SCROLL (AL=0 MEANS BLANK THE ENTIRE FIELD)
;  DS = DATA SEGMENT
;  ES = REGEN SEGMENT
; EXIT --
;  NOTHING, THE SCREEN IS SCROLLED
;--------------------------------------------------------

	; cl = upper left column
	; ch = upper left row
	; dl = lower rigth column
	; dh = lower right row
	;
	; al = line count (AL=0 means blank entire fields)
	; bl = fill value for blanked lines	
	; bh = unused

GRAPHICS_UP:
	; 07/07/2016
	;AH = Current video mode, [CRT_MODE]
	cmp	ah, 7
	ja	short vga_graphics_up
	;je	n0

	MOV	bh, al			; save line count in BH
	MOV	AX, CX			; GET UPPER LEFT POSITION INTO AX REG

;-----	USE CHARACTER SUBROUTINE FOR POSITIONING
;-----	ADDRESS RETURNED IS MULTIPLIED BY 2 FROM CORRECT VALUE

	CALL	GRAPH_POSN
	MOVzx	eDI, AX			; SAVE RESULT AS DESTINATION ADDRESS

;-----	DETERMINE SIZE OF WINDOW

	SUB	DX, CX
        ADD     DX, 101h                ; ADJUST VALUES
	SAL	DH, 2			; MULTIPLY ROWS BY 4 AT 8 VERT DOTS/CHAR
					; AND EVEN/ODD ROWS
;-----	DETERMINE CRT MODE

	CMP	byte [CRT_MODE], 6	; TEST FOR MEDIUM RES
        JNC     short _R7_              ; FIND_SOURCE

;-----	MEDIUM RES UP
	SAL	DL, 1			; # COLUMNS * 2, SINCE 2 BYTES/CHAR
	SAL	DI, 1			; OFFSET *2 SINCE 2 BYTES/CHAR

;-----	DETERMINE THE SOURCE ADDRESS IN THE BUFFER
_R7_:                                   ; FIND_SOURCE
	add	edi, 0B8000h
	sal	bh, 2			; multiply number of lines by 4
        JZ      short _R11              ; IF ZERO, THEN BLANK ENTIRE FIELD
	MOV	AL, 80			; 80 BYTES/ROW
	mul	bh			; determine offset to source
	movzx	esi, ax			; offset to source
	add	eSI, eDI		; SET UP SOURCE
	MOV	AH, DH			; NUMBER OF ROWS IN FIELD
	sub	ah, bh			; determine number to move

;-----	LOOP THROUGH, MOVING ONE ROW AT A TIME, BOTH EVEN AND ODD FIELDS
_R8:                                    ; ROW_LOOP
        CALL    _R17                    ; MOVE ONE ROW
	SUB	SI, 2000h-80		; MOVE TO NEXT ROW
	SUB	DI, 2000h-80
	DEC	AH			; NUMBER OF ROWS TO MOVE
        JNZ     short _R8               ; CONTINUE TILL ALL MOVED

;-----	FILL IN THE VACATED LINE(S)
_R9:                                    ; CLEAR ENTRY
	mov	al, bl			; attribute to fill with
_R10_:
        CALL    _R18                    ; CLEAR THAT ROW
	SUB	DI, 2000h-80		; POINT TO NEXT LINE
	dec	bh			; number of lines to fill
        JNZ     short _R10_             ; CLEAR LOOP
	retn				; EVERYYHING DONE

_R11:                                   ; BLANK_FIELD
	mov	bh, dh			; set blank count to everything in field
        JMP     short _R9               ; CLEAR THE FIELD

vga_graphics_up:
	; 08/08/2016
	; 07/08/2016
	; 04/08/2016
	; 01/08/2016
	; 31/07/2016
	; 07/07/2016 - TRDOS 386 (TRDOS v2.0)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_scroll'
	;

	; cl = upper left column
	; ch = upper left row
	; dl = lower rigth column
	; dh = lower right row
	;
	; al = line count (AL=0 means blank entire fields)
	; bl = fill value for blanked lines	
	; bh = unused
	;
	; ah = [CRT_MODE], current video mode	

	mov	bh, al ; 31/07/2016
	mov	esi, vga_g_modes
	mov	edi, esi
	add	edi, vga_g_mode_count
vga_g_up_0:
	lodsb
	cmp	al, ah ; [CRT_MODE]
	je	short vga_g_up_1
	cmp	esi, edi
	jb	short vga_g_up_0
	;xor	bh, bh ; 31/07/2016)
	retn	; nothing to do
vga_g_up_1:
	mov	al, bh ; 31/07/2016
	add	esi, vga_g_memmodel - (vga_g_modes + 1)  
	; [ESI] = VGA memory model number (LINEAR8, PLANAR4, PLANAR1)
	
	; if(rlr>=nbrows)rlr=nbrows-1;
 	; if(clr>=nbcols)clr=nbcols-1;
 	; if(nblines>nbrows)nblines=0;
 	; cols=clr-cul+1;

	cmp	dh, [VGA_ROWS]
	jb	short vga_g_up_2
	mov	dh, [VGA_ROWS]
	dec	dh
vga_g_up_2:
	cmp	dl, [CRT_COLS]  ; = [VGA_COLS]
	jb	short vga_g_up_3
	mov	dl, [CRT_COLS]
	dec	dl
vga_g_up_3:	
	cmp	al, [VGA_ROWS]
	jna	short vga_g_up_4
	sub	al, al ; 0
vga_g_up_4:
	mov	bh, dl ; clr
	sub	bh, cl ; cul
	inc	bh ; cols = clr-cul+1

	and	al, al ; nblines = 0
	jnz	short vga_g_up_6
	and	ch, ch ; rul = 0
	jnz	short vga_g_up_6
	and	cl, cl ; cul = 0
	jnz	short vga_g_up_6

	push	ax
	mov	al, [VGA_ROWS]
	dec	al  
	cmp	dh, al ; rlr = nbrows-1
	jne	short vga_g_up_5
        mov     al, [CRT_COLS]  ; = VGA_COLS
	dec	al 
 	cmp	dl, al ; clr = nbcols-1
	jne	short vga_g_up_5
	pop	ax

	mov	ax, 0205h
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	out	dx, ax
	mov	al, [VGA_ROWS]
	mov	ah, [CRT_COLS] ; = [VGA_COLS]
	mul	ah
	movzx	edx, ax
	; 08/08/2016
	movzx	eax, byte [CHAR_HEIGHT]
	mul	edx
	; eax = byte count	
	mov	ecx, eax
	;; 07/08/2016
	;shl	dx, 3 ; * 8 ; * [CHAR_HEIGHT]
	;mov	ecx, edx
	mov	al, bl ; fill value for blanked lines
	mov	edi, 0A0000h
	rep	stosb		
	
	mov	ax, 5
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	out	dx, ax ; 0005h	

	retn

vga_g_up_5:
	pop	ax

vga_g_up_6:
	; [ESI] = VGA memory model number for current video mode
        ;
        ; LINEAR8 equ 5
        ; PLANAR4 equ 4
        ; PLANAR1 equ 3

	cmp	byte [esi], PLANAR4
	je	short vga_g_up_planar
	cmp	byte [esi], PLANAR1
	je	short vga_g_up_planar
vga_g_up_linear8:
	; 07/07/2016 (TEMPORARY)
	;
	; cl = upper left column ; cul
	; ch = upper left row ; rul
	; dl = lower rigth column ; clr
	; dh = lower right row ; rlr

vga_g_up_l0:
 	;{for(i=rul;i<=rlr;i++)
  	; if((i+nblines>rlr)||(nblines==0))
	or	al, al
	jz	short vga_g_up_l2
	mov	ah, al
	add	ah, ch ; i+nblines
	;jc	short vga_g_up_l2	
	cmp	ah, dh
	ja	short vga_g_up_l2
	; else
	;  vgamem_copy_pl4(cul,i+nblines,i,cols,nbcols,cheight);
	call	vgamem_copy_l8
vga_g_up_l1:
	inc	ch
	cmp	ch, dh
	jna	short vga_g_up_l0
	retn
vga_g_up_l2:
	; vgamem_fill_pl4(cul,i,cols,nbcols,cheight,attr);
	call	vgamem_fill_l8
	jmp	short vga_g_up_l1

vga_g_up_planar:
	; cl = upper left column ; cul
	; ch = upper left row ; rul
	; dl = lower rigth column ; clr
	; dh = lower right row ; rlr
vga_g_up_pl0:
 	;{for(i=rul;i<=rlr;i++)
  	; if((i+nblines>rlr)||(nblines==0))
	and	al, al
	jz	short vga_g_up_pl2	
	mov	ah, al
	add	ah, ch ; i+nblines
	;jc	short vga_g_up_pl2
	cmp	ah, dh
	ja	short vga_g_up_pl2
	; else
	;  vgamem_copy_pl4(cul,i+nblines,i,cols,nbcols,cheight);
	call	vgamem_copy_pl4
vga_g_up_pl1:
	inc	ch 
	cmp	ch, dh
	jna	short vga_g_up_pl0
	retn
vga_g_up_pl2:
	; vgamem_fill_pl4(cul,i,cols,nbcols,cheight,attr);
	call	vgamem_fill_pl4
	jmp	short vga_g_up_pl1

vgamem_copy_pl4:
	; 08/08/2016
	; 07/08/2016
	; 07/07/2016 - TRDOS 386 (TRDOS v2.0)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'vgamem_copy_pl4'
	;
	; vgamem_copy_pl4(xstart,ysrc,ydest,cols,nbcols,cheight)
	; cl = xstart, ah = ysrc (i+nblines), ch = ydest (i),
	; bh = cols, [CRT_COLS] = nbcols, [CHAR_HEIGHT] = cheight

	; src=ysrc*cheight*nbcols+xstart;
	; dest=ydest*cheight*nbcols+xstart;

	push	edx
	push	eax

	; outw(VGAREG_GRDC_ADDRESS, 0x0105)
	mov	ax, 0105h
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	out	dx, ax

	; 07/08/2016
 	;mov     ah, [esp+1]
	;movzx	edx, ah ; ysrc
	movzx	edx, byte [esp+1]
	; 08/08/2016
	movzx	eax, byte [CHAR_HEIGHT]
	mov	ah, [CRT_COLS] ; nbcols
	mul	ah 
	;; 07/08/2016
	;movzx	eax, byte [CRT_COLS]
	;shl	ax, 3 ; * 8 ; * [CHAR_HEIGHT]
	push	eax ; cheight * nbcols
	mul	edx ; * ysrc
	; eax = ysrc * cheight * nbcols
	; edx = 0
	mov	dl, cl ; edx = xstart	
 	add	eax, edx
	mov	esi, eax ; src
	mov	dl, ch ; ydest
	pop	eax ; cheight * nbcols
	mul	edx
	; eax = ydest * cheight * nbcols
	mov	dl, cl ; edx = xstart	
 	add	eax, edx
	mov	edi, eax ; dest
	; esi = src
	; edi = dest
	; for(i=0;i<cheight;i++)
  	; {
   	;  memcpyb(0xa000,dest+i*nbcols,0xa000,src+i*nbcols,cols);
  	; }
	push	ecx
	mov	ecx, 0A0000h
	add	esi, ecx
	add	edi, ecx
	; 08/08/2016
	mov	dh, [CHAR_HEIGHT]
	;; 07/08/2016
	;mov	dh, 8 ; 07/08/2016
	sub	dl, dl ; i
vgamem_copy_pl4_0:
	push	esi
	push	edi
	movzx	eax, byte [CRT_COLS]
	mul	dl
	; eax = i * nbcols
	add	edi, eax ; dest+i*nbcols
	add	esi, eax
	movzx	ecx, bh ; cols
	rep	movsb
	pop	edi
	pop	esi
	dec	dh
	jnz	short vgamem_copy_pl4_0
vgamem_copy_pl4_1:
	pop	ecx

	; outw(VGAREG_GRDC_ADDRESS, 0x0005);
	mov	ax, 0005h
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	out	dx, ax

	pop	eax
	pop	edx

	retn	

vgamem_fill_pl4:
	; 08/08/2016
	; 07/08/2016
	; 04/08/2016
	; 07/07/2016 - TRDOS 386 (TRDOS v2.0)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'vgamem_fill_pl4'
	;
	; vgamem_fill_pl4(xstart,ystart,cols,nbcols,cheight,attr)
	; cl = xstart, edi = ch = ystart, bh = cols,
	; [CRT_COLS] = nbcols, [CHAR_HEIGHT] = cheight, attr = 0

	; dest=ystart*cheight*nbcols+xstart;
	push	edx
	push	eax

	; outw(VGAREG_GRDC_ADDRESS, 0x0205)
	mov	ax, 0205h
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	out	dx, ax

      	; 08/08/2016
	movzx   eax, byte [CHAR_HEIGHT]
	mul	ch
	;; 07/08/2016
	;movzx	eax, ch
	;shl	ax, 3 ; * 8 ; * [CHAR_HEIGHT]
	movzx	edx, byte [CRT_COLS] ; = [VGA_COLS]
	mul	edx
	; edx  = 0
	mov	dl, cl 
	add	eax, edx
	mov	edi, eax
	; edi = dest
	; for(i=0;i<cheight;i++)
  	; {
   	;  memsetb(0xa000,dest+i*nbcols,attr,cols);
  	; }
	add	edi, 0A0000h
	push	ecx
	; 08/08/2016
	mov	dh, [CHAR_HEIGHT]
	;; 07/08/2016
	;mov	dh, 8 ; 07/08/2016
	sub	dl, dl ; i
vgamem_fill_pl4_0:
	push	edi
	movzx	eax, byte [CRT_COLS]
	mul	dl
	; eax = i * nbcols
	add	edi, eax ; dest+i*nbcols
	mov	al, bl ; attr ; 04/08/2016
	movzx	ecx, bh ; cols
 	rep	stosb
	pop	edi
	jnz	short vgamem_fill_pl4_0
vgamem_fill_pl4_1:
	pop	ecx

	; outw(VGAREG_GRDC_ADDRESS, 0x0005);
	mov	ax, 0005h
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	out	dx, ax

	pop	eax
	pop	edx 
	
	retn

vgamem_copy_l8:
	; 08/08/2016
	; 07/08/2016
	; 06/08/2016
	; 07/07/2016 - TRDOS 386 (TRDOS v2.0)
	;
	; TEMPORARY
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'vgamem_copy_pl4'
	;
	; vgamem_copy_pl4(xstart,ysrc,ydest,cols,nbcols,cheight)
	; cl = xstart, ah = ysrc (i+nblines), ch = ydest (i),
	; bh = cols, [CRT_COLS] = nbcols, [CHAR_HEIGHT] = cheight

	; src=ysrc*cheight*nbcols+xstart;
	; dest=ydest*cheight*nbcols+xstart;

	push	edx
	push	eax

	; outw(VGAREG_GRDC_ADDRESS, 0x0105)
	;mov	ax, 0105h
	;mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	;out	dx, ax

	;mov	ah, [esp+1]

	movzx	edx, ah ; ysrc
	; 08/08/2016
	movzx	eax, byte [CHAR_HEIGHT]
	mov	ah, [CRT_COLS] ; nbcols
	mul	ah 
	;; 07/08/2016
	;movzx	eax, byte [CRT_COLS]
	;shl	ax, 3 ; * 8 ; * [CHAR_HEIGHT]
	push	eax ; cheight * nbcols
	mul	edx ; * ysrc
	; eax = ysrc * cheight * nbcols
	; edx = 0
	mov	dl, cl ; edx = xstart
 	add	eax, edx
	mov	esi, eax ; src
	shl	si, 3 ; * 8 ; 06/08/2016
	mov	dl, ch ; ydest
	pop	eax ; cheight * nbcols
	mul	edx
	; eax = ydest * cheight * nbcols
	mov	dl, cl ; edx = xstart	
 	add	eax, edx
	mov	edi, eax ; dest
	shl	di, 3 ; * 8 ; 06/08/2016
	; esi = src
	; edi = dest
	; for(i=0;i<cheight;i++)
  	; {
   	;  memcpyb(0xa000,dest+i*nbcols,0xa000,src+i*nbcols,cols);
  	; }
	push	ecx
	mov	ecx, 0A0000h
	add	esi, ecx
	add	edi, ecx
	; 08/08/2016
	mov	dh, [CHAR_HEIGHT]
	;; 07/08/2016
	;mov	dh, 8 ; 07/08/2016
	sub	dl, dl ; i
vgamem_copy_l8_0:
	push	esi
	push	edi
	movzx	eax, byte [CRT_COLS]
	mul	dl
	; eax = i * nbcols
	shl	ax, 3 ; * 8 ; 06/08/2016
	add	edi, eax ; dest+i*nbcols
	add	esi, eax
	movzx	ecx, bh ; cols
	shl	cx, 3 ; * 8 ; 06/08/2016
	rep	movsb
	pop	edi
	pop	esi
	inc	dl ; 06/08/2016
	dec	dh
	jnz	short vgamem_copy_l8_0
vgamem_copy_l8_1:
	pop	ecx

	;; outw(VGAREG_GRDC_ADDRESS, 0x0005);
	;mov	ax, 0005h
	;mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	;out	dx, ax

	pop	eax
	pop	edx

	retn

vgamem_fill_l8:
	; 08/08/2016
	; 07/08/2016
	; 06/08/2016
	; 04/08/2016
	; 07/07/2016 - TRDOS 386 (TRDOS v2.0)
	;
	; TEMPORARY
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'vgamem_fill_pl4'
	;
	; vgamem_fill_pl4(xstart,ystart,cols,nbcols,cheight,attr)
	; cl = xstart, edi = ch = ystart, bh = cols,
	; [CRT_COLS] = nbcols, [CHAR_HEIGHT] = cheight, attr = 0

	; dest=ystart*cheight*nbcols+xstart;
	push	edx
	push	eax

	;; outw(VGAREG_GRDC_ADDRESS, 0x0205)
	;mov	ax, 0205h
	;mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	;out	dx, ax

        ; 08/08/2016
	movzx   eax, byte [CHAR_HEIGHT]
	mul	ch
	;; 07/08/2016
	;movzx	eax, ch
	;shl	ax, 3 ; * 8 ; * [CHAR_HEIGHT]
	movzx	edx, byte [CRT_COLS] ; = [VGA_COLS]
	mul	edx
	; edx  = 0
	mov	dl, cl 
	add	eax, edx
	mov	edi, eax
	shl	di, 3 ; * 8 ; 06/08/2016
	; edi = dest
	; for(i=0;i<cheight;i++)
  	; {
   	;  memsetb(0xa000,dest+i*nbcols,attr,cols);
  	; }
	add	edi, 0A0000h
	push	ecx
	; 08/08/2016
	mov	dh, [CHAR_HEIGHT]
	;; 07/08/2016
	;mov	dh, 8 ; 07/08/2016
	sub	dl, dl ; i
vgamem_fill_l8_0:
	push	edi
	movzx	eax, byte [CRT_COLS]
	mul	dl
	; eax = i * nbcols
	shl	ax, 3 ; * 8 ; 06/08/2016
	add	edi, eax ; dest+i*nbcols
	mov	al, bl ; attr ; 04/08/2016
	movzx	ecx, bh ; cols
	shl	cx, 3 ; * 8 ; 06/08/2016
 	rep	stosb
	pop	edi
	inc	dl ; 06/08/2016
	dec	dh
	jnz	short vgamem_fill_l8_0
vgamem_fill_l8_1:
	pop	ecx

	;; outw(VGAREG_GRDC_ADDRESS, 0x0005);
	;mov	ax, 0005h
	;mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	;out	dx, ax

	pop	eax
	pop	edx 

	retn

vga_graphics_down:
	; 08/08/2016
	; 07/08/2016
	; 31/07/2016
	; 07/07/2016 - TRDOS 386 (TRDOS v2.0)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_scroll'
	;

	; cl = upper left column
	; ch = upper left row
	; dl = lower rigth column
	; dh = lower right row
	;
	; al = line count (AL=0 means blank entire fields)
	; bl = fill value for blanked lines	
	; bh = unused
	;
	; ah = [CRT_MODE], current video mode	

        cld     ; !!! Clear direction flag !!! 

	mov	bh, al ; 31/07/2016

	mov	esi, vga_modes
	mov	edi, esi
	add	edi, vga_mode_count
vga_g_down_0:
	lodsb
	cmp	al, ah ; [CRT_MODE]
	je	short vga_g_down_1
	cmp	esi, edi
	jb	short vga_g_down_0
	; xor 	bh, bh	; 31/07/2016
	retn	; nothing to do
vga_g_down_1:
	mov	al, bh ; 31/07/2016
	add	esi, vga_memmodel - (vga_modes + 1)  
	; [ESI] = VGA memory model number (LINEAR8, PLANAR4, PLANAR1)
	
	; if(rlr>=nbrows)rlr=nbrows-1;
 	; if(clr>=nbcols)clr=nbcols-1;
 	; if(nblines>nbrows)nblines=0;
 	; cols=clr-cul+1;

	cmp	dh, [VGA_ROWS]
	jb	short vga_g_down_2
	mov	dh, [VGA_ROWS]
	dec	dh
vga_g_down_2:
	cmp	dl, [CRT_COLS]  ; = [VGA_COLS]
	jb	short vga_g_down_3
	mov	dl, [CRT_COLS]
	dec	dl
vga_g_down_3:	
	cmp	al, [VGA_ROWS]
	jna	short vga_g_down_4
	sub	al, al ; 0
vga_g_down_4:
	mov	bh, dh ; clr
	sub	bh, cl ; cul
	inc	bh ; cols = clr-cul+1

	and	al, al ; nblines = 0
	jnz	short vga_g_down_6
	and	ch, ch ; rul = 0
	jnz	short vga_g_down_6
	and	cl, cl ; cul = 0
	jnz	short vga_g_down_6

	push	ax
	mov	al, [VGA_ROWS]
	dec	al  
	cmp	dh, al ; rlr = nbrows-1
	jne	short vga_g_down_5
        mov     al, [CRT_COLS]  ; = VGA_COLS
	dec	al 
 	cmp	dl, al ; clr = nbcols-1
	jne	short vga_g_down_5
	pop	ax

	mov	ax, 0205h
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	out	dx, ax
	mov	al, [VGA_ROWS]
	mov	ah, [CRT_COLS] ; = [VGA_COLS]
	mul	ah
	movzx	edx, ax
	; 08/08/2016
	movzx	eax, byte [CHAR_HEIGHT]
	mul	edx
	; eax = byte count	
	mov	ecx, eax
	;; 07/08/2016
	;shl	dx, 3 ; * 8 ; * [CHAR_HEIGHT]
	;mov	ecx, edx
	mov	al, bl ; fill value for blanked lines
	mov	edi, 0A0000h
	rep	stosb			
	
	mov	al, 5
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	out	dx, ax ; 0005h	

	retn

vga_g_down_5:
	pop	ax

vga_g_down_6:
	; [ESI] = VGA memory model number for current video mode
        ;
        ; LINEAR8 equ 5
        ; PLANAR4 equ 4
        ; PLANAR1 equ 3

	cmp	byte [esi], PLANAR4
	je	short vga_g_down_planar
	cmp	byte [esi], PLANAR1
	je	short vga_g_down_planar
vga_g_down_linear8:
	; 07/07/2016 (TEMPORARY)
	;
	; cl = upper left column ; cul
	; ch = upper left row ; rul
	; dl = lower rigth column ; clr
	; dh = lower right row ; rlr

vga_g_down_l0:
	;{for(i=rlr;i>=rul;i--)
        ; if((i<rul+nblines)||(nblines==0))
	or	al, al
	jz	short vga_g_down_l2
	mov	ah, al
	add	ah, ch
	;jc	short vga_g_down_l2	
	xchg	ch, dh
	cmp	ch, ah
	jb	short vga_g_down_l2
	mov	ah, ch
	sub	ah, al ; ah = i - nblines
	; else
	; vgamem_copy_pl4(cul,i,i-nblines,cols,nbcols,cheight);
	call	vgamem_copy_l8
vga_g_down_l1:
	xchg	dh, ch
	dec	dh 
	cmp	dh, ch
	jnb	short vga_g_down_l0
	retn

vga_g_down_l2:
	; vgamem_fill_pl4(cul,i,cols,nbcols,cheight,attr);
	call	vgamem_fill_l8
	jmp	short vga_g_down_l1
		 
vga_g_down_planar:
	; cl = upper left column ; cul
	; ch = upper left row ; rul
	; dl = lower rigth column ; clr
	; dh = lower right row ; rlr
vga_g_down_pl0:
 	;{for(i=rlr;i>=rul;i--)
        ; if((i<rul+nblines)||(nblines==0))
	or	al, al
	jz	short vga_g_down_pl2
	mov	ah, al
	add	ah, ch
	;jc	short vga_g_down_pl2	
	xchg	ch, dh
	cmp	ch, ah
	jb	short vga_g_down_pl2
	mov	ah, ch
	sub	ah, al ; ah = i - nblines
	; else
	; vgamem_copy_pl4(cul,i,i-nblines,cols,nbcols,cheight);
	call	vgamem_copy_pl4
vga_g_down_pl1:
	xchg	dh, ch
	dec	dh 
	cmp	dh, ch
	jnb	short vga_g_down_pl0
	retn

vga_g_down_pl2:
	; vgamem_fill_pl4(cul,i,cols,nbcols,cheight,attr);
	call	vgamem_fill_pl4
	jmp	short vga_g_down_pl1

; 07/07/2016
; 27/06/2016 - TRDOS 386 (TRDOS v2.0)
; VIDEO1.ASM - 24/03/1985 (IBM PC-AT BIOS source code)
;------------------------------------------------------
; SCROLL DOWN
;  THIS ROUTINE SCROLLS DOWN THE INFORMATION ON THE CRT
; ENTRY --
;  CH,CL = UPPER LEFT CORNER OF REGION TO SCROLL
;  DH,DL = LOWER RIGHT CORNER OF REGION TO SCROLL
;   BOTH OF THE ABOVE ARE IN CHARACTER POSITIONS
;  BH = FILL VALUE FOR BLANKED LINES
;  AL = # LINES TO SCROLL (AL=0 MEANS BLANK THE ENTIRE FIELD)
;  DS = DATA SEGMENT
;  ES = REGEN SEGMENT
; EXIT --
;  NOTHING, THE SCREEN IS SCROLLED
;--------------------------------------------------------

	; cl = upper left column
	; ch = upper left row
	; dl = lower rigth column
	; dh = lower right row
	;
	; al = line count (AL=0 means blank entire fields)
	; bl = fill value for blanked lines	
	; bh = unused

GRAPHICS_DOWN:
	; 07/07/2016
	;AH = Current video mode, [CRT_MODE]
	;STD				; SET DIRECTION
	cmp	ah, 7
        ja      vga_graphics_down
	;je	_n0

	MOV	bh, al			; save line count in BH
	MOV	AX, DX			; GET LOWER RIGHT POSITION INTO AX REG

;-----	USE CHARACTER SUBROUTINE FOR POSITIONING
;-----	ADDRESS RETURNED IS MULTIPLIED BY 2 FROM CORRECT VALUE

	CALL	GRAPH_POSN
	MOVzx	eDI, AX			; SAVE RESULT AS DESTINATION ADDRESS

;-----	DETERMINE SIZE OF WINDOW

	SUB	DX, CX
        ADD     DX, 101h                ; ADJUST VALUES
	SAL	DH, 2			; MULTIPLY ROWS BY 4 AT 8 VERT DOTS/CHAR
					; AND EVEN/ODD ROWS

;-----	DETERMINE CRT MODE

	CMP	byte [CRT_MODE], 6	; TEST FOR MEDIUM RES
        JNC     short _R12              ; FIND_SOURCE_DOWN

;-----	MEDIUM RES DOWN
	SAL	DL, 1			; # COLUMNS * 2, SINCE 2 BYTES/CHAR
	SAL	DI, 1			; OFFSET *2 SINCE 2 BYTES/CHAR
	INC	DI			; POINT TO LAST BYTE

;-----	DETERMINE THE SOURCE ADDRESS IN THE BUFFER

_R12:                                   ; FIND_SOURCE_DOWN
	add	edi, 0B8000h		
	ADD	DI, 240			; POINT TO LAST ROW OF PIXELS
	sal	bh, 2			; multiply number of lines by 4
	JZ	short 6			; IF ZERO, THEN BLANK ENTIRE FIELD
	MOV	AL, 80			; 80 BYTES/ROW
	mul	bh			; determine offset to source
	MOV	eSI, eDI		; SET UP SOURCE
	SUB	SI, AX			; SUBTRACT THE OFFSET
	MOV	AH, DH			; NUMBER OF ROWS IN FIELD
	sub	ah, bh			; determine number to move

;-----	LOOP THROUGH, MOVING ONE ROW AT A TIME, BOTH EVEN AND ODD FIELDS

_R13:                                   ; ROW_LOOP_DOWN
        CALL    _R17                    ; MOVE ONE ROW
	SUB	SI, 2000h+80		; MOVE TO NEXT ROW
	SUB	DI, 2000h+80
	DEC	AH			; NUMBER OF ROWS TO MOVE
        JNZ     short _R13              ; CONTINUE TILL ALL MOVED

;-----	FILL IN THE VACATED LINE(S)
_R14:                                   ; CLEAR_ENTRY_DOWN
	mov	al, bl			; attribute to fill with
_R15_:                                  ; CLEAR_LOOP_DOWN
	CALL	_R18			; CLEAR A ROW
	SUB	DI, 2000h+80		; POINT TO NEXT LINE
	dec	bh			; number of lines to fill
        JNZ     short _R15_             ; CLEAR_LOOP_DOWN

	retn				; EVERYYHING DONE

_R16:                                   ; BLANK_FIELD_DOWN
	mov	bh, dh			; set blank count to everything in field
        JMP     short _R14              ; CLEAR THE FIELD

; 27/06/2016 - TRDOS 386 (TRDOS v2.0)
; VIDEO1.ASM - 24/03/1985 (IBM PC-AT BIOS source code)

;-----	ROUTINE TO MOVE ONE ROW OF INFORMATION

_R17:
	MOVzx	ecx, DL			; NUMBER OF BYTES IN THE ROW
	PUSH	eSI
	PUSH	eDI			; SAVE POINTERS
	REP	MOVSB			; MOVE THE EVEN FIELD
	POP	eDI
	POP	eSI
	ADD	SI, 2000h
	ADD	DI, 2000h		; POINT TO THE ODD FIELD
	PUSH	eSI
	PUSH	eDI			; SAVE THE POINTERS
	MOV	CL, DL			; COUNT BACK
	REP	MOVSB			; MOVE THE ODD FIELD
	POP	eDI
	POP	eSI			; POINTERS BACK
	RETn				; RETURN TO CALLER

;-----	CLEAR A SINGLE ROW

_R18:
	MOVzx	ecx, DL			; NUMBER OF BYTES IN FIELD
	PUSH	eDI			; SAVE POINTER
	REP	STOSB			; STORE THE NEW VALUE
	POP	eDI			; POINTER BACK
	ADD	DI, 2000h		; POINT TO ODD FIELD
	PUSH	eDI
	MOV	CL, DL
	REP	STOSB			; FILL THE ODD FIELD
	POP	eDI
	RETn				; RETURN TO CALLER

; 04/07/2016
; 01/07/2016
; 30/06/2016 - TRDOS 386 (TRDOS v2.0)
; VIDEO1.ASM - 24/03/1985 (IBM PC-AT BIOS source code)
;--------------------------------------------------
; GRAPHICS WRITE
;  THIS ROUTINE WRITES THE ASCII CHARACTER TO THE CURRENT
;  POSITION ON THE SCREEN.
; ENTRY --
;  AL = CHARACTER TO WRITE
;  BL = COLOR ATTRIBUTE TO BE USED FOR FOREGROUND COLOR
;	IF BIT 7 IS SET, THE CHAR IS XOR'D INTO THE REGEN BUFFER
;	(0 IS USED FOR THE BACKGROUND COLOR)
;  CX = NUMBER OF CHARS TO WRITE
;  DS = DATA SEGMENT
;  ES = REGEN SEGMENT
; EXIT --
;  NOTHING IS RETURNED
;
; GRAPHICS READ
;  THIS ROUTINE READS THE ASCII CHARACTER AT THE CURRENT CURSOR
;  POSITION ON THE SCREEN BY MATCHING THE DOTS ON THE SCREEN TO THE
;  CHARACTER GENERATOR CODE POINTS
; ENTRY --
;  NONE (0 IS ASSUMED AS THE BACKGROUND COLOR)
; EXIT --
;  AL = CHARACTER READ AT THAT POSITION (0 RETURNED IF NONE FOUND)
;
; FOR BOTH ROUTINES, THE IMAGES USED TO FORM CHARS ARE CONTAINED IN ROM
;  FOR THE 1ST 128 CHARS.  TO ACCESS CHARS IN THE SECOND HALF, THE USER
;  MUST INITIALIZE THE VECTOR AT INTERRUPT 1FH (LOCATION 0007CH) TO
;  POINT TO THE USER SUPPLIED TABLE OF GRAPHIC IMAGES (8X8 BOXES).
;  FAILURE TO DO SO WILL CAUSE IN STRANGE RESULTS
;-----------------------------------------------------

GRAPHICS_WRITE:
	and 	eax, 0FFh		; ZERO TO HIGH OF CODE POINT
	PUSH	eAX			; SAVE CODE POINT VALUE

;-----	DETERMINE POSITION IN REGEN BUFFER TO PUT CODE POINTS

	CALL	S26			; FIND LOCATION IN REGEN BUFFER
	MOV	eDI, eAX		; REGEN POINTER IN DI

;-----	DETERMINE REGION TO GET CODE POINTS FROM

	POP	eAX			; RECOVER CODE POINT

	MOV	eSI, CRT_CHAR_GEN	; OFFSET OF IMAGES

;-----	DETERMINE GRAPHICS MODE IN OPERATION
					; DETERMINE_MODE
	SAL	AX, 3			; MULTIPLY CODE POINT VALUE BY 8
	ADD	eSI, eAX		; SI HAS OFFSET OF DESIRED CODES
	
	CMP	byte [CRT_MODE], 6
	JC	short S6		; TEST FOR MEDIUM RESOLUTION MODE

;-----	HIGH RESOLUTION MODE

	add	edi, 0B8000h
S1:					; HIGH_CHAR
	PUSH	eDI			; SAVE REGEN POINTER
	PUSH	eSI			; SAVE CODE POINTER
	MOV	DH, 4			; NUMBER OF TIMES THROUGH LOOP
S2:
	LODSB				; GET BYTE FROM CODE POINTS
	TEST	BL, 80H			; SHOULD WE USE THE FUNCTION
	JNZ	short S5		; TO PUT CHAR IN
	STOSB				; STORE IN REGEN BUFFER
	LODSB
S4:
	MOV	[eDI+2000H-1], AL ; STORE IN SECOND HALF
	ADD	eDI, 79			; MOVE TO NEXT ROW IN REGEN
	DEC	DH			; DONE WITH LOOP
	JNZ	short S2
	POP	eSI
	POP	eDI			; RECOVER REGEN POINTER
	INC	eDI			; POINT TO NEXT CHAR POSITION
	LOOP	S1			; MORE CHARS TO WRITE
	retn

S5:
	XOR	AL, [eDI]		; EXCLUSIVE OR WITH CURRENT
	STOSB				; STORE THE CODE POINT
	LODSB				; AGAIN FOR ODD FIELD
	XOR	AL, [eDI+2000H-1]
	JMP	short S4		; BACK TO MAINSTREAM

;-----	MEDIUM RESOLUTION WRITE
S6:					; MED_RES_WRITE
	MOV	DL, BL			; SAVE HIGH COLOR BIT
	SAL	DI, 1			; OFFSET*2 SINCE 2 BYTES/CHAR
					; EXPAND BL TO FULL WORD OF COLOR
	AND	BL, 3			; ISOLATE THE COLOR BITS ( LOW 2 BITS )
	MOV	AL, 055H 		; GET BIT CONVERSION MULTIPLIER
	MUL	BL			; EXPAND 2 COLOR BITS TO 4 REPLICATIONS
	MOV	BL, AL			; PLACE BACK IN WORK REGISTER
	MOV	BH, AL			; EXPAND TO 8 REPLICATIONS OF COLOR BITS
	add	edi, 0B8000h
S7:                                     ; MED_CHAR
	PUSH	eDI			; SAVE REGEN POINTER
	PUSH	eSI			; SAVE THE CODE POINTER
	MOV	DH, 4			; NUMBER OF LOOPS
S8:
	LODSB				; GET CODE POINT
	CALL	S21			; DOUBLE UP ALL THE BITS
	AND	AX, BX			; CONVERT TO FOREGROUND COLOR ( 0 BACK )
	XCHG	AH, AL			; SWAP HIGH/LOW BYTES FOR WORD MOVE
	TEST	DL, 80H			; IS THIS XOR FUNCTION
	JZ	short S9		; NO, STORE IT IN AS IS
	XOR	AX, [eDI]		; DO FUNCTION WITH LOW/HIGH
S9:
	MOV	[eDI], AX		; STORE FIRST BYTE HIGH, SECOND LOW
	LODSB				; GET CODE POINT
	CALL	S21
	AND	AX, BX			; CONVERT TO COLOR
	XCHG	AH, AL			; SWAP HIGH/LOW BYTES FOR WORD MOVE
	TEST	DL, 80H			; AGAIN, IS THIS XOR FUNCTION
	JZ	short _S10		; NO, JUST STORE THE VALUES
	XOR	AX, [eDI+2000H]		; FUNCTION WITH FIRST HALF LOW
_S10:
	MOV	[eDI+2000H], AX		; STORE SECOND PORTION HIGH
	ADD	DI, 80			; POINT TO NEXT LOCATION
	DEC	DH
	JNZ	short S8		; KEEP GOING
	POP	eSI			; RECOVER CODE POINTER
	POP	eDI			; RECOVER REGEN POINTER
	INC	eDI			; POINT TO NEXT CHAR POSITION
	INC	eDI
	LOOP	S7			; MORE TO WRITE
	retn

; 04/07/2016
; 01/07/2016
; 30/06/2016 - TRDOS 386 (TRDOS v2.0)
; VIDEO1.ASM - 24/03/1985 (IBM PC-AT BIOS source code)
;----------------------------------------
; GRAPHICS READ
;----------------------------------------
GRAPHICS_READ:
	CALL	S26			; CONVERTED TO OFFSET IN REGEN
	MOV	eSI, eAX		; SAVE IN SI
	add	esi, 0B8000h		; 01/07/2016
	SUB	eSP, 8			; ALLOCATE SPACE FOR THE READ CODE POINT
	MOV	eBP, eSP		; POINTER TO SAVE AREA

;-----	DETERMINE GRAPHICS MODES
	mov	dh, 4			; number of passes ; 01/07/2016
	CMP	byte [CRT_MODE], 6
	JC	short S12		; MEDIUM RESOLUTION

;-----	HIGH RESOLUTION READ
;-----	GET VALUES FROM REGEN BUFFER AND CONVERT TO CODE POINT
	;MOV	DH,4			; NUMBER OF PASSES
S11:
	MOV	AL, [eSI] 		; GET FIRST BYTE
	MOV	[eBP], AL 		; SAVE IN STORAGE AREA
	INC	eBP			; NEXT LOCATION
	MOV	AL, [eSI+2000H]		; GET LOWER REGION BYTE
	MOV	[eBP], AL 		; ADJUST AND STORE
	INC	eBP
	ADD	eSI, 80			; POINTER INTO REGEN
	DEC	DH			; LOOP CONTROL
	JNZ	short S11		; DO IT SOME MORE
	JMP	SHORT S14		; GO MATCH THE SAVED CODE POINTS

;-----	MEDIUM RESOLUTION READ
S12:	
	SAL	SI, 1			; OFFSET*2 SINCE 2 BYTES/CHAR
	;MOV	DH, 4			; NUMBER OF PASSES
S13:
	CALL	S23			; GET BYTES FROM REGEN INTO SINGLE SAVE
	ADD	eSI, 2000H-2		; GO TO LOWER REGION
	CALL	S23			; GET THIS PAIR INTO SAVE
	SUB	eSI, 2000H-80+2		; ADJUST POINTER BACK INTO UPPER
	DEC	DH
	JNZ	short S13		; KEEP GOING UNTIL ALL 8 DONE

;-----	SAVE AREA HAS CHARACTER IN IT, MATCH IT
S14:					; FIND_CHAR
	MOV	eDI, CRT_CHAR_GEN	; ESTABLISH ADDRESSING
	SUB	eBP, 8			; ADJUST POINTER TO START OF SAVE AREA
	MOV	eSI, eBP
S15:
	mov	ax, 256			; NUMBER TO TEST AGAINST
S16:
	PUSH	eSI			; SAVE SAVE AREA POINTER
	PUSH	eDI			; SAVE CODE POINTER
	;MOV	eCX, 4			; NUMBER OF WORDS TO MATCH
	;REPE	CMPSW			; COMPARE THE 8 BYTES AS WORDS
	cmpsd				; compare first 4 bytes 
	jne	short S17		; 
	cmpsd				; compare last 4 bytes
S17:
	POP	eDI			; RECOVER THE POINTERS
	POP	eSI
	;JZ	short S18		; IF ZERO FLAG SET, THEN MATCH OCCURRED
	je	short S18
	;				; NO MATCH, MOVE ON TO NEXT
	ADD	eDI, 8			; NEXT CODE POINT
	dec	ax			; LOOP CONTROL
	JNZ	short S16		; DO ALL OF THEM

;-----	CHARACTER IS FOUND ( AL=0 IF NOT FOUND )
S18:	
	ADD	eSP, 8			; READJUST THE STACK, THROW AWAY SAVE
	retn				; ALL DONE

; 30/06/2016 - TRDOS 386 (TRDOS v2.0)
; VIDEO1.ASM - 24/03/1985 (IBM PC-AT BIOS source code)
;--------------------------------------------
; EXPAND BYTE
;  THIS ROUTINE TAKES THE BYTE IN AL AND DOUBLES ALL
;  OF THE BITS, TURNING THE 8 BITS INTO 16 BITS.
;  THE RESULT IS LEFT IN AX
;--------------------------------------------
S21:
	PUSH	CX			; SAVE REGISTER
	;MOV	CX, 8			; SHIFT COUNT REGISTER FOR ONE BYTE
	mov	cl, 8
S22:
	ROR	AL,1			; SHIFT BITS, LOW BIT INTO CARRY FLAG
	RCR	BP,1			; MOVE CARRY FLAG (LOW BIT INTO RESULTS
	SAR	BP,1			; SIGN EXTEND HIGH BIT (DOUBLE IT)
	;LOOP	S22			; REPEAT FOR ALL 8 BITS
	dec	cl
	jnz	short S22
	XCHG	AX, BP			; MOVE RESULTS TO PARAMETER REGISTER
	POP	CX			; RECOVER REGISTER
	RETn				; ALL DONE

; 01/07/2016 - TRDOS 386 (TRDOS v2.0)
; VIDEO1.ASM - 24/03/1985 (IBM PC-AT BIOS source code)
;--------------------------------------------------
; MED_READ_BYTE
; THIS ROUTINE WILL TAKE 2 BYTES FROM THE REGEN BUFFER,
;  COMPARE AGAINST THE CURRENT FOREGROUND COLOR, AND PLACE
;  THE CORRESPONDING ON/OFF BIT PATTERN INTO THE CURRENT
;  POSITION IN THE SAVE AREA
; ENTRY --
;  SI,DS = POINTER TO REGEN AREA OF INTEREST
;  BX = EXPANDED FOREGROUND COLOR
;  BP = POINTER TO SAVE AREA
; EXIT --
;  SI AND BP ARE INCREMENTED
;----------------------------------------------------
S23:
	LODSW				; GET FIRST BYTE AND SECOND BYTES
	XCHG	AL, AH			; SWAP FOR COMPARE
	MOV	CX, 0C000H		; 2 BIT MASK TO TEST THE ENTRIES
	MOV	DL, 0			; RESULT REGISTER
S24:
	TEST	AX, CX			; IS THIS SECTION BACKCROUND?
        JZ      short S25               ; IF ZERO, IT IS BACKGROUND (CARRY=0)
	STC				; WASN'T, SO SET CARRY
S25:
	RCL	DL, 1			; MOVE THAT BIT INTO THE RESULT
	SHR	CX, 2			; MOVE THE MASK TO THE RIGHT BY 2 BITS
	JNC	short S24		; DO IT AGAIN IF MASK DIDN'T FALL OUT
	MOV	[eBP], DL 		; STORE RESULT IN SAVE AREA
	INC	eBP			; ADJUST POINTER
	RETn				; ALL DONE

; 30/06/2016 - TRDOS 386 (TRDOS v2.0)
; VIDEO1.ASM - 24/03/1985 (IBM PC-AT BIOS source code)
;-----------------------------------------
; V4_POSITION
;  THIS ROUTINE TAKES THE CURSOR POSITION CONTAINED IN
;  THE MEMORY LOCATION, AND CONVERTS IT INTO AN OFFSET
;  INTO THE REGEN BUFFER, ASSUMING ONE BYTE/CHAR.
;  FOR MEDIUM RESOLUTION GRAPHICS, THE NUMBER MUST
;  BE DOUBLED.
; ENTRY -- NO REGISTERS,MEMORY LOCATION @CURSOR_POSN IS USED
; EXIT--
;  AX CONTAINS OFFSET INTO REGEN BUFFER
;-----------------------------------------
S26:
	movzx	eax, word [CURSOR_POSN]	; GET CURRENT CURSOR
GRAPH_POSN:
	PUSH	eBX			; SAVE REGISTER
	movzx	ebx, al			; SAVE A COPY OF CURRENT CURSOR
	MOV	AL, [CRT_COLS]		; GET BYTES PER COLUMN
	MUL	AH			; MULTIPLY BY ROWS
	SHL	AX, 2			; MULTIPLY * 4 SINCE 4 ROWS/BYTE
	ADD	eAX, eBX		; DETERMINE OFFSET
	POP	eBX			; RECOVER POINTER
	RETn				; ALL DONE

; 09/07/2016
; 01/07/2016 - TRDOS 386 (TRDOS v2.0)
; VIDEO1.ASM - 24/03/1985 (IBM PC-AT BIOS source code)
;---------------------------------------------
; SET_COLOR
;	THIS ROUTINE WILL ESTABLISH THE BACKGROUND COLOR, THE OVERSCAN COLOR,
;	AND THE FOREGROUND COLOR SET FOR MEDIUM RESOLUTION GRAPHICS
; INPUT
;	(BH) HAS COLOR ID
;		IF BH=0, THE BACKGROUND COLOR VALUE IS SET
;			FROM THE LOW BITS OF BL (0-31)
;		IF BH=1, THE PALETTE SELECTION IS MADE
;			BASED ON THE LOW BIT OF BL:
;				0 = GREEN, RED, YELLOW FOR COLORS 1,2,3
;				1 = BLUE, CYAN, MAGENTA FOR COLORS 1,2,3
;	(BL) HAS THE COLOR VALUE TO BE USED
; OUTPUT
;	THE COLOR SELECTION IS UPDATED
;----------------------------------------------
SET_COLOR:
        cmp     byte [CRT_MODE], 7      ; 09/07/2016
	ja	VIDEO_RETURN		; nothing to do for VGA modes	 

	;MOV	DX, [ADDR_6845]		; I/O PORT FOR PALETTE
	;mov	dx, 3D4h
	;ADD	DX,5			; OVERSCAN PORT
	mov	dx, 3D9h
	MOV	AL, [CRT_PALETTE] 	; GET THE CURRENT PALETTE VALUE
	OR	BH, BH			; IS THIS COLOR 0?
	JNZ	short M20		; OUTPUT COLOR 1

;-----	HANDLE COLOR 0 BY SETTING THE BACKGROUND COLOR

	AND	AL, 0E0H 		; TURN OFF LOW 5 BITS OF CURRENT
	AND	BL, 01FH 		; TURN OFF HIGH 3 BITS OF INPUT VALUE
	OR	AL, BL			; PUT VALUE INTO REGISTER
M19:					; OUTPUT THE PALETTE
	OUT	DX, AL			; OUTPUT COLOR SELECTION TO 3D9 PORT
	MOV	[CRT_PALETTE], AL 	; SAVE THE COLOR VALUE
	JMP	VIDEO_RETURN

;-----	HANDLE COLOR 1 BY SELECTING THE PALETTE TO BE USED

M20:
	AND	AL, 0DFH 		; TURN OFF PALETTE SELECT BIT
	SHR	BL, 1			; TEST THE LOW ORDER BIT OF BL
	JNC	short M19		; ALREADY DONE
	OR	AL, 20H			; TURN ON PALETTE SELECT BIT
	JMP	short M19		; GO DO IT

; 09/07/2016
; 01/07/2016 - TRDOS 386 (TRDOS v2.0)
; VIDEO1.ASM - 24/03/1985 (IBM PC-AT BIOS source code)
;--------------------------------------------
; READ DOT -- WRITE DOT
; THESE ROUTINES WILL WRITE A DOT, OR READ THE
;  DOT AT THE INDICATED LOCATION
; ENTRY --
;   DX = ROW (0-199)	(THE ACTUAL VALUE DEPENDS ON THE MODE)
;   CX = COLUMN ( 0-639) ( THE VALUES ARE NOT RANGE CHECKED )
;   AL = DOT VALUE TO WRITE (1,2 OR 4 BITS DEPENDING ON MODE,
;	REQUIRED FOR WRITE DOT ONLY, RIGHT JUSTIFIED)
;	BIT 7 OF AL = 1 INDICATES XOR THE VALUE INTO THE LOCATION
;   DS = DATA SEGMENT
;   ES = REGEN SEGMENT
;
; EXIT
;	AL = DOT VALUE READ, RIGHT JUSTIFIED, READ ONLY
;----------------------------------------------

READ_DOT:
	; 09/07/2016
	mov	ah,  [CRT_MODE]
	cmp	ah, 7 ; 6!?
	jna	short read_dot_cga

	call	vga_read_pixel
	; al = pixel value
	jmp	_video_return

read_dot_cga:
	;je	VIDEO_RETURN ; 7	
	cmp	ah, 4 ; graphics ? 
	jb	VIDEO_RETURN ; no, text mode, nothing to do

	CALL	R3			; DETERMINE BYTE POSITION OF DOT
	MOV	AL, [eSI]		; GET THE BYTE
	AND	AL, AH			; MASK OFF THE OTHER BITS IN THE BYTE
	SHL	AL, CL			; LEFT JUSTIFY THE VALUE
	MOV	CL, DH			; GET NUMBER OF BITS IN RESULT
	ROL	AL, CL			; RIGHT JUSTIFY THE RESULT
	;JMP	VIDEO_RETURN		; RETURN FROM VIDEO I/O
	movzx	eax, al
	jmp	_video_return

; 09/07/2016
; 01/07/2016 - TRDOS 386 (TRDOS v2.0)
; VIDEO1.ASM - 24/03/1985 (IBM PC-AT BIOS source code)

WRITE_DOT:
	; 09/07/2016
	mov	ah, [CRT_MODE]
	cmp	ah, 7 ; 6!?
	jna	short write_dot_cga
	
	call	vga_write_pixel
	jmp	VIDEO_RETURN

write_dot_cga:
	;je	VIDEO_RETURN ; 7	
	cmp	ah, 4 ; graphics ? 
	jb	VIDEO_RETURN ; no, text mode, nothing to do

	;PUSH	AX			; SAVE DOT VALUE
	PUSH	AX			; TWICE
	CALL	R3			; DETERMINE BYTE POSITION OF THE DOT
	SHR	AL, CL			; SHIFT TO SET UP THE BITS FOR OUTPUT
	AND	AL, AH			; STRIP OFF THE OTHER BITS
	MOV	CL, [eSI]		; GET THE CURRENT BYTE
	POP	BX			; RECOVER XOR FLAG
	TEST	BL, 80H			; IS IT ON
	JNZ	short R2		; YES, XOR THE DOT
	NOT	AH			; SET MASK TO REMOVE THE INDICATED BITS
	AND	CL, AH
	OR	AL, CL			; OR IN THE NEW VALUE OF THOSE BITS
R1:					; FINISH_DOT
	MOV	[eSI], AL		; RESTORE THE BYTE IN MEMORY
	;POP	AX
	JMP	VIDEO_RETURN		; RETURN FROM VIDEO I/O
R2:					; XOR_DOT
	XOR	AL, CL			; EXCLUSIVE OR THE DOTS
	JMP	short R1		; FINISH UP THE WRITING

; 01/07/2016 - TRDOS 386 (TRDOS v2.0)
; VIDEO1.ASM - 24/03/1985 (IBM PC-AT BIOS source code)

;----------------------------------------------
; THIS SUBROUTINE DETERMINES THE REGEN BYTE LOCATION OF THE
; INDICATED ROW COLUMN VALUE IN GRAPHICS MODE.
; ENTRY --
;  DX = ROW VALUE (0-199)
;  CX = COLUMN VALUE (0-639)
; EXIT --
;  SI = OFFSET INTO REGEN BUFFER FOR BYTE OF INTEREST
;  AH = MASK TO STRIP OFF THE BITS OF INTEREST
;  CL = BITS TO SHIFT TO RIGHT JUSTIFY THE MASK IN AH
;  DH = # BITS IN RESULT
;  BX = MODIFIED
;-----------------------------------------------
R3:

;-----	DETERMINE 1ST BYTE IN INDICATED ROW BY MULTIPLYING ROW VALUE BY 40
;-----	 ( LOW BIT OF ROW DETERMINES EVEN/ODD, 80 BYTES/ROW )

	movzx	esi, ax			; WILL SAVE AL AND AH DURING OPERATION
	MOV	AL, 40
	MUL	DL			; AX= ADDRESS OF START OF INDICATED ROW
	TEST	AL, 08H 		; TEST FOR EVEN/ODD ROW CALCULATED
	JZ	short R4		; JUMP IF EVEN ROW
	ADD	AX, 2000H-40		; OFFSET TO LOCATION OF ODD ROWS ADJUST
R4:					; EVEN_ROW
	XCHG	SI, AX			; MOVE POINTER TO (SI) AND RECOVER (AX)
	add	esi, 0B8000h
	MOV	DX, CX			; COLUMN VALUE TO DX

;-----	DETERMINE GRAPHICS MODE CURRENTLY IN EFFECT

; SET UP THE REGISTERS ACCORDING TO THE MODE
; CH = MASK FOR LOW OF COLUMN ADDRESS ( 7/3 FOR HIGH/MED RES )
; CL = # OF ADDRESS BITS IN COLUMN VALUE ( 3/2 FOR H/M )
; BL = MASK TO SELECT BITS FROM POINTED BYTE ( 80H/C0H FOR H/M )
; BH = NUMBER OF VALID BITS IN POINTED BYTE ( 1/2 FOR H/M )

	MOV	BX, 2C0H
	MOV	CX, 302H 		; SET PARMS FOR MED RES
	CMP	byte [CRT_MODE], 6
	JC	short R5		; HANDLE IF MED RES
	MOV	BX, 180H
	MOV	CX, 703H 		; SET PARMS FOR HIGH RES

;-----	DETERMINE BIT OFFSET IN BYTE FROM COLUMN MASK
R5:
	AND	CH, DL			; ADDRESS OF PEL WITHIN BYTE TO CH

;-----	DETERMINE BYTE OFFSET FOR THIS LOCATION IN COLUMN

	SHR	DX, CL			; SHIFT BY CORRECT AMOUNT
	ADD	SI, DX			; INCREMENT THE POINTER
	MOV	DH, BH			; GET THE # OF BITS IN RESULT TO DH

;-----	MULTIPLY BH (VALID BITS IN BYTE) BY CH (BIT OFFSET)

	SUB	CL, CL			; ZERO INTO STORAGE LOCATION
R6:
	ROR	AL, 1			; LEFT JUSTIFY VALUE IN AL (FOR WRITE)
	ADD	CL, CH			; ADD IN THE BIT OFFSET VALUE
	DEC	BH			; LOOP CONTROL
	JNZ	short R6		; ON EXIT, CL HAS COUNT TO RESTORE BITS
	MOV	AH, BL			;  GET MASK TO AH
	SHR	AH, CL			;  MOVE THE MASK TO CORRECT LOCATION
	RETn				;  RETURN WITH EVERYTHING SET UP

load_dac_palette:
	; 29/07/2016
	; 23/07/2016
	; 03/07/2016 (TRDOS 386 = TRDOS v2.0)
	; (set_mode_vga)
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'load_dac_palette'
	;
	; Oracle VirtualBox 5.0.24 VGABios Source Code
	; ('vgabios.c', 'vgatables.h', 'vgafonts.h', 'vgarom.asm')
	;
	; INPUT -> AH = DAC selection number (3, 2 or 1)
	; OUTPUT -> ECX = 0, AX = 0
	; (Modifed registers: EAX, ECX, EDX, ESI)
	;
	mov	dx, 3C8h  ; VGAREG_DAC_WRITE_ADDRESS
	sub	al, al ; 0
	out	dx, al ; 0 ; color index, always 0 at the beginning	
	inc	dx   ; 3C9h ; VGAREG_DAC_DATA
	mov	ecx, 256   ; always 256*3 values
	;push	esi
	mov	al, ah
	mov	ah, 3Fh	; 3Fh except DAC selection number 3
	cmp 	al, 2
	je	short l_dac_p_2
	ja	short l_dac_p_3
	and	al, al
	jnz	short l_dac_p_1
l_dac_p_0:
	mov	esi, palette0
	jmp	short l_dac_p_4	
l_dac_p_1:
	mov	esi, palette1
	jmp	short l_dac_p_4
l_dac_p_2:
	mov	esi, palette2
	jmp	short l_dac_p_4
l_dac_p_3:
	mov	ah, 0FFh ; dac registers
	mov	esi, palette3
l_dac_p_4:
	lodsb
	out	dx, al  ; Red
	lodsb
	out	dx, al	; Green
	lodsb
	out	dx, al	; Blue
	and	ah, ah
	jz	short l_dac_p_5	
	dec	ah
	loop	l_dac_p_4
	;pop	esi
	retn
l_dac_p_5:
	; 29/07/2016
	dec	cl
	jz	short l_dac_p_7
	;
	sub	al, al ; 0
l_dac_p_6:
	out	dx, al ; outb(VGAREG_DAC_DATA,0);
	out	dx, al
	out	dx, al
	loop	l_dac_p_6
l_dac_p_7:
	;pop	esi
	retn

gray_scale_summing:
	; 03/07/2016 (TRDOS 386 = TRDOS v2.0)
	; (set_mode_vga)
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_perform_gray_scale_summing'
	;
	; Oracle VirtualBox 5.0.24 VGABios Source Code
	; ('vgabios.c', 'vgatables.h', 'vgafonts.h', 'vgarom.asm')
	;

	; INPUT -> EBX = Start address (color index <= 255)
	;	   ECX = Count (<= 256)
	; OUTPUT -> (E)CX = 0
	; (Modifed registers: EAX, ECX, EDX, EBX)

	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	xor	al, al ; 0
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	out	dx, al	; clear bit 5
			; (while loading palette registers)
	; set read address and switch to read mode
g_s_s_1:
	mov	dx, 3C7h ; VGAREG_DAC_READ_ADDRESS
	mov	al, bl
	out	dx, al
	; get 6-bit wide RGB data values
 	; intensity = (0.3*Red)+(0.59*Green)+(0.11*Blue)
	; i = ( ( 77*r + 151*g + 28*b ) + 0x80 ) >> 8;
	mov	dx, 3C9h ; VGAREG_DAC_DATA
	in	al, dx ; red
	mov	ah, 77 ; 0.3* Red
	mul	ah
	push	ax
	in	al, dx ; green
	mov	ah, 151  ; 0.59 * Green
	mul	ah
	push	ax
	in	al, dx ; blue
	mov	ah, 28 ; 0.11 * Blue
	mul	ah
	pop	dx
	add	ax, dx
	pop	dx
	add	ax, dx
	add	ax, 80h  
	mov	al, 3Fh
	cmp	ah, al
	jna	short g_s_s_2
	mov	ah, al
g_s_s_2:
	mov	dx, 3C8h  ; VGAREG_DAC_WRITE_ADDRESS
	mov	al, bl ; color index
	out	dx, al
	mov	al, ah ; intensity
	inc	dx ; 3C9h ; VGAREG_DAC_DATA
	out	dx, al ; R (R=G=B)
	mov	al, ah ; intensity
	out	dx, al ; G (R=G=B)
 	mov	al, ah ; intensity
	out	dx, al ; B (R=G=B)
	dec	cx
	jz	short g_s_s_3
	inc	bl    ; next color index value
	jmp	short g_s_s_1
g_s_s_3:
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	mov	al, 20h
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	out	dx, al ; 20h -> set bit 5
		        ; (after loading palette regs)	
	retn

vga_write_char_attr:
vga_write_char_only: 
	; 08/07/2016 (TRDOS 386 = TRDOS v2.0)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_write_char_attr'
	; 'biosfn_write_char_only'

	; INPUT ->
	; [CRT_MODE] = current video mode (>7)
	; CX = Count of characters to write
	; AL = Character to write
	; BL = Color of character
	; OUTPUT ->
	; Regen buffer updated
 
	mov 	ah, [CRT_MODE]
	mov	dx, [CURSOR_POSN] ; cursor pos for page 0

	mov	esi, vga_modes
	mov	edi, esi
	add	edi, vga_mode_count
vga_wca_0:
	lodsb
	cmp	al, ah ; [CRT_MODE]
	je	short vga_wca_2
	cmp	esi, edi
	jb	short vga_wca_0
vga_wca_1:
	retn	; nothing to do
vga_wca_2:
	add	esi, vga_memmodel - (vga_modes + 1)  
	; [ESI] = VGA memory model number (LINEAR8, PLANAR4, PLANAR1)

	; biosfn_write_char_attr (car,page,attr,count) 
	; AL = car, page = 0, BL = attr, CX = count
	cmp	byte [esi], PLANAR4
	je	short vga_wca_planar
	cmp	byte [esi], PLANAR1
	je	short vga_wca_planar
vga_wca_linear8:
	; while((count-->0) && (xcurs<nbcols))
	; CX = count
	and	cx, cx
	jz	short vga_wca_1
	cmp	dl, [CRT_COLS]
	jnb	short vga_wca_1
	; write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols);
	; AL = car, BL = attr, DL = xcurs, DH = ycurs, 
	; [CRT_COLS] = nbcols
	call	write_gfx_char_lin	 
	dec	cx ; count
	inc	dl ; xcurs
	jmp	short vga_wca_linear8
vga_wca_planar:
	; while((count-->0) && (xcurs<nbcols))
	; CX = count
	and	cx, cx
	jz	short vga_wca_1
	cmp	dl, [CRT_COLS]
	jnb	short vga_wca_1
	; write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight);
	; AL = car, BL = attr, DL = xcurs, DH = ycurs, 
	; [CRT_COLS] = nbcols, [CHAR_HEIGHT] = cheight
	call	write_gfx_char_pl4
	dec	cx ; count
	inc	dl ; xcurs
	jmp	short vga_wca_planar

write_gfx_char_lin:
	; 08/08/2016
	; 31/07/2016
	; 08/07/2016 (TRDOS 386 = TRDOS v2.0)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'write_gfx_char_lin'

	; write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols)
	; INPUT ->
	; AL = car, BL = attr, DL = xcurs, DH = ycurs, 
	; [CRT_COLS] = nbcols
	; OUTPUT ->
	; Regen buffer updated

	push	ecx
	push	ebx
	push	edx
	push	eax
	; addr=xcurs*8+ycurs*nbcols*64;
	; 08/08/2016
	movzx	esi, al ; car
	movzx	eax, dh ; ycurs
	mov	ah, [CRT_COLS] ; nbcols
	mul	ah
	;shl	ax, 6 ; * 64
 	shl	ax, 3 ; * 8
	;sub	dh, dh
	;shl	dx, 3 ; xcurs * 8
	;movzx	edi, dx
	movzx	edi, dl
	shl	di, 3 ; xcurs * 8
	xor	dh, dh
	mov	dl, [CHAR_HEIGHT]
	mul	dx
	; eax = ycurs*nbcols*8*[CHAR_HEIGHT]
	add	edi, eax ; addr
	add	edi, 0A0000h
	;shl	si, 3 ; car * 8
	xor	ah, ah
	mov	al, [CHAR_HEIGHT]
	mul	si
	mov	si, ax
	;; esi = src = car * 8
	; esi = src = car * [CHAR_HEIGHT]
	; i = 0
	;add	esi, vgafont8 ; fdata [src+i]
	; 08/08/2016
	mov	eax, [VGA_INT43H]
	cmp	eax, vgafont16
        je      short wgfxl_0
	cmp	eax, vgafont14
	je	short wgfxl_0
	add	esi, vgafont8
	jmp	short wgfxl_1
wgfxl_0:
	add	esi, eax
wgfxl_1:
	sub	bh, bh ; i = 0
wgfxl_2:
	; for(i=0;i<8;i++)
	push	edi ; addr
	movzx	eax, byte [CRT_COLS] ; nbcols
	mul	bh ; nbcols*i
	shl	ax, 3 ; i*nbcols*8
 	; dest=addr+i*nbcols*8;
	add	edi, eax ; dest + j ; j = 0
	mov	cl, 80h ; mask = 0x80;
	; esi = fdata + src + i
	; for(j=0;j<8;j++)
	sub	edx, edx ; j = 0
wgfxl_3:
	mov	al, [esi] ; al = fdata[src+i]
	and	al, cl ; if (fdata[src+i] & mask)
	jz	short wgfxl_4  ; data = 0, zf = 1
	mov	al, bl ; data = attr;
wgfxl_4:
	; write_byte(0xa000,dest+j,data);		
	stosb  ; dest + j (+ 0A0000h)
	;inc	dl ; j++
	;cmp	dl, 8
	cmp	dl, 7
	jb	short wgfxl_5
	pop	edi
	; 08/08/2016
	;cmp	bh, 7
	;jnb	short wgfxl_6
	inc	bh ; i++
	cmp	bh, [CHAR_HEIGHT]
	jnb	short wgfxl_6
	inc	esi
	jmp	short wgfxl_2
wgfxl_5:
	shr	cl, 1 ; mask >>= 1;
	inc	dl ; j++
        jmp     short wgfxl_3
wgfxl_6:
	pop	eax
	pop	edx
	pop	ebx
	pop	ecx
	retn

write_gfx_char_pl4:
	; 08/08/2016
	; 08/07/2016 (TRDOS 386 = TRDOS v2.0)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'write_gfx_char_pl4'

	; write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight)
	; INPUT ->
	; AL = car, BL = attr, DL = xcurs, DH = ycurs, 
	; [CRT_COLS] = nbcols, [CHAR_HEIGHT] = cheight
	; OUTPUT ->
	; Regen buffer updated

	push	ecx
	push	ebx
	push	edx
	push	eax
wgfxpl_f0:
	; switch(cheight)
	mov	ah, [CHAR_HEIGHT]
	cmp	ah, 16 ; case 16:
	jne	short wgfxpl_f1
	; fdata = &vgafont16;
	mov	esi, vgafont16
	jmp	short wgfxpl_f3
wgfxpl_f1:
	cmp	ah, 14 ; case 14:
	jne	short wgfxpl_f2
	mov	esi, vgafont14
	jmp	short wgfxpl_f3
wgfxpl_f2:
	; default:
	;  fdata = &vgafont8;
	mov	esi, vgafont8
	mov	ah, 8	
wgfxpl_f3:
	; al = car
	mul	ah ; ah = cheight
	and	eax, 0FFFFh ; car * cheight
	; src = car * cheight;
	add	esi, eax ; esi = fdata[src+i]
	; addr=xcurs*8+ycurs*nbcols*64;
	mov	al, dh ; ycurs
	mov	ah, [CRT_COLS] ; nbcols
	mul	ah
	; 08/08/2016
	;shl	ax, 6 ; * 64
	shl	ax, 3 ; * 8
	;sub	dh, dh ; 0
	;shl	dx, 3 ; xcurs * 8
	;movzx	edi, dx
	movzx	edi, dl
	shl	di, 3 ; xcurs * 8
	xor	dh, dh
	mov	dl, [CHAR_HEIGHT]
	mul	dx
	; eax = ycurs*nbcols*8*[CHAR_HEIGHT]
	add	edi, eax ; addr
	add	edi, 0A0000h
	;
	; outw(VGAREG_SEQU_ADDRESS, 0x0f02);
	; outw(VGAREG_GRDC_ADDRESS, 0x0205);
	mov	dx, 3C4h ; VGAREG_SEQU_ADDRESS
	mov	ax, 0F02h
	out	dx, ax
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	mov	ax, 0205h
	out	dx, ax
	;
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	test	bl, 80h ; if(attr&0x80)
	jz	short wgfxpl_f4 ; else
	; outw(VGAREG_GRDC_ADDRESS, 0x1803);
	mov	ax, 1803h
	jmp	short wgfxpl_f5
wgfxpl_f4:
	; outw(VGAREG_GRDC_ADDRESS, 0x0003);
	mov	ax, 0003h	
wgfxpl_f5:
	out	dx, ax
	;	
	sub	bh, bh ; i = 0
wgfxpl_0:
	; for(i=0;i<cheight;i++)
	push	edi ; addr
	movzx	eax, byte [CRT_COLS] ; nbcols
	mul	bh ; nbcols*i
	; dest=addr+i*nbcols
	add	edi, eax ; dest
	mov	ch, 80h ; mask = 0x80;
	; for(j=0;j<8;j++)
	sub	cl, cl ; j = 0
wgfxpl_1:
	shr	ch, cl ; mask=0x80>>j;
	;
	; outw(VGAREG_GRDC_ADDRESS, (mask << 8) | 0x08);
     	; read_byte(0xa000,dest);
	;mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	mov	ah, ch
	mov	al, 8
	out	dx, ax
	mov	al, [edi] ; ? (io delay?)
	;
	sub	al, al ; attr = 0
	; if (fdata[src+i] & mask)
	test	byte [esi], ch
	jz	short wgfxpl_2  ; zf = 1
	; write_byte(0xa000,dest,attr&0x0f);
	mov	al, bl ; attr;
	and	al, 0Fh	; attr&0x0f
wgfxpl_2:
	; write_byte(0xa000,dest,0x00);		
	mov	[edi], al ; dest (+ 0A0000h)
	inc	cl ; j++
	cmp	cl, 8
	jb	short wgfxpl_1
	pop	edi
	; 08/08/2016
	;cmp	bh, 7
	;jnb	short wgfxpl_3
	inc	bh ; i++
	cmp	bh, [CHAR_HEIGHT]
	jnb	short wgfxpl_3
	inc	esi
	jmp	short wgfxpl_0
wgfxpl_3:
	;mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
  	mov	ax, 0FF08h
	out	dx, ax
	mov	ax, 0005h
	out	dx, ax
	mov	ax, 0003h
	out	dx, ax
	;
	pop	eax
	pop	edx
	pop	ebx
	pop	ecx
	retn

vga_write_pixel: 
	; 09/07/2016 (TRDOS 386 = TRDOS v2.0)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_write_pixel'

	; INPUT ->
	; 	DX = row (0-239)
	; 	CX = column (0-799)
	; 	AL = pixel value
	;	(AH = [CRT_MODE])
	; OUTPUT ->
	; 	none
 
	mov	bl, al ; pixel value
	;mov 	ah, [CRT_MODE]
	mov	esi, vga_modes
	mov	edi, esi
	add	edi, vga_mode_count
vga_wp_0:
	lodsb
	cmp	al, ah ; [CRT_MODE]
	je	short vga_wp_1
	cmp	esi, edi
	jb	short vga_wp_0
	retn	; nothing to do
vga_wp_1:
	add	esi, vga_memmodel - (vga_modes + 1)  
	; [ESI] = VGA memory model number (LINEAR8, PLANAR4, PLANAR1)
	mov	edi, 0A0000h
	;
	cmp	byte [esi], PLANAR4
	je	short vga_wp_planar
	cmp	byte [esi], PLANAR1
	je	short vga_wp_planar
vga_wp_linear8:
	; addr=CX+DX*(read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
	movzx	eax, byte [CRT_COLS] ; = [VGA_COLS] ; nbcols
     	shl	ax, 3 ; * 8
	mul	dx
	push	eax
	;mov	edi, 0A0000h
	add	di, cx
	pop	eax
	add	edi, eax ; addr
	; write_byte(0xa000,addr,AL);
	mov	[edi], bl
	retn    
vga_wp_planar:
	; addr = CX/8+DX*read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
	movzx	eax, cx
	shr	ax, 3 ; CX/8
	push	eax
	sub	ah, ah ; 0
	mov	al, [CRT_COLS] ; = [VGA_COLS] ; nbcols
	mul	dx
	;mov	edi, 0A0000h
        add     di, ax
	pop	eax
	add	edi, eax ; addr
	and	cl, 7
	mov	ch, 80h ; mask
	shr	ch, cl 	; mask = 0x80 >> (CX & 0x07);
	
	; outw(VGAREG_GRDC_ADDRESS, (mask << 8) | 0x08);
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	mov	ah, ch
	mov	al, 8
	out	dx, ax
	; outw(VGAREG_GRDC_ADDRESS, 0x0205);
	mov	ax, 0205h
	out	dx, ax
	; data = read_byte(0xa000,addr);
	mov	al, [edi] ; (delay?)	
	; if (AL & 0x80)
	; {
	;  outw(VGAREG_GRDC_ADDRESS, 0x1803);
	; }
	test	bl, 80h
	jz	short vga_wp_2
	mov	ax, 1803h
	out	dx, ax
vga_wp_2:	
	; write_byte(0xa000,addr,AL);
	mov	[edi], bl
	;
	;mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
  	mov	ax, 0FF08h
	out	dx, ax
	mov	ax, 0005h
	out	dx, ax
	mov	ax, 0003h
	out	dx, ax
	;
	retn

vga_read_pixel: 
	; 09/07/2016 (TRDOS 386 = TRDOS v2.0)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_read_pixel'

	; INPUT ->
	; 	DX = row (0-239)
	; 	CX = column (0-799)
	;	(AH = [CRT_MODE])
	; OUTPUT ->
	; 	AL = pixel value
	 
	;mov 	ah, [CRT_MODE]
	mov	esi, vga_modes
	mov	edi, esi
	add	edi, vga_mode_count
vga_rp_0:
	lodsb
	cmp	al, ah ; [CRT_MODE]
	je	short vga_rp_1
	cmp	esi, edi
	jb	short vga_rp_0
	retn	; nothing to do
vga_rp_1:
	add	esi, vga_memmodel - (vga_modes + 1)  
	; [ESI] = VGA memory model number (LINEAR8, PLANAR4, PLANAR1)
	mov	edi, 0A0000h
	;
	cmp	byte [esi], PLANAR4
	je	short vga_rp_planar
	cmp	byte [esi], PLANAR1
	je	short vga_rp_planar
vga_rp_linear8:
	; addr=CX+DX*(read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
	movzx	eax, byte [CRT_COLS] ; = [VGA_COLS] ; nbcols
     	shl	ax, 3 ; * 8
	mul	dx
	push	eax
	;mov	edi, 0A0000h
	add	di, cx
	pop	eax
	add	edi, eax ; addr
	; attr=read_byte(0xa000,addr);
	mov	al, [edi] ; pixel value
	retn    
vga_rp_planar:
	; addr = CX/8+DX*read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
	movzx	eax, cx
	shr	ax, 3 ; CX/8
	push	eax
	sub	ah, ah ; 0
	mov	al, [CRT_COLS] ; = [VGA_COLS] ; nbcols
	mul	dx
	;mov	edi, 0A0000h
        add     di, ax
	pop	eax
	add	edi, eax ; addr
	and	cl, 7
	mov	ch, 80h ; mask
	shr	ch, cl 	; mask = 0x80 >> (CX & 0x07);
	; attr = 0x00;
	xor	bl, bl ; attr = bl = 0, 
	xor	cl, cl ; i = cl = 0
	; for(i=0;i<4;i++)
      	; {
       	;  outw(VGAREG_GRDC_ADDRESS, (i << 8) | 0x04);
       	;  data = read_byte(0xa000,addr) & mask;
       	;  if (data > 0) attr |= (0x01 << i);
      	; }
vga_rp_2:
	mov	ah, cl ; i << 8
	mov	al, 4  ; | 0x04
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	out	dx, ax
	; data = read_byte(0xa000,addr) & mask;
	mov	al, [edi]
	and	al, ch ; & mask
	; if (data > 0) attr |= (0x01 << i);
	or	al, al
	jz	short vga_rp_3 ; al = 0 
	mov	bh, 1
	shl	bh, cl ; (0x01 << i)
	or	bl, bh ; attr |= (0x01 << i)
	mov	al, bl ; pixel value	
vga_rp_3:	
	retn

vga_beeper:
	; 04/08/2016  (TRDOS 386 = TRDOS v2.0)
	sti
	;mov	bh, [ACTIVE_PAGE]
        jmp     beeper_gfx

vga_write_teletype:
	; 09/12/2017
	; 06/08/2016
	; 04/08/2016
	; 01/08/2016
	; 31/07/2016
	; 09/07/2016 (TRDOS 386 = TRDOS v2.0)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_write_teletype'
	; 'biosfn_write_char_only'

	; INPUT ->
	; [CRT_MODE] = current video mode (>7)
	; AL = Character to write
	; BL = Color of character
	; OUTPUT ->
	; Regen buffer updated

	; biosfn_write_teletype (car, page, attr, flag) 
	; car = character (AL)
	; page = 0
	; attr = color (BL)
	; 'flag' not used

	mov 	ah, [CRT_MODE]
	mov	bh, al ; character
	mov	dx, [CURSOR_POSN] ; cursor pos for page 0

	mov	esi, vga_g_modes
	mov	edi, esi
	add	edi, vga_g_mode_count
vga_wtty_0:
	lodsb
	cmp	al, ah ; [CRT_MODE]
	je	short vga_wtty_2
	cmp	esi, edi
	jb	short vga_wtty_0
vga_wtty_1:
	retn	; nothing to do
vga_wtty_2:
	cmp	bh, 07h ; bell (beep)
	je	short vga_beeper  ; u11
	cmp	bh, 08h ; backspace
	jne	short vga_wtty_3
	; if(xcurs>0)xcurs--;
	or	dl, dl ; xcurs (column)
	jz	short vga_wtty_1
	dec	dl ; xcurs--;
        jmp     short vga_wtty_12 
vga_wtty_3:			
	cmp	bh, 0Dh ; carriage return (\r)
	jne	short vga_wtty_4
	; xcurs=0;
	sub	dl, dl ; 0
        jmp     short vga_wtty_12 
vga_wtty_4:	
	cmp	bh, 0Ah ; new line (\n)
	jne	short vga_wtty_5
	; ycurs++;
	inc	dh ; next row
        jmp     short vga_wtty_11
vga_wtty_5:
	cmp 	bh, 09h ; tab stop
	jne	short vga_wtty_8
	mov	al, dl
	;cbw
	xor	ah, ah ; 09/12/2017
	mov	cl, 8
	div	cl
	sub	cl, ah
	;
	mov	bh, 20h ; space
vga_wtty_6: ; tab stop loop
	push	cx
	push	bx
	call	vga_wtty_8
	pop	bx  ; bh = character, bl = color
	pop	cx
	dec	cl
	jz	short vga_wtty_7
	mov	dx, [CURSOR_POSN] ; new cursor position (pg 0)
	jmp	short vga_wtty_6
vga_wtty_7:
	retn
	;
vga_wtty_8:
	add	esi, vga_g_memmodel - (vga_g_modes + 1)  
	; [ESI] = VGA memory model number (LINEAR8, PLANAR4, PLANAR1)
	mov	edi, 0A0000h
	;
	mov	al, bh ; character
	;
	cmp	byte [esi], PLANAR4
	je	short vga_wtty_planar
	cmp	byte [esi], PLANAR1
	je	short vga_wtty_planar
vga_wtty_linear8:
	; write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols);
	; AL = car, BL = attr (color), DL = xcurs, DH = ycurs, 
	; [CRT_COLS] = nbcols
	call	write_gfx_char_lin
	jmp	short vga_wtty_9

vga_wtty_12:
	; 09/07/2016
	; set cursor position
	; NOTE: Hardware cursor position will not be set
	;   in any VGA modes (>7)
	;   But, cursor position will be saved into
	;   [CURSOR_POSN].
	;   TRDOS 386 (TRDOS v2.0) uses only one page
	;   (page 0) for all graphics modes.

	mov	[CURSOR_POSN], dx ; save cursor pos for pg 0
	; 04/08/2016
	;mov	bh, [ACTIVE_PAGE] ; = 0
	;call	_set_cpos
	retn

vga_wtty_planar:
	; write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight);
	; AL = car, BL = attr (color), DL = xcurs, DH = ycurs, 
	; [CRT_COLS]= nbcols, [CHAR_HEIGHT] = cheight
	call	write_gfx_char_pl4
vga_wtty_9:
	inc	dl ; xcurs++;
vga_wtty_10:
	; Do we need to wrap ?
	; if(xcurs==nbcols)
	cmp	dl, [CRT_COLS] ; [VGA_COLS]
	jb	short vga_wtty_11 ; no
	sub	dl, dl ; xcurs=0;
	inc	dh ;  ycurs++;
vga_wtty_11:
	; Do we need to scroll ?
	; if(ycurs==nbrows)
	cmp	dh, [VGA_ROWS]
	jb	short vga_wtty_12 ; no
	;
	; biosfn_scroll (nblines,attr,rul,cul,rlr,clr,page,dir)
	; al = nblines = 1, bl = attr (color) = 0
	; ch = rul, cl = cul, dh = rlr, dl = clr, page = 0
	; dir = SCROLL_UP
	
	mov	al, 1
	sub	bl, bl ; 0 ; blank/black line (attr=0) will be used
	sub	cx, cx ; 0,0

	; 06/08/2016
	mov	dh, [VGA_ROWS]
	dec	dh ; nbrows -1

	push	dx 	; 04/08/2016
	mov	dl, [CRT_COLS]
	dec	dl ; nbcols -1
	
	mov	ah, [CRT_MODE]
	
	; biosfn_scroll(0x01,0x00,0,0,nbrows-1,nbcols-1,page,SCROLL_UP);
	call	vga_graphics_up
	; 04/08/2016
	pop	dx
	;dec	dh ; ycurs-=1
	jmp	short vga_wtty_12 

font_setup:
	; 09/07/2016
	; character generator (font loading) functions
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'int10_func'

	; AX = 1100H ; Load User-Defined Font (EGA/VGA)
	;
        ; BH    height of each character (bytes per character definition)
        ; (BL   font block to load (EGA: 0-3; VGA: 0-7))
	; CX    number of characters to redefine (<=256)
        ; DX    ASCII code of the first character defined at ES:BP
        ; EBP	address of font-definition information
	;	(in user's memory space)

	; case 0x11:
     	; switch(GET_AL())
      	; {
	; case 0x00:
        ; case 0x10:
        ; biosfn_load_text_user_pat(GET_AL(),ES,BP,CX,DX,GET_BL(),GET_BH());
        ; break;

	; AX = 1110H ; Load and Activate User-Defined Font (EGA/VGA)
	or	al, al ; 0
	jz	short font_setup_0
	cmp	al, 10h
	jne	short font_setup_1	
font_setup_0:
	call	transfer_user_fonts
	jc	short font_setup_error
	call	load_text_user_pat
        jmp     VIDEO_RETURN 
font_setup_1:
	; AX = 1101H ; Load ROM 8x14 Character Set (EGA/VGA)
	; case 0x01:
        ; case 0x11:
        ; biosfn_load_text_8_14_pat(GET_AL(),GET_BL());
        ; break;
	cmp	al, 1
	je	short font_setup_2
	cmp	al, 11h
	jne	short font_setup_3	
font_setup_2:
	; AX = 1111H ; Load and Activate ROM 8x14 Character Set (EGA/VGA)
	; (BL = font block to load (EGA: 0-3; VGA: 0-7))
	call	load_text_8_14_pat
        jmp     VIDEO_RETURN
font_setup_error:
	sub	eax, eax ; 0 -> fonts could not be loaded
	jmp	_video_return
font_setup_3:
	; AX = 1102H ; Load ROM 8x8 Character Set (EGA/VGA)
	; case 0x02:
        ; case 0x12:
        ; biosfn_load_text_8_8_pat(GET_AL(),GET_BL());
        ; break;
	cmp	al, 2
	je	short font_setup_4
	cmp	al, 12h
	jne	short font_setup_5	
font_setup_4:
	; AX = 1112H ; Load and Activate ROM 8x8 Character Set (EGA/VGA)
	; (BL = font block to load (EGA: 0-3; VGA: 0-7))
	call	load_text_8_8_pat
        jmp     VIDEO_RETURN
font_setup_5:
	; AX = 1104H ; Load ROM 8x16 Character Set (EGA/VGA)
	; case 0x04:
        ; case 0x14:
        ; biosfn_load_text_8_16_pat(GET_AL(),GET_BL());
        ; break;
	cmp	al, 4
	je	short font_setup_6
	cmp	al, 14h
	jne	short font_setup_7	
font_setup_6:
	; AX = 1114H ; Load and Activate ROM 8x16 Character Set (EGA/VGA)
	; (BL = font block to load (EGA: 0-3; VGA: 0-7))
	call	load_text_8_16_pat
        jmp     VIDEO_RETURN
font_setup_7:
	; Note: AX=1120h (Setup INT 1Fh, EXT_PTR) is not needed
	; for TRDOS 386 (TRDIOS v2.0) video functionality;
	; because, originally EXT_PTR (font address) was used for
	; chars 80h to 0FFh (after the first 128 ASCII char fonts), for
	; CGA graphics mode; currenty, 'vgafont8' address has 256 chars!
	; 
	; case 0x20:
        ; biosfn_load_gfx_8_8_chars(ES,BP);
        ; break; 
	; case 0x21:
        ; biosfn_load_gfx_user_chars(ES,BP,CX,GET_BL(),GET_DL());
        ; break;
	; AX = 1121H ; Setup User-Defined Font for Graphics Mode (VGA)
	; BL   screen rows code: 00H = user-specified (in DL)
        ;                        01H = 14 rows
        ;                        02H = 25 rows
        ;                        03H = 43 rows
        ; CX   bytes per character definition
        ; DL   (when BL=0) custom number of character rows on screen
        ; EBP  address of font-definition information (user's mem space)

	cmp	al, 21h
	jne	short font_setup_9

	; TRDOS 386 modification !
	; dh = 0 -> 256 characters
	; dh = 80h -> 128 characters
	; (If DH <> 0 and DH <> 80h -> invalid)     
	and	dh, dh
	jz	short font_setup_8 ; 256 characters
	cmp	dh, 80h ; 128 characters
	jne	short font_setup_error ; invalid !
font_setup_8:
	call	transfer_user_fonts
	jc	short font_setup_error
	; ebp = user's font data address in system's memory space
	call	load_gfx_user_chars
        jmp     VIDEO_RETURN 
font_setup_9:
	; case 0x22:
        ; biosfn_load_gfx_8_14_chars(GET_BL());
        ; break;
	cmp	al, 22h
	jne	short font_setup_10
	call	load_gfx_8_14_chars	
        jmp     VIDEO_RETURN 
font_setup_10:	
	; case 0x23:
        ; biosfn_load_gfx_8_8_dd_chars(GET_BL());
        ; break;
	cmp	al, 23h
	jne	short font_setup_11
	call	load_gfx_8_8_chars	
        jmp     VIDEO_RETURN 
font_setup_11:	
	; case 0x24:
        ; biosfn_load_gfx_8_16_chars(GET_BL());
        ; break;
	cmp	al, 24h
	jne	short font_setup_12
	call	load_gfx_8_16_chars	
        jmp     VIDEO_RETURN 
font_setup_12:
 	; case 0x30:
        ; biosfn_get_font_info(GET_BH(),&ES,&BP,&CX,&DX);
        ; break;
	cmp	al, 30h
	jne	short font_setup_13			
	call	get_font_info
	; eax = return value (info: 4 bytes for 4 parms)
	; eax = 0 -> invalid function (input)
        jmp     _video_return
font_setup_13:
	cmp	al, 03h ; AX = 1103h	
	jne	short font_setup_14
	; biosfn_set_text_block_specifier:
	; BL = font block selector code	
	; NOTE: TRDOS 386 only uses and sets font block 0
	; (It is as BL = 0 for TRDOS 386)
	mov	dx, 3C4h ; VGAREG_SEQU_ADDRESS
	;mov	ah, bl
	sub	ah, ah ; 0
	;mov	al, 03h
	out	dx, ax
	jmp     VIDEO_RETURN 
	
font_setup_14:
	sub	eax, eax ; 0 = invalid function
        jmp     _video_return

transfer_user_fonts:
	; 09/07/2016
	;and	ecx, 0FFFFh
	; ECX = byte count
	;push	ecx
	mov	esi, ebp ; user buffer
	mov	edi, Cluster_Buffer  ; system buffer
	call	transfer_from_user_buffer
	;pop	ecx
	; ecx = transfer (byte) count = character count
	mov	ebp, Cluster_Buffer
	; jc	VIDEO_RETURN -> failed
	retn

load_text_user_pat:
	; 26/07/2016
	; 09/07/2016
	; load user defined (EGA/VGA) text fonts
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_load_text_user_pat'

	; biosfn_load_text_user_pat (AL,ES,BP,CX,DX,BL,BH)

	; get_font_access();
	; blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
	; for(i=0;i<CX;i++)
	; {
	;  src = BP + i * BH;
	;  dest = blockaddr + (DX + i) * 32;
	;  memcpyb(0xA000, dest, ES, src, BH);
	; }
	; release_font_access();
	; if(AL>=0x10)
	; {
	; set_scan_lines(BH);
	; }

	push	eax
	call	get_font_access
	sub	bl, bl ; i = 0	
ltup_1:
	mov	al, bl
	mul	bh
	movzx	esi, ax
	add	esi, ebp
	mov	al, bl
	sub	ah, ah
	add	ax, dx ; (DX + i)
	shl	ax, 5  ; * 32
	movzx	edi, ax
	add	edi, 0A0000h
	push	ecx
	movzx	ecx, bh 		
	rep	movsb
	pop	ecx	
	inc	bl
	cmp	bl, cl
	jne	short ltup_1	
	;
	call	release_font_access
	;
	pop	eax
	; if(AL>=0x10)
	cmp	al, 10h
	jb	short ltup_2
   	; set_scan_lines(BH);
	call	set_scan_lines
ltup_2:
	retn

get_font_access:
	; 09/07/2016
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'get_font_access'

	; get_font_access()
	push	edx
	mov	dx, 3C4h ; VGAREG_SEQU_ADDRESS
	mov	ax, 0100h
	out	dx, ax
	mov	ax, 0402h
	out	dx, ax
	mov	ax, 0704h
	out	dx, ax
	mov	ax, 0300h
	out	dx, ax
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	mov	ax, 0204h
	out	dx, ax
	mov	ax, 0005h
	out	dx, ax
	mov	ax, 0406h
	out	dx, ax
	pop	edx
	retn

release_font_access:
	; 29/07/2016
	; 09/07/2016
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'release_font_access'

	mov	dx, 3C4h ; VGAREG_SEQU_ADDRESS
	mov	ax, 0100h
	out	dx, ax
	mov	ax, 0302h
	out	dx, ax
	mov	ax, 0304h
	out	dx, ax
	mov	ax, 0300h
	out	dx, ax
	mov	dx, 3CCh ; VGAREG_READ_MISC_OUTPUT
 	in	al, dx
	and	al, 01h
	shl	al, 2
	or	al, 0Ah
	mov	ah, al
	mov	al, 06h
	mov	dx, 3CEh ; VGAREG_GRDC_ADDRESS
	out	dx, ax
	mov	ax, 0004h
	out	dx, ax
	mov	ax, 1005h
	out	dx, ax
	retn

set_scan_lines:
	; 09/07/2016
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'set_scan_lines'

	; set_scan_lines(lines)
	; BH = lines

	; outb(crtc_addr, 0x09);
	mov	dx, 3D4h ; CRTC_ADDRESS = 3D4h (always)
	mov	al, 09h
	out	dx, al
	; crtc_r9 = inb(crtc_addr+1);
	inc	dx ; 3D5h
	in	al, dx
	; crtc_r9 = (crtc_r9 & 0xe0) | (lines - 1);
	and	al, 0E0h
	dec	bh ; lines - 1
	or	al, bh
	; outb(crtc_addr+1, crtc_r9);
	out	dx, al
	;inc	bh 
	; if(lines==8)
	;cmp	bh, 8
	cmp	bh, 7
	jne	short ssl_1
	; biosfn_set_cursor_shape(0x06,0x07);
	mov	cx, 0607h
	jmp	short ssl_2
ssl_1:
	; biosfn_set_cursor_shape(lines-4,lines-3);
	mov	cl, bh ; lines - 1
	mov	ch, cl ; lines - 1 (16 -> 15)
	dec	ch  ; lines - 2 (16 -> 14)
ssl_2:
	; CH = start line, CL = stop line
	mov	ah, 10	; 6845 register for cursor set
	mov	[CURSOR_MODE], cx ; save in data area
	call	m16	; output cx register
	; write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT, lines);
	inc	bh ; lines
	mov	[CHAR_HEIGHT], bh
	;  outb(crtc_addr, 0x12);
	mov	dx, 3D4h ; CRTC_ADDRESS
	mov	al, 12h
	out	dx, al
	; vde = inb(crtc_addr+1);
	inc	dx
	in	al, dx
	mov	ah, al
	; outb(crtc_addr, 0x07);
	dec	dx
	mov	al, 07h
	out	dx, al
	; ovl = inb(crtc_addr+1);
	inc	dx
	in	al, dx
	; vde += (((ovl & 0x02) << 7) + ((ovl & 0x40) << 3) + 1);
	mov	dl, ah ; vde
	mov	dh, al ; ovl
	and	ax, 02h
	shl	ax, 7
	mov	cx, ax ; (ovl & 0x02) << 7)	
	mov	al, dh ; ovl
	and	ax, 40h			
	shl	ax, 3  ; (ovl & 0x40) << 3)
	inc	ax ; + 1 
	add	ax, cx
	xor	dh, dh
	add	ax, dx ; + vde
	; rows = vde / lines;
	div	bh
	;dec	al ; rows -1
	; write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, rows-1);
	mov	[VGA_ROWS], al ; rows (not 'rows-1' !)
	; write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE, rows * cols * 2);
	mov	ah, [CRT_COLS]
	mul	ah
	shl	ax, 1
	mov 	[CRT_LEN], ax	
	retn

load_text_8_14_pat:
	; 26/07/2016
	; 25/07/2016
	; 23/07/2016
	; 09/07/2016
	; load user defined (EGA/VGA) text fonts
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_load_text_8_14_pat'

	; biosfn_load_text_8_14_pat (AL,BL)

	; get_font_access();
	; blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
	; for(i=0;i<0x100;i++)
	; {
	;  src = i * 14;
	;  dest = blockaddr + i * 32;
	;  memcpyb(0xA000, dest, 0xC000, vgafont14+src, 14);
	; }
	; release_font_access();
	; if(AL>=0x10)
	; {
	; set_scan_lines(14);
	; }

	push	eax
	call	get_font_access

	; blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
	;mov	dl, bl
	;and	dl, 3
	;shl	dx, 14
	;xchg	dx, bx
	;and	dl, 4
	;shl	dx, 11
	;add	dx, bx 	
 
	;xor	dx, dx  ; blockaddr = 0
	; Always block 0 for TRDOS 386 ! (blockaddr=0(

	sub	bl, bl ; i = 0
	mov	bh, 14
	mov	esi, vgafont14
	mov	edi, 0A0000h		
lt8_14_1:
	;mov	al, bl
	;mul	bh
	;movzx	esi, ax
	;add	esi, vgafont14
	;mov	al, bl
	;sub	ah, ah
	;shl	ax, 5 ; * 32
	;;add	ax, dx ; blockaddr + i * 32;
	;movzx	edi, ax ; dest
	;add	edi, 0A0000h
	movzx	ecx, bh 		
	rep	movsb
	add	edi, 18 ; 32 - 14
	inc	bl
	jnz	short lt8_14_1	
	;
	call	release_font_access
	;
	pop	eax
	; if(AL>=0x10)
	cmp	al, 10h
	jb	short lt8_14_4
	; BH = 14
   	; set_scan_lines(14);
	call	set_scan_lines
lt8_14_4:
	retn

load_text_8_8_pat:
	; 26/07/2016
	; 25/07/2016
	; 23/07/2016
	; 09/07/2016
	; load user defined (EGA/VGA) text fonts
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_load_text_8_8_pat'

	; biosfn_load_text_8_8_pat (AL,BL)

	; get_font_access();
	; blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
	; for(i=0;i<0x100;i++)
	; {
	;  src = i * 8;
	;  dest = blockaddr + i * 32;
	;  memcpyb(0xA000, dest, 0xC000, vgafont8+src, 8);
	; }
	; release_font_access();
	; if(AL>=0x10)
	; {
	; set_scan_lines(8);
	; }

	push	eax
	call	get_font_access

	; blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
	;mov	dl, bl
	;and	dl, 3
	;shl	dx, 14
	;xchg	dx, bx
	;and	dl, 4
	;shl	dx, 11
	;add	dx, bx 	
 
	;xor	dx, dx  ; blockaddr = 0
	; Always block 0 for TRDOS 386 ! (blockaddr=0(

	sub	bl, bl ; i = 0
	mov	bh, 8
	mov	esi, vgafont8
	mov	edi, 0A0000h		
lt8_8_1:
	;mov	al, bl
	;mul	bh
	;movzx	esi, ax
	;add	esi, vgafont8
	;mov	al, bl
	;sub	ah, ah
	;shl	ax, 5 ; * 32
	;;add	ax, dx ; blockaddr + i * 32;
	;movzx	edi, ax ; dest
	;add	edi, 0A0000h
	movzx	ecx, bh 		
	rep	movsb
	add	edi, 24 ; 32 - 8
	inc	bl
	jnz	short lt8_8_1	
	;
	call	release_font_access
	;
	pop	eax
	; if(AL>=0x10)
	cmp	al, 10h
	jb	short lt8_8_2
	; BH = 8
   	; set_scan_lines(8);
	call	set_scan_lines
lt8_8_2:
	retn

load_text_8_16_pat:
	; 26/07/2016
	; 25/07/2016
	; 23/07/2016
	; 09/07/2016
	; load user defined (EGA/VGA) text fonts
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_load_text_8_16_pat'

	; biosfn_load_text_8_16_pat (AL,BL)

	; get_font_access();
	; blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
	; for(i=0;i<0x100;i++)
	; {
	;  src = i * 16;
	;  dest = blockaddr + i * 32;
	;  memcpyb(0xA000, dest, 0xC000, vgafont16+src, 16);
	; }
	; release_font_access();
	; if(AL>=0x10)
	; {
	; set_scan_lines(16);
	; }

	push	eax
	call	get_font_access

	; blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
	;mov	dl, bl
	;and	dl, 3
	;shl	dx, 14
	;xchg	dx, bx
	;and	dl, 4
	;shl	dx, 11
	;add	dx, bx 	
 
	;xor	dx, dx  ; blockaddr = 0
	; Always block 0 for TRDOS 386 ! (blockaddr=0(

	sub	bl, bl ; i = 0
	mov	bh, 16
	mov	esi, vgafont16
	mov	edi, 0A0000h
	movzx	eax, bh	
lt8_16_1:
	;mov	al, bl
	;mul	bh
	;movzx	esi, ax
	;add	esi, vgafont16
	;mov	al, bl ; i
	;sub	ah, ah
	;shl	ax, 5 ; * 32
	;;add	ax, dx ; blockaddr + i * 32;
	;movzx	edi, ax ; dest
	;add	edi, 0A0000h
	;movzx	ecx, bh
	mov	ecx, eax ; 16	
	rep	movsb
	add	edi, eax ; add edi, 16
	inc	bl
	jnz	short lt8_16_1
	;
	call	release_font_access
	;
	pop	eax
	; if(AL>=0x10)
	cmp	al, 10h
	jb	short lt8_16_2
	; BH = 16
   	; set_scan_lines(16);
	call	set_scan_lines
lt8_16_2:
	retn

load_gfx_user_chars:
	; 08/08/2016
	; 10/07/2016
	; Setup User-Defined Font for Graphics Mode (VGA)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_load_gfx_user_chars'

	; biosfn_load_gfx_user_chars (ES,BP,CX,BL,DL)
	; /* set 0x43 INT pointer */
    	; write_word(0x0, 0x43*4, BP);
    	; write_word(0x0, 0x43*4+2, ES);
	xor	eax, eax
	dec	eax ; 0FFFFFFFFh (user defined fonts)
	mov	[VGA_INT43H], eax
		
	; BL   screen rows code: 00H = user-specified (in DL)
        ;                        01H = 14 rows
        ;                        02H = 25 rows
        ;                        03H = 43 rows
        ; CX   bytes per character definition
        ; DL   (when BL=0) custom number of character rows on screen
	; dh = 0 -> 256 characters
	; dh = 80h -> 128 characters
	; (If DH <> 0 and DH <> 80h -> invalid)     	 
        ; EBP  address of font-definition information (user's mem space)

	; switch (BL) {
	; case 0:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, DL-1);
	;    break;
	and	bl, bl
	jnz	short l_gfx_uc_1
	mov	[VGA_ROWS], dl  ; not DL-1 !
	jmp	short l_gfx_uc_4 	
l_gfx_uc_1:
	; case 1:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, 13);
	;    break;
	dec	bl
	jnz	short l_gfx_uc_2
	; bl = 1
	mov	byte [VGA_ROWS], 14  ; not 13 !
	jmp	short l_gfx_uc_4 	
l_gfx_uc_2:
	dec	bl
	jz	short l_gfx_uc_3 ; bl = 2
	dec	bl
	jnz	short l_gfx_uc_4 ; bl > 3
	; bl = 3
	; case 3:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, 42);
	;    break;
	mov	byte [VGA_ROWS], 43  ; not 42 !
l_gfx_uc_3:
    	; case 2:
    	; default:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, 24);
	;    break;
	; bl = 2 or bl > 3
	mov	byte [VGA_ROWS], 25  ; not 24 !	
    	; }
l_gfx_uc_4:
    	; write_byte(BIOSMEM_SEG, BIOSMEM_CHAR_HEIGHT, CX);
	mov	[CHAR_HEIGHT], cl
	; }
	retn	

load_gfx_8_14_chars:
	; 08/08/2016
	; 10/07/2016
	; Setup ROM 8x14 Font for Graphics Mode (VGA)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_load_gfx_8_14_chars'

	; biosfn_load_gfx_8_14_chars (BL)
	; /* set 0x43 INT pointer */
    	; write_word(0x0, 0x43*4, &vgafont14);
    	; write_word(0x0, 0x43*4+2, 0xC000);
	mov	dword [VGA_INT43H], vgafont14
		
	; BL    screen rows code: 00H = user-specified (in DL)
        ;                         01H = 14 rows
        ;                         02H = 25 rows
        ;                         03H = 43 rows
        ; DL    (when BL=0) custom number of char rows on screen

	; switch (BL) {
	; case 0:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, DL-1);
	;    break;
	and	bl, bl
	jnz	short l_gfx_8_14c_1
	mov	[VGA_ROWS], dl  ; not DL-1 !
	jmp	short l_gfx_8_14c_4 	
l_gfx_8_14c_1:
	; case 1:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, 13);
	;    break;
	dec	bl
	jnz	short l_gfx_8_14c_2
	; bl = 1
	mov	byte [VGA_ROWS], 14  ; not 13 !
	jmp	short l_gfx_8_14c_4 	
l_gfx_8_14c_2:
	dec	bl
	jz	short l_gfx_8_14c_3 ; bl = 2
	dec	bl
	jnz	short l_gfx_8_14c_4 ; bl > 3
	; bl = 3
	; case 3:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, 42);
	;    break;
	mov	byte [VGA_ROWS], 43  ; not 42 !
l_gfx_8_14c_3:
    	; case 2:
    	; default:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, 24);
	;    break;
	; bl = 2 or bl > 3
	mov	byte [VGA_ROWS], 25  ; not 24 !	
    	; }
l_gfx_8_14c_4:
    	; write_byte(BIOSMEM_SEG, BIOSMEM_CHAR_HEIGHT, 14);
        mov     byte [CHAR_HEIGHT], 14
	; }
	retn	

load_gfx_8_8_chars:
	; 08/08/2016
	; 10/07/2016
	; Setup ROM 8x14 Font for Graphics Mode (VGA)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_load_gfx_8_8_dd_chars'

	; biosfn_load_gfx_8_8_dd_chars (BL)
	; /* set 0x43 INT pointer */
    	; write_word(0x0, 0x43*4, &vgafont8);
    	; write_word(0x0, 0x43*4+2, 0xC000);
	mov	dword [VGA_INT43H], vgafont8
		
	; BL    screen rows code: 00H = user-specified (in DL)
        ;                         01H = 14 rows
        ;                         02H = 25 rows
        ;                         03H = 43 rows
        ; DL    (when BL=0) custom number of char rows on screen

	; switch (BL) {
	; case 0:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, DL-1);
	;    break;
	and	bl, bl
	jnz	short l_gfx_8_8c_1
	mov	[VGA_ROWS], dl  ; not DL-1 !
	jmp	short l_gfx_8_8c_4 	
l_gfx_8_8c_1:
	; case 1:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, 13);
	;    break;
	dec	bl
	jnz	short l_gfx_8_8c_2
	; bl = 1
	mov	byte [VGA_ROWS], 14  ; not 13 !
	jmp	short l_gfx_8_8c_4 	
l_gfx_8_8c_2:
	dec	bl
	jz	short l_gfx_8_8c_3 ; bl = 2
	dec	bl
	jnz	short l_gfx_8_8c_4 ; bl > 3
	; bl = 3
	; case 3:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, 42);
	;    break;
	mov	byte [VGA_ROWS], 43  ; not 42 !
l_gfx_8_8c_3:
    	; case 2:
    	; default:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, 24);
	;    break;
	; bl = 2 or bl > 3
	mov	byte [VGA_ROWS], 25  ; not 24 !	
    	; }
l_gfx_8_8c_4:
    	; write_byte(BIOSMEM_SEG, BIOSMEM_CHAR_HEIGHT, 8);
        mov     byte [CHAR_HEIGHT], 8
	; }
	retn

load_gfx_8_16_chars:
	; 08/08/2016
	; 10/07/2016
	; Setup ROM 8x14 Font for Graphics Mode (VGA)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_load_gfx_8_16_chars'

	; biosfn_load_gfx_8_16_chars (BL)
	; /* set 0x43 INT pointer */
    	; write_word(0x0, 0x43*4, &vgafont16);
    	; write_word(0x0, 0x43*4+2, 0xC000);
	mov	dword [VGA_INT43H], vgafont16
		
	; BL    screen rows code: 00H = user-specified (in DL)
        ;                         01H = 14 rows
        ;                         02H = 25 rows
        ;                         03H = 43 rows
        ; DL    (when BL=0) custom number of char rows on screen

	; switch (BL) {
	; case 0:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, DL-1);
	;    break;
	and	bl, bl
	jnz	short l_gfx_8_16c_1
	mov	[VGA_ROWS], dl  ; not DL-1 !
	jmp	short l_gfx_8_16c_4 	
l_gfx_8_16c_1:
	; case 1:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, 13);
	;    break;
	dec	bl
	jnz	short l_gfx_8_16c_2
	; bl = 1
	mov	byte [VGA_ROWS], 14  ; not 13 !
	jmp	short l_gfx_8_16c_4 	
l_gfx_8_16c_2:
	dec	bl
	jz	short l_gfx_8_16c_3 ; bl = 2
	dec	bl
	jnz	short l_gfx_8_16c_4 ; bl > 3
	; bl = 3
	; case 3:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, 42);
	;    break;
	mov	byte [VGA_ROWS], 43  ; not 42 !
l_gfx_8_16c_3:
    	; case 2:
    	; default:
	;    write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, 24);
	;    break;
	; bl = 2 or bl > 3
	mov	byte [VGA_ROWS], 25  ; not 24 !	
    	; }
l_gfx_8_16c_4:
    	; write_byte(BIOSMEM_SEG, BIOSMEM_CHAR_HEIGHT, 16);
        mov     byte [CHAR_HEIGHT], 16
	; }
	retn
			
get_font_info:
	; 19/09/2016
	; 08/08/2016
	; 10/07/2016
	; Get Current Character Generator Info (VGA)
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'biosfn_get_font_info'

	; Modified for TRDOS 386 !
	;
	; INPUT ->
	;    AX = 1130h
	;    BL = 0 -> Get info for current VGA font
	;	       (BH = unused)
	;    19/09/2016
	;    BL > 0 -> Get requested character font data
	;	 BL = 1 -> vgafont8
	;        BL = 2 -> vgafont14
	;	 BL = 3 -> vgafont16   
	;	 BL > 3 -> Invalid function (for now!)
	;	 BH = ASCII code of the first character
	;	 ECX = Number of characters from the 1st char
	;	 ECX >= 256 -> All (256-BH) characters
	;	 ECX = 0 -> All characters (BH = unused)	
	;        EDX = User's Buffer Address	
	; OUTPUT ->
	;    AL = height (scanlines), bytes per character
	;    AH = screen rows
	;    Byte 16-23 of EAX = number of columns
	;    Byte 24-31 of EAX = 
	;	  0 -> default font (not configured yet)
	;	  0FFh -> user defined font
	;	  14 = vgafont14
	;	   8 = vgafont8
	;	  16 = vgafont16
	;   If BL input > 0 -> 
	;       EAX = Actual transfer count
	;
	and	bl, bl
	jz	short gfi_0
	; invalid function (input)
	cmp	bl, 3
	jna	short gfi_4
	xor	eax, eax ; 0
	retn
gfi_0:
	mov	al, [CHAR_HEIGHT]
	mov	ah, [VGA_ROWS]
	shl	eax, 16
	mov	al, [CRT_COLS]
	mov	ecx, [VGA_INT43H]
	and	ecx, ecx
	jz	short gfi_2 ; 0 = default font
	inc	ecx ; 0FFFFFFFFh -> 0 (user defined font)
	jnz	short gfi_1
	dec	ah ; 0FFh
	jmp	short gfi_2
gfi_1:
	dec	ecx ; 08/08/2016
	mov	ah, 14
	cmp	ecx, vgafont14
	je	short gfi_2
	mov	ah, 8
	cmp	ecx, vgafont8
	je	short gfi_2
	; vgafont16
	shl	ah, 1 ; ah = 16
gfi_2:
	rol	eax, 16
gfi_3:
	retn
gfi_4:
	mov	edi, edx ; **
	cmp	bl, 2
	jb	short gfi_5
	ja	short gfi_7
	;BL = 2 -> vgafont14
	mov	esi, vgafont14 ; *
	mov	bl, 14
	jmp	short gfi_6 
gfi_5:
	;BL = 1 -> vgafont8
	mov	esi, vgafont8 ; *
	mov	bl, 8
gfi_6:
	or	ecx, ecx
	jz	short gfi_8 ; all chars from the 00h
	mov	al, bh ; character index
	mul	bl ; char index * char height/size
	movzx	edx, ax
	add	esi, edx ; *
	mov	dx, 255
	sub	dl, bh
	inc	dx	
	cmp	ecx, edx
	ja	short gfi_8
	je	short gfi_9
	mov	ecx, edx
	jmp	short gfi_9
gfi_7:
	;BL = 3 -> vgafont16
	mov	esi, vgafont16 ; *
	mov	bl, 16
	jmp	short gfi_6
gfi_8:
	mov	ecx, 256
gfi_9:
	mov	ax, cx ; character count
	xor	bh, bh
	mul	bx ; char count * char height/size	
 	mov	cx, ax

	; ESI = source address in system space
	; EDI = user's buffer address
	; ECX = transfer (byte) count
	call	transfer_to_user_buffer
	mov	eax, ecx ; actual transfer count
	retn
	
vga_pal_funcs:
	; 10/08/2016
	; VGA Palette functions
	;
	; derived from 'Plex86/Bochs VGABios' source code
	; vgabios-0.7a (2011)
	; by the LGPL VGABios developers Team (2001-2008)
	; 'vgabios.c', 'vgarom.asm'

	cmp	al, 0
        je      set_single_palette_reg
vga_palf_1001:
	cmp	al, 1
        je      set_overscan_border_color
vga_palf_1002:
	cmp	al, 2
        je      set_all_palette_reg
vga_palf_1003:
	cmp	al, 3
        je      toggle_intensity
vga_palf_1007:
	cmp	al, 7
        je      get_single_palette_reg
	jb	short vga_palf_unknown
vga_palf_1008:
	cmp	al, 8
        je      read_overscan_border_color
vga_palf_1009:
	cmp	al, 9
	je	get_all_palette_reg
vga_palf_1010:
	cmp	al, 10h
	je 	set_single_dac_reg
	jb	short vga_palf_unknown
vga_palf_1012:
	cmp	al, 12h
        je      set_all_dac_reg
	jb	short vga_palf_unknown
vga_palf_1013:
	cmp	al, 13h
        je      select_video_dac_color_page
vga_palf_1015:
	cmp	al, 15h
	je	read_single_dac_reg
	jb	short vga_palf_unknown
vga_palf_1017:
	cmp	al, 17h
        je      read_all_dac_reg
	jb	short vga_palf_unknown
vga_palf_1018:
	cmp	al, 18h
        je      set_pel_mask
vga_palf_1019:
	cmp	al, 19h
        je      read_pel_mask
vga_palf_101A:
	cmp	al, 1Ah
        je      read_video_dac_state
vga_palf_101B:
	cmp	al, 1Bh
	;jne	short vga_palf_unknown
	ja	short vga_palf_unknown
 
	call	gray_scale_summing
        jmp     VIDEO_RETURN 	

vga_palf_unknown:
	sub	eax, eax ; 0 = invalid function
        jmp     _video_return

set_single_palette_reg:
	; 10/08/2016
	; Set One Palette Register
	; BL = register number to set
	;     (a 4-bit attribute nibble: 00h-0Fh)
	; BH = 6-bit RGB color to display 
	;      for that attribute

	cmp	bl, 14h
	;ja	short no_actl_reg1
	ja	VIDEO_RETURN
	push	ax
	push	dx
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	mov	al, bl
	out	dx, al
	mov	al, bh
	out	dx, al
	mov	al, 20h
	out	dx, al
	; ifdef VBOX
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	; endif ; VBOX
	pop	dx
	pop	ax
;no_actl_reg1:
	jmp	VIDEO_RETURN

set_overscan_border_color:
	; 10/08/2016
	; Set Overscan/Border Color Register
	; BH = 6-bit RGB color to display 
	;      for that attribute
	
	mov	bl, 11h
  	jmp	short set_single_palette_reg

set_all_palette_reg:
	; 10/08/2016
	; Set All Palette Registers and Overscan
	; EDX = Address of 17 bytes; 
	; an rgbRGB value for each of 16 palette
	; registers plus one for the border.

	mov	esi, edx ; user buffer
	mov	ecx, 17
	mov	edi, esp
	sub	esp, 20	 	 
	call	transfer_from_user_buffer
	;jc	VIDEO_RETURN

	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	mov	cl, 0
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
set_palette_loop:
	mov	al, cl
	out	dx, al
	mov	al, [edi]
	out	dx, al
	inc	edi
	inc	cl
	cmp	cl, 10h
	jne	short set_palette_loop
	mov	al, 11h
	out	dx, al
	mov	al, [edi]
	out	dx, al
	mov	al, 20h
	out	dx, al
	; ifdef VBOX
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	; endif ; VBOX
	add	esp, 20
	jmp	VIDEO_RETURN

toggle_intensity:
	; 10/08/2016
	; Select Foreground Blink or Bold Background
	; BL = 00h = enable bold backgrounds
	;	    (16 background colors)
        ;      01h = enable blinking foreground
	;	    (8 background colors)

	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	mov	al, 10h
	out	dx, al
	mov	dx, 3C1h ; VGAREG_ACTL_READ_DATA
	in	al, dx
	and	al, 0F7h
	and	bl, 01h
	shl	bl, 3
	or	al, bl
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	out	dx, al
	mov	al, 20h
	out	dx, al
	; ifdef VBOX
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	; endif ; VBOX
	jmp	VIDEO_RETURN

get_single_palette_reg:
	; 10/08/2016
	; Read One Palette Register
        ; INPUT:
	; BL = Palette register to read (00h-0Fh)
	; OUTPUT:
	; BH = Current rgbRGB value of specified register
	;      for that attribute

	cmp	bl, 14h
	;ja	short no_actl_reg2
	ja	VIDEO_RETURN

	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	mov	al, bl
	out	dx, al
	mov	dx, 3C1h ; VGAREG_ACTL_READ_DATA
	in	al, dx
	mov	[esp+13], al ; bh
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	mov	al, 20h
	out	dx, al
	; ifdef VBOX
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	; endif ; VBOX
	jmp	VIDEO_RETURN

read_overscan_border_color:
	; 10/08/2016
	; Read Overscan Register
	; OUTPUT:
	; BH = current rgbRGB value 
	;      of the overscan/border register

	mov	bl, 11h
	jmp	short get_single_palette_reg

get_all_palette_reg:
	; 10/08/2016
	; Read All Palette Registers
	; EDX = Address of 17-byte buffer 
	;	to receive data
	
	mov	edi, edx
	mov	ebx, esp
	mov	esi, ebx
	sub	esp, 20	 

	mov	cl, 0
get_palette_loop:
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	mov	al, cl
	out	dx, al
	mov	dx, 3C1h ; VGAREG_ACTL_READ_DATA
	in	al, dx
	mov	[ebx], al
	inc	ebx
	inc	cl
	cmp	cl, 10h
	jne	short get_palette_loop
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	mov	al, 11h
	out	dx, al
	mov	dx, 3C1h ; VGAREG_ACTL_READ_DATA
	in	al, dx
	mov	[ebx], al
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	mov	al, 20h
	out	dx, al
	; ifdef VBOX
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	; endif ; VBOX

	mov	ecx, 17 ; transfer (byte) count
	; ESI = source address in system space
	; EDI = user's buffer address
	call	transfer_to_user_buffer

	add	esp, 20
	jmp	VIDEO_RETURN

set_single_dac_reg:
	; 10/08/2016
	; Set One DAC Color Register
	; BX = color register to set (0-255)
        ; CH = green value (00h-3Fh)
        ; CL = blue value  (00h-3Fh)
        ; DH = red value   (00h-3Fh)

	push	dx
	mov	dx, 3C8h ; VGAREG_DAC_WRITE_ADDRESS
	mov	al, bl
	out	dx, al
	;mov	dx, 3C9h ; VGAREG_DAC_DATA
	inc	dx
	pop	ax
	mov	al, ah
	out	dx, al
	mov	al, ch
	out	dx, al
	mov	al, cl
	out	dx, al
	jmp	VIDEO_RETURN

set_all_dac_reg:
	; 12/08/2016
	; 11/08/2016
	; 10/08/2016
	; Set a Block of DAC Color Register
	; BX = first DAC register to set (0-00FFh)
	; ECX = number of registers to set (0-00FFh)
	; EDX = addr of a table of R,G,B values 
	;	(it will be CX*3 bytes long)

	mov	esi, edx ; user buffer
	mov	edx, ecx
	shl	cx, 1 ; *2
	add	ecx, edx ; ecx = 3*ecx
	mov	ebp, esp
	mov	edi, ebp
	sub	edi, ecx
	and	di, 0FFFCh ; (dword alignment)
	mov	esp, edi
	call	transfer_from_user_buffer
	;jc	VIDEO_RETURN

	mov	ecx, edx
	mov	dx, 3C8h ; VGAREG_DAC_WRITE_ADDRESS
	mov	al, bl
	out	dx, al
	mov	dx, 3C9h ; VGAREG_DAC_DATA
set_dac_loop:
	mov	al, [edi]
	out	dx, al
	inc	edi
	mov	al, [edi]
	out	dx, al
	inc	edi
	mov	al, [edi]
	out	dx, al
	inc	edi
	dec	cx
	jnz	short set_dac_loop
	mov	esp, ebp
	jmp	VIDEO_RETURN

select_video_dac_color_page:
	; 10/08/2016
	; DAC Color Paging Functions
	; BL = 00H = select color paging mode
        ;       BH = paging mode
        ;            00h = 4 blocks of 64 registers
        ;            01h = 16 blocks of 16 registers
	; BL = 01H = activate color page
        ;       BH = DAC color page number
        ;            00h-03h (4-page/64-reg mode)
        ;            00h-0Fh (16-page/16-reg mode)

	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	mov	al, 10h
	out	dx, al
	mov	dx, 3C1h ; VGAREG_ACTL_READ_DATA
	in	al, dx
	and	bl, 01h
	jnz	short set_dac_page
	and	al, 07Fh
	shl	bh, 7
	or	al, bh
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	out	dx, al
	jmp	short set_actl_normal
set_dac_page:
	push	ax
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	mov	al, 14h
	out	dx, al
	pop	ax
	and	al, 80h
	jnz	short set_dac_16_page
	shl	bh, 2
set_dac_16_page:
	and	bh, 0Fh
	mov	al, bh
	out	dx, al
set_actl_normal:
	mov	al, 20h
	out	dx, al
	; ifdef VBOX
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	; endif ; VBOX
	jmp	VIDEO_RETURN	

read_single_dac_reg:
	; 10/08/2016
	; Read One DAC Color Register
	; INPUT:
	; BX = color register to read (0-255)
	; OUTPUT:
	; CH = green value (00h-3Fh)
        ; CL = blue value  (00h-3Fh)
        ; DH = red value   (00h-3Fh)

	mov	dx, 3C7h ; VGAREG_DAC_READ_ADDRESS
	mov	al, bl
	out	dx, al
	mov	dx, 3C9h ; VGAREG_DAC_DATA
	in	al, dx
	mov	[esp+21], al ; dh
	in	al, dx
	mov	ch, al
	in	al, dx
	mov	cl, al
	mov	[esp+16], cx ; cx
	jmp	VIDEO_RETURN	

read_all_dac_reg:
	; 12/08/2016
	; 11/08/2016
	; 10/08/2016
	; Read a Block of DAC Color Registers
        ; BX = first DAC register to read (0-00FFh)
        ; ECX = number of registers to read (0-00FFh)
        ; EDX = addr of a buffer to hold R,G,B values
	;	(CX*3 bytes long)

	mov	edi, edx ; user buffer
	mov	edx, ecx
	shl	dx, 1 ; *2
	add	edx, ecx ; edx = 3*ecx
	mov	ebp, esp
	mov	esi, ebp
	sub	esi, edx
	and	si, 0FFFCh ; (dword alignment)
	mov	esp, esi
	push	edx ; 3*ecx
	mov	dx, 3C7h ; VGAREG_DAC_READ_ADDRESS
	mov	al, bl
	out	dx, al
	mov	dx, 3C9h ; VGAREG_DAC_DATA
	mov	ebx, esi
read_dac_loop:
	in	al, dx
	mov	[ebx], al
	inc	ebx
	in	al, dx
	mov	[ebx], al
	inc	ebx
	in	al, dx
	mov	[ebx], al
	inc	ebx
	dec	cx
	jnz	short read_dac_loop
	pop	ecx ; 3*ecx
	; ECX = transfer (byte) count
	; ESI = source address in system space
	; EDI = user's buffer address
	call	transfer_to_user_buffer
	mov	esp, ebp
	jmp	VIDEO_RETURN

set_pel_mask:
	; 10/08/2016
	; BL = mask value
	mov	dx, 3C6h ; VGAREG_PEL_MASK
	mov	al, bl
	out	dx, al
	jmp	VIDEO_RETURN

read_pel_mask:
	; 10/08/2016
	; Output: BL = mask value 
	mov	dx, 3C6h ; VGAREG_PEL_MASK
	in	al, dx
	mov	[esp+12], al ; bl
	jmp	VIDEO_RETURN

read_video_dac_state:
	; 10/08/2016
	; Query DAC Color Paging State
	; Output:
	; BH = current active DAC color page
        ; BL = current active DAC paging mode

	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	mov	al, 10h
	out	dx, al
	mov	dx, 3C1h ; VGAREG_ACTL_READ_DATA
	in	al, dx
	mov	bl, al
	shr	bl, 7
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	mov	al, 14h
	out	dx, al
	mov	dx, 3C1h ; VGAREG_ACTL_READ_DATA
	in	al, dx
	mov	bh, al
	and	bh, 0Fh
	test	bl, 01
	jnz	short get_dac_16_page
	shr	bh, 2
get_dac_16_page:
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	mov	dx, 3C0h ; VGAREG_ACTL_ADDRESS
	mov	al, 20h
	out	dx, al
	; ifdef VBOX
	mov	dx, 3DAh ; VGAREG_ACTL_RESET
	in	al, dx
	; endif ; VBOX 
	mov	[esp+12], bx ; bx
	jmp	VIDEO_RETURN

; % include 'vidata.s' ; VIDEO DATA

; /// End Of VIDEO FUNCTIONS ///