TITLE	scrn3.asm

;	AUTHOR	Tim Spencer - Compuserve [73657,1400]
;	DATE	March 17, 1987

_TEXT 	SEGMENT BYTE PUBLIC 'CODE'
_TEXT	ENDS

_DATA 	SEGMENT WORD PUBLIC 'DATA'
SCRN	STRUC		; screen data structure - defined in video.h
off	dw	0	; offset (cursor position) 
seg	dw	0	; screen buffer address
port	dw	0	; status port address
attrib	dw	0	; attribute to use 
cgacrd	dw	0	; enable retrace checking if not zero
SCRN 	ENDS

_DATA	ENDS

DGROUP	GROUP _DATA
	ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP, ES:NOTHING


_TEXT	SEGMENT BYTE PUBLIC 'CODE'

;-----------------------------------------------------------------------;
; scrn_restore - MSC callable function to restore a rectangular area	;
; 		 of the screen buffer.	Checks for vertical retrace	;
;		 only if the external structure member cga_card is	;
;		 non-zero. &scrn is the address of that structure.	;
;		 (see video.h).						;
;									;									;
; Note:	This procedure uses stosb in retrace checking mode (instead of	;
;       movsb) because it stuffs the char/attrib into the screen buffer	;
;       slightly faster.						;
;									;
; Usage:   scrn_restore(left, right, top, bottom, data_buff, &scrn)	;
;									;
;-----------------------------------------------------------------------;
_DATA SEGMENT
restore_args STRUC		; structure for easy argument reference
	dw	0		; saved BP value
	dw	0		; return address
rleft	dw	0		; rectangular boundries...
rright	dw	0
rtop	dw	0
rbottom	dw	0
mdata	dw	0		; address of data buffer to write to screen
mstruct	dw	0		; pointer to SCRN structure(defined in video.h)
restore_args ENDS

cga	db	0		; variable to hold cga_card value
_DATA ENDS

	PUBLIC	_scrn_restore

_scrn_restore	PROC	NEAR
	push	bp			; set up frame pointer
	mov	bp,sp		
	push	si			
	push	di
	mov	bx,[bp].mstruct		; get pointer to SCRN structure
	les	di,dword ptr[bx].off	; get scrn seg in es, off in di
	mov	dx,[bx].port		; get status port address
	mov	ax,[bx].cgacrd 		; hold cga status in variable cga
	mov	cga,al
	mov	si,[bp].mdata		; make si point to data buffer
	mov	bh,byte ptr[bp].rtop	; top will be incremented until it
	mov	bl,byte ptr[bp].rbottom	;  is greater than bottom, then exit.
	xor	cx,cx			; set initial logical cursor position
	mov	cl,bh			;  by getting top into cx,
	mov	al,80			;  multiplying by 80,
	mul	cl
	mov	cx,ax
	add	cx,[bp].rleft		;  adding left.
	shl	cx,1			;  and multiplying by 2
	mov	di,cx			;  put result into di 
	mov	cx,[bp].rright		; get the length of one line into
	sub	cx,[bp].rleft		;  cx by subtracting left from right
	add	cx,1			;  adding 1
	push	cx			;  save it
	mov	ax,79			; calculate offset from end of line to
	sub	ax,[bp].rright		;  the start of the next line
	add	ax,[bp].rleft
	shl	ax,1
	push	ax			;  and save it
write_line:
	cmp	cga,0			; cga card in use?
	jnz	rwait1			; yes, go wait
	rep	movsw			; no, warp speed.
	jmp	short rcheck_pos	; go check position	 
rwait1:
        in      al, dx                  ; wait for end of retrace
        shr     al, 1                   ; test horizontal trace bit
        jc      rwait1			; loop if still tracing
        cli                             ; disable writus interuptus
rwait2:
        in      al, dx                  ; now wait until the very moment
        shr     al, 1                   ; when the next retrace begins
        jnc     rwait2			; still waiting...
        mov     al,[si]			; load the char into al for stosb
        stosb				; write it and update pointer
        sti                             ; enable interrupts again
	inc	si			; point si at attribute
rwait3:
        in      al, dx                  ; repeat these steps for the attribute
        shr     al, 1
        jc      rwait3
        cli                             
rwait4:
        in      al, dx                  
        shr     al, 1                   
	jnc     rwait4
        mov     al,[si]			; load the attribute
        stosb
        sti  
	inc	si			; point si at next char                           
	loop	rwait1
rcheck_pos:
	pop	ax			; restore offset to next line start
	pop	cx			; restore count
	inc	bh			; is top greater than bottom yet?
	cmp	bh,bl
	ja	rexit			; yes.
	push	cx			; no, save count again
	push	ax			; save line start offset again
	add	di,ax			; move di to start of next line
	jmp	short write_line	; write another line
rexit:
	pop	di
	pop	si
	pop	bp
	ret
_scrn_restore	ENDP



;-----------------------------------------------------------------------;
; scrn_save - MSC callable function to save a rectangular area of the	;
;	      screen to a user defined buffer. 				;
;									;
; Usage:	scrn_save(left, right, top, bottom, data_buff, &scrn)	;
;-----------------------------------------------------------------------;
_DATA	SEGMENT
save_args STRUC			; structure for easy argument reference	
	dw	0		; saved bp value
	dw	0		; return address
sleft	dw	0		; rectangular boundries
sright	dw	0		
stop	dw	0
sbottom	dw	0
sbuff	dw	0		; user defined buffer to hold screen contents
sstruct	dw	0		; pointer to SCRN structure (see video.h)
save_args ENDS		
_DATA	ENDS

scga	db	0		; store cga true/false value here - must be
				; declared outside data segment because es and
				; ds are swapped in this function.

	PUBLIC	_scrn_save

_scrn_save	PROC	NEAR
	push	bp			; set up frame pointer
	mov	bp,sp	
	push	si			
	push	di
	push	ds
	mov	bx,[bp].mstruct		; get pointer to SCRN structure
	mov	dx,[bx].port		; get status port address
	mov	ax,[bx].cgacrd 		; hold cga status in variable scga
	mov	scga,al
	mov	ax,ds
	mov	es,ax			; get data segment into es 
	mov	di,[bp].sbuff		;  and offset of user buffer in di	
	mov	ax,[bx].seg		; get the screen segment and
	mov	ds,ax			;  put in ds 	
	mov	bh,byte ptr[bp].stop	; top will be incremented until it
	mov	bl,byte ptr[bp].sbottom	;  is greater than bottom, then exit.
	xor	cx,cx			; set initial logical cursor position
	mov	cl,bh			;  by getting top into cx,
	mov	al,80			;  multiplying by 80,
	mul	cl
	mov	cx,ax
	add	cx,[bp].sleft		;  adding left.
	shl	cx,1			;  and multiplying by 2
	mov	si,cx			;  put result into si 
	mov	cx,[bp].sright		; get the length of one line into
	sub	cx,[bp].sleft		;  cx by subtracting left from right
	add	cx,1			;  adding 1
	push	cx			;  save it
	mov	ax,79			; calculate offset from end of line to
	sub	ax,[bp].sright		;  the start of the next line
	add	ax,[bp].sleft
	shl	ax,1
	push	ax			;  and save it
read_line:
	cmp	cga,0			; cga card in use?
	jnz	swait1			; yes, go wait
	rep	movsw			; no, warp speed.
	jmp	short scheck_pos	; go check position	 
swait1:
        in      al, dx                  ; wait for end of retrace
        shr     al, 1                   ; test horizontal trace bit
        jc      swait1			; loop if still tracing
        cli                             ; disable writus interuptus
swait2:
        in      al, dx                  ; now wait until the very moment
        shr     al, 1                   ; when the next retrace begins
        jnc     swait2			; still waiting...
        mov     al,[si]			; load the char into al for stosb
        stosb				; write it and update pointer
        sti                             ; enable interrupts again
	inc	si			; point si at attribute
swait3:
        in      al, dx                  ; repeat these steps for the attribute
        shr     al, 1
        jc      swait3
        cli                             
swait4:
        in      al, dx                  
        shr     al, 1                   
	jnc     swait4
        mov     al,[si]			; load the attribute
        stosb
        sti  
	inc	si			; point si at next char                           
	loop	swait1
scheck_pos:
	pop	ax			; restore offset to next line start
	pop	cx			; restore count
	inc	bh			; is top greater than bottom yet?
	cmp	bh,bl
	ja	sexit			; yes.
	push	cx			; no, save count again
	push	ax			; save line start offset again
	add	si,ax			; move di to start of next line
	jmp	short read_line		; write another line
sexit:
	pop	ds
	pop	di
	pop	si
	pop	bp
	ret
_scrn_save	ENDP


_TEXT	ENDS

	END