;************************************************************************
;
; __Int10Pgm  BIOS Video interrupt service routine
;
; Description This procedure is a BIOS Video (interrupt $10) handler.
;	      Its purpose is to capture video calls and force TTY write
;	      calls within a window specified by the external variables
;	      WinUpper_ (upper left corner) and WinLower_ (lower right
;	      corner).	The result is that programs that use standard
;	      input and output will have their I/O forced within the
;	      window.
;
;	      __Int10Pgm is installed by __WinExPgm and then removed
;	      when the child process terminates.  It must not be called
;	      directly, and should not be installed by any other
;	      procedure except __WinExPgm.
;
;	      Note that programs that write directly to the video adapter
;	      are not controlled by __Int10Pgm.  Moreover, programs that
;	      use BIOS video services other than GetCur, SetCur, Scroll
;	      and WriteTTY may not function as expected.  Turbo Pascal
;	      programs that use the CRT unit write directly to the
;	      adapter, and even if the variable DirectVideo is set to
;	      FALSE, BIOS services that cannot be trapped are used.
;	      Turbo Programs that you want to run as a child within a
;	      window, but need some CRT functions, should reopen Input
;	      and Output and standard input and output respectively.
;
; Version     4.00 (C)Copyright Blaise Computing Inc.  1987
;________________________________________________________________________

; Row and col are used to access row and column within the global
; variables WinUpper_ and WinLower_.  Row is the high order byte and
; column is the lower order byte.  Row and column numbers begin at 0 to
; conform to BIOS conventions (Turbo Pascal begins at 1).

row	   equ	   (byte ptr 1)        ; Used to access row and column
col	   equ	   (byte ptr 0)

; The previous BIOS Video vector is stored in the global variable
; Prev10Vector_ (it is initialized by __WinExPgm).  This macro invokes
; the handler as a far procedure so it is necessary to push the flags
; (the ISR pops them).

callvideo  macro		      ;; Call previous VIDEO handler
	   pushf
	   call dword ptr Prev10Vector_
	   endm

popff	   macro		      ;; Simulate POPF instruction
	   local   do_call, do_iret
	   jmp	   short do_call
do_iret:
	   iret 		      ;; Pop IP, CS, flags.
do_call:
	   push   cs		      ;; Push CS
	   call   do_iret	      ;; Push IP & jump.
	   endm

; Global (external) variable declarations.  These are declared in
; the interface file unit_pgm.pas, but are not exported.

data	   segment word public

	   extrn   WinUpper_	 : word     ; Top left corner of window
	   extrn   WinLower_	 : word     ;  and bottom right corner.
	   extrn   Prev10Vector_ : dword    ; Previous $10 vector
	   extrn   VideoAttr_	 : byte     ; Attribute with which to
					    ;  display all characters.
data	   ends

code	   segment byte public
	   assume  cs:code,ds:data,es:nothing
	   public  __int10pgm

__int10pgm proc    far

	   push    ds
	   push    ax
	   mov	   ax,data
	   mov	   ds,ax
	   pop	   ax

	   cmp	   ah,2
	   je	   set_curpos	       ; Set the cursor position
	   cmp	   ah,3
	   je	   ret_curpos	       ; Return the cursor position
	   cmp	   ah,6
	   je	   scroll	       ; Scroll up
	   cmp	   ah,7
	   je	   scroll	       ; Scroll down
	   cmp	   ah,14
	   jne	   filter
	   jmp	   tty_write	       ; Write like TTY
filter:
	   callvideo		       ; Just filter any other function
	   jmp	   exit 	       ;  and exit.

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; set_curpos  The upper left coordinates are added to the requested
;	      position.  If the new position is outside of the window,
;	      then no action is taken.
;________________________________________________________________________

set_curpos:
	   push    dx		       ; This is altered
	   add	   dh,WinUpper_.row
	   cmp	   dh,WinLower_.row
	   ja	   exit_set_curpos
	   add	   dl,WinUpper_.col
	   cmp	   dl,WinLower_.col
	   ja	   exit_set_curpos
	   callvideo
exit_set_curpos:
	   pop	   dx		       ; Restore the orginal value
	   jmp	   exit

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; ret_curpos  The upper left coordinates are subtracted from the returned
;	      cursor position
;________________________________________________________________________

ret_curpos:
	   callvideo
	   sub	   dh,WinUpper_.row
	   sub	   dl,WinUpper_.col
	   jmp	   exit

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Scroll      The requested scoll area is forced within the specified
;	      window.  The area is forced by adding the WinUpper_ to CX
;	      (the requested to left corner), and if it extends beyond
;	      the lower right, set it equal to the lower right.  A similar
;	      calculation is done for DX.  The attribute used for the
;	      vacant lines is VideoAttr_.
;________________________________________________________________________

scroll:
	   push    ax		       ; Save the registers which may be
	   push    bx		       ; altered.
	   push    cx
	   push    dx

	   add	   ch,WinUpper_.row
	   cmp	   ch,WinLower_.row
	   jbe	   scroll_1
	   mov	   ch,WinLower_.row
scroll_1:
	   add	   cl,WinUpper_.col
	   cmp	   cl,WinLower_.col
	   jbe	   scroll_2
	   mov	   ch,WinLower_.col
scroll_2:
	   add	   dh,WinUpper_.row
	   cmp	   dh,ch
	   jb	   scroll_2a
	   cmp	   dh,WinLower_.row
	   jbe	   scroll_3
	   mov	   dh,WinLower_.row
	   jmp	   scroll_3
scroll_2a:
	   mov	   dh,ch

scroll_3:
	   add	   dl,WinUpper_.col
	   cmp	   dl,cl
	   jb	   scroll_3a
	   cmp	   dl,WinLower_.col
	   jbe	   scroll_4
	   mov	   dl,WinLower_.col
	   jmp	   scroll_4
scroll_3a:
	   mov	   dl,cl

scroll_4:
	   push    dx
	   sub	   dh,ch
	   inc	   dh
	   cmp	   al,dh
	   jne	   scroll_4a
	   mov	   al,0
scroll_4a:
	   pop	   dx
	   mov	   bh,VideoAttr_

	   callvideo
	   pop	   dx
	   pop	   cx
	   pop	   bx
	   pop	   ax

	   jmp	   exit

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; tty_write   Write to the screen interpreting BEL, BACKSPACE, LINEFEED
;	      and RETURN as commands instead of characters. In each case,
;	      including standard characters, if the new cursor position
;	      is adjusted so that output remains within the window.
;________________________________________________________________________

tty_write:
	   cmp	   al,7
	   je	   tty_write_beep

	   push    ax		       ; For all cases except the BEL,
	   push    bx		       ; begin by saving registers
	   push    cx		       ; which are changed, and get
	   push    dx		       ; the current position to deter-
				       ; mine if a linefeed or carriage
	   push    ax		       ; return must be performed.
	   push    bx
	   mov	   ah,3
	   xor	   bh,bh
	   callvideo		       ; Get cursor position
	   pop	   bx
	   pop	   ax

	   cmp	   al,8
	   je	   tty_write_backspace
	   cmp	   al,13
	   je	   tty_write_return
	   cmp	   al,10
	   je	   tty_write_linefeed

tty_write_standard:
	   mov	   ah,9 	       ; Write the character using the
	   xor	   bx,bx	       ; attribute globally defined.
	   mov	   bl,VideoAttr_
	   mov	   cx,1
	   callvideo

	   cmp	   dl,WinLower_.col    ; Now adjust the cursor position.
	   jb	   standard_1	       ; Can just increment it.
	   cmp	   dh,WinLower_.row
	   jb	   standard_2	       ; Room to do a simple CR/LF
	   push    dx		       ; Must scroll up one line
	   mov	   ah,6
	   mov	   al,1
	   mov	   cx,WinUpper_
	   mov	   dx,WinLower_
	   mov	   bh,VideoAttr_
	   callvideo
	   pop	   dx
	   jmp	   standard_4	       ; Move the column back to the left
standard_2:
	   inc	   dh		       ; Increment the column
standard_4:
	   mov	   dl,WinUpper_.col
	   jmp	   tty_write_exit
standard_1:
	   inc	   dl
	   jmp	   tty_write_exit

tty_write_beep:
	   callvideo		       ; Let the previous handler do this
	   jmp	   exit

tty_write_backspace:
	   cmp	   dl,WinUpper_.col    ; Only move back if within the
	   ja	   backspace_1	       ; window.
	   jmp	   tty_write_exit
backspace_1:
	   dec	   dl
	   jmp	   tty_write_exit

tty_write_return:
	   mov	   dl,WinUpper_.col    ; Move to the left edge
	   jmp	   tty_write_exit

tty_write_linefeed:
	   cmp	   dh,WinLower_.row    ; If there is enough room, just
	   jb	   linefeed_1	       ;  increment the row;
	   push    dx		       ;  otherwise scroll up one line.
	   mov	   ah,6
	   mov	   al,1
	   mov	   bh,VideoAttr_
	   mov	   cx,WinUpper_
	   mov	   dx,WinLower_
	   callvideo
	   pop	   dx
	   jmp	   tty_write_exit
linefeed_1:
	   inc	   dh

tty_write_exit:
	   mov	   ah,2 	       ; Now set the new cursor position
	   xor	   bh,bh
	   callvideo
	   pop	   dx		       ; Restore the registers
	   pop	   cx
	   pop	   bx
	   pop	   ax

exit:
	   pop	   ds		       ; Restore DS
	   iret

__int10pgm endp
code	   ends
	   end
