	 page  60,132
	 title Graphics Module for Print Screen with the EGA
Dmp200	 segment para public 'code'
	 assume cs:Dmp200,ds:Dmp200,es:Dmp200

Comment #

This routine was written by Dr. R.Brooks Van Horn, Jr. to fill a void
left by Big Blue when their support for the EGA never arrived.	This
routine will perform the Shift Print Screen function for a Radio Shack
DMP200 printer. I also have an Epson (IBM Printer) version and an EGA
Mode 16 driver to supplement EGA BIOS versions that like IBM's do not
support a read/write mode 16 (10H) interupt.

#

	 org 100h 
	 jmp   Setup

Mlen	 db    'EGA Print Screen Version 2.0'
Cmp_Str	 dw    $-mlen

;	 normal int 5 entry point

Begin:	 pushf		     ; enable further interrupts
	 push  ax
	 push  bx
	 push  bp
	 push  di
	 push  si
	 push  ds
	 xor   ax,ax
	 mov   ds,ax
	 mov   al,Byte Ptr ds:[449h] ; get the video mode
	 pop   ds	     ; restore data set register
	 and   al,07fh	     ; mask off any unwanted data
	 mov   cs:[Mode],ax  ; and save it for future use
	 cmp   al,7	     ; is this a mono card?
	 jz    Outz	     ; yes, exit - when in text mode
	 cmp   al,3	     ; see if we are in the graphics mode?
	 jg    cont	     ; yes, go around using dos
Outz:	 pop   si	     ; restore the registers saved
	 pop   di
	 pop   bp
	 pop   bx
	 pop   ax
	 popf
;-----	 jump far to Dos
	 db    0EAh	     ; jump far segment to Print
dos	 dw    0	     ; offset of jump
dos2	 dw    0	     ; segment of jump

Cont:	 push  cx	     ; and save all the registers
	 push  dx
	 push  ds
	 push  es
	 push  cs	     ; now put code seg register into
	 pop   es	     ; the extra segment register
	 xor   ax,ax
	 mov   ds,ax
	 inc   al	     ; see if we are already being done
	 mov   al,Byte Ptr ds:[500h] ; get the current printer status
	 or    al,al
	 jnz   Getout	     ; if 500h was not zero then exit
	 push  cs
	 pop   ds
	 xchg  al,Byte Ptr cs:switch
	 or    al,al
	 jnz   GetOut
	 sti
	 mov   cs:[sp_save],sp
	 jmp   Process

Return:	 mov   si,Offset Restore
	 mov   cl,cs:[si]    ; get number of bytes to print
	 xor   ch,ch
	 inc   si
	 call  Tabptr	     ; restore printer to original state
Exit:	 mov   sp,cs:[sp_save]
	 xor   ax,ax
	 mov   Byte Ptr cs:switch,al ; set value to zero
Getout:	 pop   es	     ; restore registers and exit
	 pop   ds
	 pop   dx
	 pop   cx
	 pop   si
	 pop   di
	 pop   bp
	 pop   bx
	 pop   ax
	 popf
	 sti
	 iret		     ; bye...............

;--------------------------------------------------------------------
;	       Radio Shack DMP-200 Printer with PC or AT
;--------------------------------------------------------------------

;  set graphics mode, condensed, 8 CRs, and tab to pos 45 to print
Line_Sp	 db    11,27,20,18,10,10,10,10,10,10,10,10,27,16,0,45
NofByte	 dw    0		  ; total number of columns required
Restore	 db    6,30,27,18,27,31,12
Line_Buf db    800 dup (?)	  ; line buffer
Col_2_Do dw    0		  ; column being printed
Eight	 dw    7
Four	 dw    4
Switch	 db    0
Sp_Save	 dw    0
Temp	 db    0,0
save_bp	 dw    0

;      Modes =	 4   5	 6   7	 8   9	 A   B	 C   D	 E   F	10  11	12  13
Col_Mode dw    320,320,640,  0,	 0,320,640,  0,720,320,640,640,640,640,640,350
Row_Mode dw    200,200,200,  0,	 0,200,200,  0,348,200,200,350,350,480,480,320

Mode	 dw    0	     ; Current Video Mode
Rows	 dw    0	     ; Number of rows for this mode
Cols	 dw    0	     ; Number of columns for this mode

;--------------------------------------------------------------------
;
;	 Print a Table of Control Values
;	 ===============================
;
Tabptr:	 mov   al,cs:[si]
	 call  Printer
	 inc   si
	 loop  Tabptr
	 nop
	 ret
;
;	 Send an alarm bell sound
;	 ========================
;
Bell:	 mov   al,7
	 mov   ah,14
	 int   10h
	 ret

;	 Send the character in AL to the printer
;	 =======================================
;
Printer: push  dx
	 sub   dx,dx
	 xor   ah,ah
	 int   17h
	 pop   dx
	 test  ah,25h
	 jnz   Error
	 ret	  
;
;	 Error returns on printer commands
;	 =================================
; 
Error:	 call  Bell
	 call  Bell
	 jmp   Exit	    
;
;	 Send a form feed to the Printer
;	 ===============================
;
Cr_Lf:	 push  dx
	 sub   dx,dx
	 mov   ax,0dh
	 int   17h
	 mov   ax,0ah
	 int   17h
	 pop   dx
	 test  ah,25h
	 jnz   Error
	 ret

;---------------------------------------------------------------------
;								     l
;		   Graphics Processing Section			     l
;								     l
;---------------------------------------------------------------------

Process: mov   bp,cs:[Mode]
	 sub   bp,4
	 sal   bp,1

	 mov   ax,cs:Row_Mode[bp]   ; save rows-1 and cols-1
	 dec   ax
	 mov   cs:Rows,ax
	 mov   ax,cs:Col_Mode[bp]
	 dec   ax
	 mov   cs:Cols,ax

	 mov   ah,1		; initialize the printer
	 xor   dx,dx
	 int   17h

	 mov   si,Offset cs:Line_Sp ; setup for correct interline spacing
	 mov   cl,cs:[si]	; get length
	 xor   ch,ch
	 inc   si
	 call  TabPtr
	 
	 mov   ax,cs:[Cols]	; decide to do graphics by row or column mode
	 cmp   ax,320
	 jg    By_Rows
	 jmp   By_Cols

;--------------------------------------------------------------------
;	  for By_Rows calls we have usually 640 x 200 type, so do 200
;	  across and 640 down the page.	 This means that we can only 
;	  single dot to get lines to connect on the printer paper.
;--------------------------------------------------------------------

By_Rows: mov   cx,0	     ; start the column index at zero

Br_Strt: push  cx	     ; save the current column index
	 mov   dx,cs:[Rows]  ; initialize the row count to max
	 mov   bh,Byte Ptr cs:[Eight]
	 xor   si,si	     ; max characters to print
	 mov   cs:[Col_2_Do],si
	 mov   di,Offset Line_Buf
	 xor   bl,bl

;	 do for a count of 'Eight'

Br_Cont: call  Read	     ; read the next dot at (row,col)
	 and   al,07fh	     ; check if any thing is there
	 jz    Br_Fill	     ; and skip if nothing there
	 mov   al,40h	     ; else fill the byte 
Br_Fill: shr   bl,1	     ; move reg left one bit 
	 or    bl,al	     ; and or in the next value
	 inc   cx	     ; increment the column count
	 dec   bh	     ; and the loop index
	 jnz   Br_Cont	     ; continue loop for 8 times

;	 undo

	 add   si,2	     ; add two to the column counter
	 or    bl,80h	     ; turn on the high order bit
	 mov   Byte Ptr cs:[di],bl ; save filled byte
	 mov   Byte Ptr cs:[di+1],bl ; twice for filling
	 cmp   cs:[Rows],199
	 jg    Br_Two
	 mov   Byte Ptr cs:[di+2],bl
	 inc   di
	 inc   si
Br_Two:	 add   di,2	     ; up the buffer ptr
	 and   bl,7fh
	 jz    Br_Skp

	 mov   Word Ptr cs:[Col_2_Do],si ; save index

Br_Skp:	 mov   bh,Byte Ptr cs:[Eight] ; set loop for 8 more cols per row
	 dec   dx	     ; delete one from the row index
	 pop   cx	     ; restore the column count
	 push  cx	     ; and save it again for this row
	 xor   bl,bl
	 cmp   dx,0	     ; and test to see if we are finished
	 jge   Br_Cont	     ; loop

	 mov   si,Offset Line_Buf ; get print line pointer
	 mov   cx,cs:[Col_2_Do] ; get the number of bytes to print
	 or    cx,cx
	 jz    Br_NoP	     ; skip if didn't get anything
	 call  TabPtr	     ; print the line
Br_NoP:	 call  Cr_Lf

	 pop   cx
	 add   cx,cs:[Eight] ; add 8 to column count
	 cmp   cx,cs:[Cols]  ; compare to max to do
	 jg    Br_Ret
	 jmp   Br_Strt	     ; continue until fini

Br_Ret:	 jmp   Return	     ; restore printer and exit

;--------------------------------------------------------------------
;	  for By_Cols calls we have usually 320 x 200 type, so do 320
;	  across and 200 down the page.	 This means that we must double
;	  dot to get lines to connect on the printer paper.
;--------------------------------------------------------------------

By_Cols: mov   dx,0	     ; start the row index at zero
	 mov   cx,799	     ; clear the buffer
	 mov   di,Offset cs:Line_Buf
	 cld
	 mov   ax,0
	 mov   Word Ptr cs:[Temp],ax
	 push  cs
	 pop   es
	 rep   stosb

;	 for each row do all the columns
Bc_Strt: push  dx	     ; save the current row index
	 mov   cx,0	     ; initialize the col count to start
	 mov   bh,Byte Ptr cs:[Four]
	 mov   cs:[Temp],bh
	 xor   si,si	     ; max characters to print
	 mov   cs:[Col_2_Do],si
	 mov   di,Offset Line_Buf

;	 for each column in the row, do
Bc_Pick: mov   bl,cs:[di]    ; get the last value
	 mov   al,cs:[Temp]  ; and see if this is a four or three count
	 shr   al,1
	 shl   al,1
	 cmp   al,cs:[Temp]  ; if even then use four
	 je    Bc_Cont
	 dec   bh	     ; otherwise use three
	 mov   cs:[Temp],bh	    

Bc_Cont: call  Read	     ; read the next dot at (row,col)
	 and   al,07fh	     ; check if any thing is there
	 jz    Bc_Fill	     ; and skip if nothing there
	 mov   al,60h	     ; else fill the byte 
Bc_Fill: shr   bl,1	     ; move reg left one bit 
	 shr   bl,1	     ; and repeat
	 or    bl,al	     ; and or in the next value
	 inc   dx	     ; increment the row count
	 dec   bh	     ; and the loop index
	 jnz   Bc_Cont	     ; continue loop for 4 times

	 add   si,2	     ; add two to the column counter
	 or    bl,80h	     ; turn on the high order bit
	 mov   Byte Ptr cs:[di],bl ; save filled byte
	 mov   Byte Ptr cs:[di+1],bl ; twice for filling
	 add   di,2	     ; up the buffer ptr
	 and   bl,7fh
	 jz    Bc_Skp

	 mov   Word Ptr cs:[Col_2_Do],si ; save index

Bc_Skp:	 mov   bh,cs:[Temp] ; set loop for 8 more cols per row
	 mov   bl,cs:[di]    ; get the last value
	 inc   cx	     ; add one to the col index
	 pop   dx	     ; restore the row count
	 push  dx	     ; and save it again for this row
	 cmp   cx,cs:[Cols]  ; and test to see if we are finished
	 jle   Bc_Pick	     ; loop

	 mov   bp,Word Ptr cs:[Temp] ; save the current Temp setting
	 xor   Byte Ptr cs:[Temp],7
	 mov   si,Offset Line_Buf ; get print line pointer
	 mov   cx,cs:[Col_2_Do] ; get the number of bytes to print
	 or    cx,cx
	 jz    Bc_NoP	     ; skip if didn't get anything
	 call  TabPtr	     ; print the line
Bc_NoP:	 call  Cr_Lf

	 pop   dx
	 add   dx,bp	     ; add to column count
	 cmp   dx,cs:[Rows]  ; compare to max to do

	 jg    Bc_Ret	     ; if fini then exit
	 jmp   Bc_Strt	     ; else go do next row
Bc_Ret:	 jmp   Return	     ; restore printer and exit

;--------------------------------------------------------------------
;
;	 Read Dot Routine Using DOS/BIOS or VDI interface for int 10h
;
;-------------------------------------------------------------------
Read:	  xor  al,al
	  cmp	dx,0	      ; see if all is ok
	  jl	Read_Ret
	  cmp	cx,cs:[Cols]  ; also check on overdoing it
	  jg	Read_Ret
	  cmp	cs:[Mode],6
	  jg	Read_Dot
	  mov	ah,13
	  push	bx
	  push	cx
	  push	dx
	  mov	cs:[save_bp],bp
	  int	10h	      ; let BIOS read the dots
	  mov	bp,cs:[save_bp]
	  pop	dx
	  pop	cx
	  pop	bx
	  xor	ah,ah	      ; clear the high byte for testing
Read_Ret: ret

Read_Dot: push bx
	 push  cx
	 push  dx
	 mov   ax,dx	     ; save column value
	 mov   bx,cx	     ; and row value
	 mov   dx,0a000h     ; Video ram address
	 mov   ds,dx	     ; into segment register
	 mov   dx,80
	 mul   dx	     ; column * 80
	 shr   bx,1
	 shr   bx,1
	 shr   bx,1	     ; row / 8
	 add   bx,ax	     ; actual data byte address
	 and   cl,7	     ; row mod 8
	 xor   cl,7	     ; bit mask 7 - (row mod 8)
	 mov   ch,1
	 shl   ch,cl	     ; 2^mask
	 mov   ah,3	     ; initialize bit plane number
Plane_Loop:
	 mov   dx,03CEh	     ; EGA Controller
	 mov   al,4	     ; map select register
	 out   dx,al
	 mov   dx,03CFh	     ; data register
	 mov   al,ah	     ; select bit planes 3,2,1,0
	 out   dx,al
	 mov   al,ds:[bx]    ; read bit plane
	 shl   cl,1
	 and   al,ch
	 jz    Not_On
	 or    cl,1
Not_On:	 dec   ah
	 jge   Plane_Loop
	 and   cl,0fh
	 mov   al,cl
	 pop   dx
	 pop   cx
	 pop   bx
	 ret

	 dw    -1	     ; fense
	 
;--------------------------------------------------------------------
;
;	 Initialization Routine for Graphics.Com
;
;--------------------------------------------------------------------
Setup:	 xor   ax,ax
	 mov   es,ax		  ; setup to address segment zero
	 mov   cx,es:[0016h]	  ; get current segment
	 mov   bx,es:[0014h]	  ; and offset
	 mov   ds,cx		  ; make that the data segment
	 mov   cx,cs:[Cmp_Str]	  ; setup to check bytes for same
	 mov   si,Offset cs:mlen  ; data field "Graphic..."
	 mov   bx,si  

Check:	 mov   ax,cs:[si]	  ; get a byte in accum
	 cmp   ax,ds:[bx]	  ; is it the same
	 jne   Ok		  ; no, then we are ok
	 inc   si		  ; otherwise, increment pointers
	 inc   bx		  ; to both fields
	 loop  Check		  ; and loop for 7 checks

;	 Bad news, it is already resident

	 push  cs		  ; now setup to display a message
	 pop   ds   
	 mov   ax,es:[0016h]	  ; get current segment address
	 mov   di,Offset Field3
	 call  Convert
	 add   di,5		  ; increase pointer to next field
	 mov   ax,es:[0014h]	  ; and the offset address
	 call  Convert 
	 mov   ax,cs
	 mov   ds,ax
	 mov   ah,09h
	 mov   dx,Offset Message1 ; setup to display message
	 int   21h
	 int   20h		  ; if same then dont overlay but exit

;	 Good News, make DMP200 resident and display location message

Ok:	 mov   bx,es:[0014h]	  ; get the current offset in int vector
	 mov   ax,cs		  ; now setup to display a message
	 mov   ds,ax
	 mov   Word Ptr dos,bx	  ; and save it locally
	 mov   bx,es:[0016h]	  ; get the segment location of item too
	 mov   Word Ptr dos2,bx	  ; and save it also
	 mov   ax,cs		  ; save our entry at int vector
	 mov   es:[0016h],ax	  ; segment
	 mov   bx,Offset cs:Begin ; define entry point to routine
	 mov   es:[0014h],bx	  ; offset
	 mov   di,Offset Field1
	 call  Convert		  ; convert starting address
	 mov   di,Offset Field2
	 mov   bx,Offset Setup
	 add   bx,15		  ; convert to next paragraph addr
	 mov   cl,4
	 shr   bx,cl
	 add   ax,bx
	 call  Convert		  ; and the new ending address
	 mov   ah,09h		  ; now do our message
	 mov   dx,Offset Message2
	 int   21h
	 mov   dx,Offset cs:Setup
	 int   27h		  ; terminate but stay resident

Convert	 proc  near
;
;	 ax = number to be converted 
;	 di = address of field in which to put the hex characters 
;
	 push  ax
	 push  bx
	 push  cx
	 push  dx
	 lea   bx,Ascii
	 mov   cl,4
	 mov   dx,ax
	 mov   al,ah
	 shr   al,cl
	 xlat  Ascii
	 mov   Byte Ptr cs:[di],al
	 mov   al,dh
	 and   al,0fh
	 xlat  Ascii
	 mov   Byte Ptr cs:[di+1],al
	 mov   al,dl
	 shr   al,cl
	 xlat  Ascii
	 mov   Byte Ptr cs:[di+2],al
	 mov   al,dl
	 and   al,0fh
	 xlat  Ascii
	 mov   Byte Ptr cs:[di+3],al
	 pop   dx
	 pop   cx
	 pop   bx
	 pop   ax
	 ret
Convert	 endp
Message1 db    'DMP200 Graphics routine is already resident ('
Field3	 db    '0000-0000)',13,10,'$'
Message2 db    'DMP200 Graphics routine is now resident ('
Field1	 db    '0000-'
Field2	 db    '0000)',13,10,'$'
Ascii	 db    '0123456789abcdef'
Dmp200	 ends
	 end
