         page  60,132
         title Graphics Module for Print Screen with EGA and Epson Printer

; Revised 17 Nov 1989 by Joe Doupnik
;  Add initial jump label start: and identify it for the DOS loader.
;  Relabel "Out" to be "Out1" to avoid assembler keyword.
;
; Revised 6 Dec 1989 by Frank da Cruz
;  Change Epson initialization and restore strings:
;  Disable perforation skip and paper-out detect during graphics print.
;  Restore string is now ESC @ (initialize) rather than ESC 0 (1/8"/line)

Epson    segment para public 'code'
         assume cs:Epson,ds:Epson,es:Epson

comment #

This routine is for public domain for those of you who bought an
EGA and enhanced monitor and determined that you could no longer
do a Shf_Prt_Scn in mode 10h (16).  If you have an IBM compatible
and an Epson FX series printer then this routine will replace the
GRAPHICS.COM routine. I also have routines for Radio Shack DMP 
printers with 8 pins (but only 7 usable for graphics) and for HP
LaserJet printers when used with an EGA. Enjoy...

Dr. R. Brooks Van Horn, Jr.
701 Fall Place
Herndon, VA. 22070

#
         org 100h 
start:   jmp   Setup

Mlen     db    'EGA Graphics Ver 1.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
         mov   ah,15         ; setup a function call to dos to
         int   10h           ; get the video mode
         and   ax,007fh      ; mask off unwanted data
         mov   cs:[Mode],ax  ; and save it for future use
         cmp   al,7          ; is this a mono card?
         jz    Out1          ; yes, exit - when in text mode
         cmp   al,3          ; see if we are in the graphics mode?
         jg    cont          ; yes, go around using dos
Out1:    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 mode and also set it
         or    al,al
         jnz   Getout        ; if 550h 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...............

;--------------------------------------------------------------------
;              Epson FX Printer with IBM-PC
;--------------------------------------------------------------------
Line_Sp  db    7,27,51,24,27,79,27,56 ; Control Codes for 24/216" line spacing
                             ; Also disable perforation skip (ESC O) and
                             ; disable paper-out detect (ESC 8) (fdc)
Set_Graf db    4,27,76       ; Cntrl Code for each graphics line printed
Col_2_Do dw    0             ; last column number  to be printed
Restore  db    2,27,64,12    ; change count to 3 if you want a form feed
                             ; changed from ESC 0 to ESC @ (fdc)   

Line_Buf db    960 dup (?)   ; line buffer
Eight    dw    8
Four     dw    4
Switch   db    0
Sp_Save  dw    0
Mode     dw    0             ; Current Video Mode
Rows     dw    0             ; Number of rows for this mode
Cols     dw    0             ; Number of columns for this mode

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

;--------------------------------------------------------------------

;        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 cs: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,1          ; else fill the byte 
Br_Fill: shl   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
         mov   Byte Ptr cs:[di],bl ; save filled byte
         mov   Byte Ptr cs:[di+1],bl ; twice for filling
         cmp   cs:[Rows],199 ; check for less than 200 rows
         jg    Br_Two
         mov   Byte Ptr cs:[di+2],bl ; and 3 times for low resolution
         inc   di
         inc   si
Br_Two:  add   di,2          ; up the buffer ptr
         or    bl,bl         ; do we have anything to print?
         jz    Br_Skp        ; no, skip

         mov   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
         cmp   dx,0          ; and test to see if we are finished
         jge   Br_Cont       ; loop

         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

         push  cx            ; save number to do
         mov   si,Offset cs:Set_Graf ; setup for graphics
         mov   cl,cs:[si]    ; get length
         xor   ch,ch
         inc   si            ; point past length field
         call  TabPtr        ; perform the setup
         pop   cx            ; now get the real characters to do
         mov   si,Offset cs:Line_Buf ; get print line pointer
         call  TabPtr        ; and print the line

Br_NoP:  call  Cr_Lf         ; do carriage return and line feed at end

         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

;        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]
         xor   si,si         ; max characters to print
         mov   cs:[Col_2_Do],si
         mov   di,Offset cs:Line_Buf

;        for each column in the row, do
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,3          ; else fill the byte from the bottom
Bc_Fill: shl   bl,1          ; move reg left one bit 
         shl   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,3          ; add three to the column counter
         mov   Byte Ptr cs:[di],bl ; save filled byte
         mov   Byte Ptr cs:[di+1],bl ; twice for filling
         mov   Byte Ptr cs:[di+2],bl ; and three times for a lady
         add   di,3          ; up the buffer ptr
         or    bl,bl         ; see if anything is there
         jz    Bc_Skp        ; skip if we have nothing to plot

         mov   cs:[Col_2_Do],si ; save index

Bc_Skp:  mov   bh,Byte Ptr cs:[Four] ; set loop for 8 more cols per row
         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_Cont       ; loop

         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

         push  cx            ; save number to do
         mov   si,Offset cs:Set_Graf ; setup for graphics
         mov   cl,cs:[si]    ; get length
         xor   ch,ch
         inc   si            ; point past length field
         call  TabPtr        ; perform the setup

         pop   cx            ; now get the real characters to do
         mov   si,Offset cs:Line_Buf ; get print line pointer
         call  TabPtr        ; print the line

Bc_NoP:  call  Cr_Lf

         pop   dx
         add   dx,cs:[Four]  ; 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],10h
          je    Read_Dot      ; if mode = 10 then do special I/O
          mov   ah,13
          push  bx
          push  cx
          push  dx
          int   10h
          pop   dx
          pop   cx
          pop   bx
          xor   ah,ah         ; clear the high byte for testing
Read_Ret: ret

;---------------------------------------------------------------
;
;        Read a Pixel (Dot) from the EGA using Read Mode 0
;
;----------------------------------------------------------------

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 EPSON.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

         mov   ax,cs
         mov   ds,ax
         mov   ah,09h
         mov   dx,Offset Message1
         int   21h
         int   20h                ; if same then dont overlay but exit

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    'EGA Graphics routine is already resident',13,10,'$'
Message2 db    'EGA Graphics routine is now resident ('
Field1   db    '0000-'
Field2   db    '0000)',13,10,'$'
Ascii    db    '0123456789abcdef'
Epson    ends
         end	start
