        PAGE  60,132
        TITLE   EPSON MX-GRAFTRAX PrtSc, V 2.3a
; GRAF.COM  3/12/83
; INTERRUPT REPLACEMENT FOR PRINT SCREEN FUNCTION ON IBMPC(tm)
;  =====> N O T I C E <=======
;       THIS PROGRAM IS SUPPLIED FOR YOUR PERSONAL AND NON-COMMERCIAL
;       USE ONLY. NO COMMERCIAL USE PERMITTED. USERS OF THIS PROGRAM
;       ARE GRANTED LIMITED RIGHTS TO REPRODUCE AND DISTRIBUTE THIS
;       PROGRAM. IT MAY NOT BE SOLD. PROVIDING THIS PROGRAM FOR
;       CONSIDERATION ANY SORT, INCLUDING COPYING OR DISTRIBUTION FEES,
;       IS SPECIFICALLY PROHIBITED.  THE AUTHOR CANNOT ASSUME RESPONSIBILITY
;       FOR DAMAGES, REAL OR IMAGINED, RESULTING FROM THE USE OF THIS PROGRAM.
;  =====> ENDNOTICE <=========
;
;       PLEASE SEND PROBLEM REPORTS AND SUGGESTIONS TO:
;               MARTY SMITH
;               310 CINNAMON OAK LANE
;               HOUSTON, TEXAS  77079
;               SOURCE ST2259, COMPUSERVE 72155,1214
;               (713) 661-1241 Office, (713) 464-6737, Home.
;
;  THIS PROGRAM ORIGINALLY DESIGNED FOR EPSON-MX(tm) SERIES PRINTERS
;  WITH GRAFTRAX80(tm) AND GRAFTRAX+(tm) BIT-PLOT GRAPHIC CAPABILITES.
;  ======> Now modified with conditional compilation and macros for
;  ======> c.itoh(tm) model 8510a and other printers with bit-plot
;  ======> capabilities. See the macro definitions and equates following
;  ======> these messages.
;
;       CHANGE HISTORY:
;       9/18/82 - Buffer in routine for a line of bit-plot bytes to allow for
;  checking for blank lines replaced by pre-scan routine, saving space.
;       1/24/83 - Modifications for conditional assembly with other printers
;  and C.ITOH 8510a.
;       1/24/83 - Improved error checking for out-of-paper and I/O errors
;  involving printer.
;       1/24/83 - Bug in error check corrected, occuring when routine does
;  error exit and is then called again, resulting in bit-plot data sent in
;  regular mode.
;       3/12/83 - Allow calling as a subroutine. i.e. no shift key depressed.
;  Defaults to small print mode. Can be poked for LARGE.
;
;       THE IBM MACRO ASSEMBLER(tm) AND LINK(tm) WILL PRODUCE
;       GRAF.EXE, WHICH MUST BE CONVERTED TO A .COM PROGRAM BY
;       THE COMMAND FROM DOS -EXE2BIN-.
;               do this:
;       X>EXE2BIN GRAF.EXE GRAF.COM
;
;       Features:
;  Accepts ESC key exit, prescans to test for blank line
;  left shift prtsc = small graphics, right shift prtsc = big
;  Runs as a .COM type program under dos
;     resident until power down or reset.
;   1 = screen sent horiz. 320 bits in 480 mode
;   2 = screen sent vert. 400 bits double printed in 480 mode
; **************  1 mode **********************
;       DL = masking character
;       DH = count of 25 (physical lines)
;       CX = counter for each line (80)
;       DS = used to index screen at 'b8000'
;   These regs must be preserved during routine
;         (increment each line by adding '14' hex to ds for para
;               boundary of 320 bytes '140')
; **************  2 mode *********************
;       DH = count of 40 (physical lines)
;       CX = counter for each line (100)
;       SI = index to screen via ds
;   These regs must be preserved during routine
;       all output to printer is done from routine -send2-, which uses
;       bios routine int 17h, and provides safe error exit.
;
;  GRAF.COM is designed with the idea that the user's main program is the
;  primary function and GRAF.COM should not cause problems of its own.
;  The author has tried his best to send control back to the main program in
;  the case of errors involving the printer, and to make the program as easy
;  to use as possible.
;
; =======================================================
; =             USER MODIFICATION SECTION.              =
; =     If your printer can treat a byte of data to     =
; =     control the wires on the dot-matrix head you    =
; =     can probably get this working with your printer =
; =     If you have Epson Graftrax or a C.Itoh 8510a,   =
; =     just set the two equates indicated to -true-    =
; =     and compile. Otherwise get out your manual      =
; =     and put the code indicated in the -other-       =
; =     section and set -other- to -true-.              =
; =     **>     The title message starts at label       =
; =     **>     -buffer-. If you set for another        =
; =     **>     printer you should change the           =
; =     **>     greeting to indicate which printer      =
; =     ===> ONLY SET *ONE* CONDITION TO -TRUE- or you  =
; =     will have a real mess!          marty smith     =
; =======================================================

TRUE    EQU     1       ; DON'T CHANGE THESE!
FALSE   EQU     0

; ===============>  A L L  U S E R S  <===================
; ====> SET ONE AND ONLY ONE OF THE FOLLOWING THREE <=====

EPSON   EQU     FALSE
CITOH   EQU     TRUE
OTHER   EQU     FALSE

; Each bit of a byte is mapped to the wire head of the printer.
; If the Epson MX is sent 80h (bit 7), the TOP wire makes a dot.
; If the C.ITOH is sent   01h (bit 0), the TOP wire makes a dot.
; ===============>  A L L  U S E R S  <===================
; =====> SET ONE AND ONLY ONE OF THE FOLLOWING TWO <======

BIT7    EQU     FALSE
BIT0    EQU     TRUE
        ; BIT7 is TRUE for EPSON
        ; BIT0 is TRUE for CITOH

; *****************************
; *  START of -OTHER- SECTION *
; *****************************
;
; ALL routines must set either BIT7 or BIT0.
;   above. If your printer can't see bit 7 or 0 as the top wire, you
;   will probably have quite a time getting this routine to work.
;
; OLINE Resets line spacing so that the print head
; will make a continuous line DOWN the page. (8/72 inch)
; This is the sequence to set the EPSON for this. (ESC A 8)
; SEND2 sends the byte in AL to the printer for ALL routines.
; It uses the INT 17h routine in order to avoid DOS's extra line
; feeds and CR's. Set for LPT1: Change SEND2 to redirect. DX=0=LPT1,1=LPT2
OLINE   MACRO
        MOV     AL,27
        CALL    SEND2
        MOV     AL,65
        CALL    SEND2
        MOV     AL,8
        CALL    SEND2
        ENDM

; ORLINE restores the printer to normal line spacing
; Example is for EPSON (ESC 2)
ORLINE  MACRO
        MOV     AL,27
        CALL    SEND2
        MOV     AL,'2'
        CALL    SEND2
        ENDM

; ORESET reinitializes the printer to default settings, spacings,
; current line becomes Top of Form.
; Is used by LARGE print to allow a series to be printed
; on separate pages. It can be modified by getting rid of label
; TOF: up to but not including   JMP DONE, which is the exit from
; the whole routine. Example is for EPSON. (ESC @)
ORESET  MACRO
        MOV     AL,27
        CALL    SEND2
        MOV     AL,'@'
        CALL    SEND2
        ENDM

; BP1 initiates bit-plot graphics. It tells the printer
; that the next xxx bytes are to be considered bit-plots and not
; regular characters. The small print routine sends 320 bit plot bytes
; to the printer. On the EPSON this is:
; ESC K 64 1   hex 1b 4b 40 1  > 1*256+64=320
; --> the first part indents the page with ordinary spaces
; --> to find spaces take TOTAL_DOTS_PER_LINE - 320. Then / BITS_PER_CHARACTER
; --> Divide this by two and you have the spaces to indent
BP1     MACRO
        MOV     CX,13   ; EPSON ( we've got 320 dots and 480 to work with
INLOP:  MOV     AL,20H  ; 480-320=160 / 6 dots per char. = 26.67 extra
        CALL    SEND2   ; so indent the picture 13 spaces to center
        LOOP    INLOP
        MOV     AL,27
        CALL    SEND2
        MOV     AL,75
        CALL    SEND2
        MOV     AL,64
        CALL    SEND2
        MOV     AL,1
        CALL    SEND2
        ENDM

; BP2.
; The LARGE print sends 400 bit plot bytes to the printer. On the EPSON:
; ESC K 144 1  hex 1b 4b 90 1  > 1*256+144=400
; FIND YOUR INDENT FOR 400 BITS
BP2     MACRO
        MOV     CX,6    ; EPSON ( we've got 400 dots and 480 to work with
INLOP2: MOV     AL,20H  ; 480-400=80 / 6 dots per char. = 13.33 extra
        CALL    SEND2   ; so indent the picture 6 spaces to center
        LOOP    INLOP2
        MOV     AL,27
        CALL    SEND2
        MOV     AL,75
        CALL    SEND2
        MOV     AL,144
        CALL    SEND2
        MOV     AL,1
        CALL    SEND2
        ENDM

; ****************************
; *   END OF -OTHER- SECTION *
; ****************************

;  ***************> START OF ACTUAL CODE <*****************

CSEG    SEGMENT 'CODE'
        ASSUME CS:CSEG
        ORG     100H            ; set up for .COM conversion
INIT    PROC    FAR             ; we're an interrupt routine
        JMP     INITIAL         ; so we have to set up first
BUFFER  DB      '         GRAFTRAX.COM v1.2',10,13
        DB      'EPSON MX-80 GRAFTRAX(tm) Screen Printer.',13,10
        DB      '   Left Sh. PrtSc. = LARGE GRAPHICS',13,10
        DB      '  Right Sh. PrtSc. = small graphics',13,10
        DB      ' Text screen uses regular ROM routine.',13,10
        DB      '   ESCape will exit GRAPHICS print.',13,10,'$'
GOWAIT  DW      0
WHERESI DW      0
PTFLAG  DB      0
ONEOR2  DB      0
DSTOR   DW      0
FUDGE   DD      0
INITIAL:
        MOV     AX,0    ; GET ADDR OF
        MOV     DS,AX   ; PRINT SCREEN ROUTINE
        MOV     SI,14H  ; IN ROM
        MOV     AX,[SI] ; from interrupt table in ram
        MOV     DX,AX   ; in case they change the ROM's!
        INC     SI
        INC     SI
        MOV     AX,[SI]
        MOV     DS,AX
        MOV     AL,0F1H ; MOVE IT TO
        MOV     AH,25H
        INT     21H     ; INT F1H described in Tech. Manual as unused vector
        MOV     AX,CS   ; RESET INT 5
        MOV     DS,AX   ; TO POINT TO
        MOV     AX,OFFSET START ; THIS ROUTINE
        MOV     DX,AX
        MOV     AL,5
        MOV     AH,25H  ; DOS routine to reset int. vector
        INT     21H
        MOV     AX,CS
        MOV     DS,AX
        MOV     AX,OFFSET BUFFER
        MOV     DX,AX
        MOV     AH,9
        INT     21H     ; PRINT GREETING
        MOV     AX,OFFSET LAST  ; last address here
        MOV     DX,AX
        INC     DX
        INT     27H     ; TERMINATE BUT STAY RESIDENT
INIT    ENDP
; --->  ACTUAL INTERRUPT ROUTINE STARTS HERE  <---
START   PROC    FAR     ; Start of main routine--Shift Prt.Sc hit.
        ASSUME CS:CSEG
        STI             ; This follows ROM routine real close
        PUSH    DS
        PUSH    AX      ;SAVE REGS
        PUSH    BX
        PUSH    CX
        PUSH    DX
        PUSH    DI
        PUSH    SI
        PUSH    BP
        MOV     BP,SP   ; Save in case of error for reset
        MOV     AX,50H  ; Check here first to see if
        MOV     DS,AX   ; routine is already in progress
        MOV     SI,0    ; otherwise it will be a mess.
        MOV     AL,01H
        CMP     [SI],AL
        JNZ     NXTS    ; if not we're go for routine
        JMP     EXIT    ; otherwise go back home.
NXTS:   MOV     [SI],AL ; if we're go don't let us start again until done.
        MOV     AH,15   ; Get the current video state.
        INT     10H     ; from the ROM routine,
        CMP     AL,4    ; AL=4-6 are all graphics so we're OK
        JZ      GRAPHIC
        CMP     AL,5
        JZ      GRAPHIC
        CMP     AL,6
        JZ      GRAPHIC
        MOV     AL,0    ; else reset and go to ROM routine.
        MOV     [SI],AL
        INT     0F1H    ; this is where we stored the ROM routine entry.
        JMP     EXIT    ; Do the ROM routine but come back here to leave.
GRAPHIC:
        MOV     AX,40H  ; Get the keyboard shift flag
        MOV     DS,AX   ; segment
        MOV     SI,17H  ; and address
        MOV     AX,[SI] ; pick it up
        AND     AX,3    ; get rid of other stuff
        CMP     AX,0    ; Mod. to create default small
        JNZ     GR1     ; for case where routine is called as
        MOV     AX,1    ; a subroutine.
GR1:    MOV     CS:ONEOR2,AL    ; store for later
        PUSH    AX      ; also here
        MOV     AL,0    ; make sure this starts out as NO print.
        MOV     CS:PTFLAG,AL
        MOV     DL,00H  ; These bits indicate whether R or L Shift is down
        MOV     DH,19H  ; 25 lines of graphic dots at 8 dots per line
        MOV     AX,0B800H ; stored in DX
        MOV     DS,AX   ;SET UP FOR SCREEN PEEK
; Printer setup section to change line spacing to 8/72" for continuous dots
        IF      EPSON
        MOV     AL,27   ; ESC
        CALL    SEND2   ; goes out through INT 17h to avoid DOS processing
        MOV     AL,65   ; A
        CALL    SEND2
        MOV     AL,8    ; 8
        CALL    SEND2
        ENDIF
        IF      CITOH   ; ESC T 16
        MOV     AL,27   ; ESC
        CALL    SEND2   ; goes out through INT 17h to avoid DOS processing
        MOV     AL,'T'  ; T
        CALL    SEND2
        MOV     AL,'1'  ; 1
        CALL    SEND2
        MOV     AL,'6'  ; 6
        CALL    SEND2
        MOV     AL,27
        CALL    SEND2
        MOV     AL,62   ; Set printer to unidirectional for dot alignment
        CALL    SEND2
        ENDIF
        IF      OTHER
        OLINE
        ENDIF
        POP     AX      ; get back which routine
        CMP     AL,2    ; Left Shift Prt Sc means LARGE graphic print
        JZ      MAIN2   ; so hop over there if so.
; START OF small GRAPHICS PRINT ROUTINE.
; This routine scans across the screen from left to right,
; building an EPSON bit plot byte out of IBM screen dots.
; EPSON wire head               IBM screen color dots
; TOP    o  128  80h bit 7      | 00 | 01 | 10 | 11 | = 4 dots, one byte
;        o   64  40h  "  6
; one    o   32  20h  "  5      ibm dots go one raster line then the next
; bit    o   16  10h  "  4      EVEN line, ie 0, 2, 4 etc.
; plot   o    8  08h  "  3
; byte   o    4  04h  "  2      then you go back and do 1, 3, 5 etc.
;        o    2  02h  "  1
; BOTTOM o    1  01h  "  0      At loc. 0000h are 4 dots, 0,0|0,1|0,2|0,3
;                               At loc. 2000h are 4 dots, 1,0|1,1|1,2|1,3
;
MAIN:   MOV     CX,80   ; 80 x 4 = 320 dots.
MLOOP:  MOV     DL,0C0H ; 11000000b
        CALL    TST4    ; see if this comes back <> 0
        MOV     AL,AH   ; we are testing bit patterns for one screen byte
        CALL    SEND    ; don't send to printer unless something to send
        MOV     DL,30H  ; 00110000b
        CALL    TST4    ; each byte is 4 dots
        MOV     AL,AH   ; so we test for each dot in a byte
        CALL    SEND    ; send sets PTFLAG if there is a dot on the line
        MOV     DL,0CH  ; 00001100b
        CALL    TST4    ; then resets to start of line and starts printing
        MOV     AL,AH   ; AL is the bit plot byte being built
        CALL    SEND
        MOV     DL,03H  ; 00000011b
        CALL    TST4    ; TST4 scans down 8 screen dot lines each time called
        MOV     AL,AH
        CALL    SEND
        LOOP    MLOOP   ; 80 bytes make 320 dots
        CALL    LFCR    ; this is a good old regular line feed/carriage return
        CALL    BREAK?  ; see if someone hit ESC key. If so take early exit
        OR      AL,AL
        DEC     DH      ; DL is line counter
        CMP     DH,0    ; when it goes 0 we're through
        JZ      DONE    ; reset everything and do an IRET
        MOV     AX,DS   ; otherwise bump the SEGMENT reg so that location
        ADD     AX,14H  ; 0 is the start of the next line
        MOV     DS,AX   ; X'140' = 320
        JMP     MAIN    ; and do this 80 times (80x4=320)
DONE:   MOV     AX,0
; This is the common exit for both routines, Printer is restored.
DONE1:  PUSH    AX      ; save AX cause it has error exit flag
; EPSON command to reset printer to 6 lines/in. = ESC 2 (1b 32)
        IF      EPSON
        MOV     AL,27   ; RESET PRINTER, RESTORE REGS
        CALL    SEND2
        MOV     AL,32H
        CALL    SEND2
        ENDIF
; FOR CITOH MAKE SURE BIDIRECTIONAL PRINTING IS RESTORED
        IF      CITOH
        MOV     AL,27
        CALL    SEND2
        MOV     AL,'A'
        CALL    SEND2
        MOV     AL,27
        CALL    SEND2
        MOV     AL,60
        CALL    SEND2
        ENDIF
        IF      OTHER
        ORLINE
        ENDIF
EDONE:  MOV     AX,50H  ; Set end of PrtSc indication
        MOV     DS,AX   ; OK to come back and do again
        MOV     SI,0
        POP     AX
        MOV     [SI],AL
EXIT:   POP     BP
        POP     SI      ; restore regs and return to caller
        POP     DI
        POP     DX
        POP     CX
        POP     BX
        POP     AX
        POP     DS
        IRET            ; were an interrupt routine so we IRET
; START OF LARGE PRINT ROUTINE
; +-------------+
; | ^ ^         |  This time we scan from 199,0 to 0,0
; | ^ ^         |  and go across
; | ^ ^         |  o  o    x  x    o  x    x  x   These are representations
; | | |         |  o  o    o  o    x  o    x  x   of one color dot.
; +-------------+    0       1       2       3     Palettes
MAIN2:  MOV     DH,80   ; we have 80 colunms x 25 lines here
        MOV     CS:WHERESI,3EF0H ; SI is our index
        MOV     SI,CS:WHERESI
MAIN2A: MOV     CX,64H  ; 100
MLOOP2: MOV     AL,[SI] ; idea is to get a byte starting at screen BOTTOM
        IF      BIT0
        CALL    REVERSE ; Bits have to be reversed on wire 0 type
        ENDIF
        CALL    SEND    ; send it since these resemble bit plot bytes
        CALL    FLIPFLOP ; then reverse(sort of) this byte and send it
        CALL    SEND    ; again.
        MOV     CS:GOWAIT,SI ; store SI for next EVEN raster line
        SUB     SI,2000H ; subtract 2000h for the next ODD raster line
        MOV     AL,[SI] ; and do the same here
        IF      BIT0
        CALL    REVERSE
        ENDIF
        CALL    SEND
        CALL    FLIPFLOP
        CALL    SEND
        MOV     SI,CS:GOWAIT ; get back the EVEN line
        SUB     SI,80   ; advance UP the screen one line (say 199,0 to 197,0)
        LOOP    MLOOP2  ; and do this 100 times
        CALL    LFCR    ; finished with one line we send normal line-end
        CALL    BREAK?  ; check for an ESC if we want to abort
        OR      AL,AL   ; clear flags
        DEC     DH      ; DH is our line counter,
        CMP     DH,0    ; when it goes 0 we're done.
        JZ      TOF     ; so we'll try to reset Top of Form and exit
        INC     CS:WHERESI      ; else go to the next byte location
        MOV     SI,CS:WHERESI   ; store
        JMP     MAIN2A          ; and do again
TOF:    MOV     CX,19   ; tof restores page to 11 inches from where it started
TOFL:   MOV     AL,13   ; send a bunch of cr's and lf's
        CALL    SEND2   ; put BREAK? in here somehow
        MOV     AL,10
        CALL    SEND2
        CALL    BREAK?  ; check for early exit
        LOOP    TOFL    ; on and on.
; This restores the EPSON to 6 lines per inch
; ESC @ = Restore all settings to default
        IF      EPSON
        MOV     AL,27   ; esc
        CALL    SEND2
        MOV     AL,64   ; @
        CALL    SEND2
        ENDIF
;       IF      CITOH   ; No equivalent to Epson ESC @
;       MOV     AL,27   ; for CITOH
;       CALL    SEND2   ; so just reset line feed pitch
;       MOV     AL,'A'  ; this is done by DONE anyway
;       CALL    SEND2   ; so leave open if someone wants to patch
;       ENDIF
        IF      OTHER
        ORESET
        ENDIF
        JMP     DONE    ; clean up and back to caller.
START   ENDP
SEND2   PROC    NEAR    ; BIOS routine to print the character in AL
        PUSH    AX
        MOV     AH,00H  ; 0=print, 1=initialize port, 2=read status to AH
        PUSH    DX
        MOV     DX,0    ; DX specifies printer 0 (LPT1:)
        INT     17H     ; BIOS used instead of DOS because DOS sends
        POP     DX      ; CR/LF's in the middle of the bit-plots
        TEST    AH,29h  ; check for timeout or errors or out-of-paper
        POP     AX
        JNZ     ERRET
        RET
ERRET:  MOV     AX,00FFH ; Flag for printer foulup
        MOV     SP,BP
        PUSH    AX
        JMP     EDONE   ; special abort
SEND2   ENDP
; EPSON bit plots operate at 480 or 960 dots across the page
; called by ESC K 'low byte';'high byte'
;  i.e.  300 dots would be 256+44 or 012CH
;  This is sent to the EPSON as --> 1b 4b 2c 01
;               or in decimal   --> 27 75 44 1
INDENT  PROC    NEAR
        PUSH    CX      ; 13 SPACES IN TO CENTER
        IF      EPSON
        MOV     CX,13   ; PICTURE ( we've got 320 dots and 480 to work with
INLOP:  MOV     AL,20H  ; 480-320=160 / 6 dots per char. = 26.67 extra
        CALL    SEND2   ; so indent the picture 13 spaces to center
        LOOP    INLOP
; ESC K 64 1  = 256+64=320 bit plot type bytes on the way
        MOV     AL,27   ; SEQUENCE TO SET UP 320
        CALL    SEND2   ; BIT PLOTS IN 480 MODE
        MOV     AL,75   ; OF MX-80
        CALL    SEND2   ; This is the set-up for the small print
        MOV     AL,64
        CALL    SEND2
        MOV     AL,1
        CALL    SEND2
        ENDIF
        IF      CITOH
        MOV     AL,27   ; ESC N = Pica pitch
        CALL    SEND2
        MOV     AL,'N'
        CALL    SEND2
        MOV     CX,20   ; PICTURE ( we've got 320 dots and 640 to work with
INLOP:  MOV     AL,20H  ; 640-320=320 / 8 dots per char. = 40 extra
        CALL    SEND2   ; so indent the picture 13 spaces to center
        LOOP    INLOP
; ESC S 0320 = 320 bit plot type bytes on the way
        MOV     AL,27   ; SEQUENCE TO SET UP 320
        CALL    SEND2   ; BIT PLOTS IN 640 MODE
        MOV     AL,'S'  ; OF CITOH
        CALL    SEND2   ; This is the set-up for the small print
        MOV     AL,'0'  ; Would love to try to use all 640 bits here
        CALL    SEND2   ; but have no way of testing routine.
        MOV     AL,'3'
        CALL    SEND2
        MOV     AL,'2'
        CALL    SEND2
        MOV     AL,'0'
        CALL    SEND2
        ENDIF
        IF      OTHER
        BP1
        ENDIF
        POP     CX
        RET
INDENT  ENDP
; This is indent for LARGE print
; This time we have 400 bit plots to send (200 lines x 2)
; 480-400=80 / 6 = 13.3 extra
INDENT2 PROC    NEAR
        PUSH    CX
        IF      EPSON
        MOV     CX,6    ; so indent 6 character type spaces
INLOP2: MOV     AL,20H
        CALL    SEND2
        LOOP    INLOP2
; ESC 27 K 144 1 = 256+144=400 bit-plots
        MOV     AL,27
        CALL    SEND2
        MOV     AL,75
        CALL    SEND2
        MOV     AL,144
        CALL    SEND2
        MOV     AL,1
        CALL    SEND2
        ENDIF
; 640-400=240 / 8 = 30 EXTRA characters
        IF      CITOH
        MOV     AL,27   ; ESC N = Pica pitch
        CALL    SEND2
        MOV     AL,'N'
        CALL    SEND2
        MOV     CX,15   ; PICTURE ( we've got 400 dots and 640 to work with
INLOP2: MOV     AL,20H  ; 640-400 / 8 dots per char. = 30 extra
        CALL    SEND2   ; so indent the picture 15 spaces to center
        LOOP    INLOP2
; ESC S 0400 = 400 bit plot type bytes on the way
        MOV     AL,27   ; SEQUENCE TO SET UP 400
        CALL    SEND2   ; BIT PLOTS IN 640 MODE
        MOV     AL,'S'  ; OF CITOH
        CALL    SEND2
        MOV     AL,'0'
        CALL    SEND2
        MOV     AL,'4'
        CALL    SEND2
        MOV     AL,'0'
        CALL    SEND2
        MOV     AL,'0'
        CALL    SEND2
        ENDIF
        IF      OTHER
        BP2
        ENDIF
        POP     CX
        RET
INDENT2 ENDP
TST4    PROC    NEAR    ;  This routine builds ONE bit plot byte
        MOV     AX,80   ;  by testing a dot with the mask sent
        SUB     AX,CX   ;  from MLOOP.
        MOV     SI,AX   ;  First it does the ODD row then the EVEN
        MOV     AH,0    ;  since alternate lines are offset 2000h
        MOV     AL,[SI] ;  from each other in memory.
        AND     AL,DL   ;  DL has the mask
        CMP     AL,0    ;  SI the location
        JZ      NO7     ;  AL the memory byte
        CALL    SET7    ;  AH is the byte being built
NO7:    ADD     SI,80   ; +80 gets us from say 0,0 to 2,0
        MOV     AL,[SI] ; get the memory byte ( 4 dots )
        AND     AL,DL   ; get rid of dots we aren't testing now
        CMP     AL,0    ; see if its COLOR 0
        JZ      NO5     ; if yes, go on
        CALL    SET5    ; otherwise set that bit
NO5:    ADD     SI,80   ; continue 7 5 3 1
        MOV     AL,[SI]
        AND     AL,DL
        CMP     AL,0
        JZ      NO3
        CALL    SET3
NO3:    ADD     SI,80
        MOV     AL,[SI]
        AND     AL,DL
        CMP     AL,0
        JZ      NO1
        CALL    SET1
NO1:    PUSH    AX
        MOV     AX,80
        SUB     AX,CX   ; CX counts our screen position
        ADD     AX,2000H ; add 2000h for the EVEN rows
        MOV     SI,AX   ; with seg set to B800h we can use SI like an
        POP     AX      ; array pointer ( AH has our byte so don't lose )
        MOV     AL,[SI] ; and continue with the same idea for 6 4 2 0
        AND     AL,DL
        CMP     AL,0
        JZ      NO6
        CALL    SET6
NO6:    ADD     SI,80
        MOV     AL,[SI]
        AND     AL,DL
        CMP     AL,0
        JZ      NO4
        CALL    SET4
NO4:    ADD     SI,80
        MOV     AL,[SI]
        AND     AL,DL
        CMP     AL,0
        JZ      NO2
        CALL    SET2
NO2:    ADD     SI,80
        MOV     AL,[SI]
        AND     AL,DL
        CMP     AL,0
        JZ      NO0
        CALL    SET0
NO0:    RET
; where's my Z80 now
; reverse this table if your bit plots use bit 0 for the top wire
        IF      BIT7
SET7:   OR      AH,80H  ; top wire - bit 7
        RET
SET6:   OR      AH,40H  ; bit 6
        RET
SET5:   OR      AH,20H  ; bit 5
        RET
SET4:   OR      AH,10H  ; bit 4
        RET
SET3:   OR      AH,08H  ; bit 3
        RET
SET2:   OR      AH,04H  ; bit 2
        RET
SET1:   OR      AH,02H  ; bit 1
        RET
SET0:   OR      AH,01H  ; bit 0
        RET
        ENDIF
        IF      BIT0
SET7:   OR      AH,01H  ; top wire - bit 0
        RET
SET6:   OR      AH,02H  ; bit 1
        RET
SET5:   OR      AH,04H  ; bit 2
        RET
SET4:   OR      AH,08H  ; bit 3
        RET
SET3:   OR      AH,10H  ; bit 4
        RET
SET2:   OR      AH,20H  ; bit 5
        RET
SET1:   OR      AH,40H  ; bit 6
        RET
SET0:   OR      AH,80H  ; bit 7
        RET
        ENDIF
TST4    ENDP
; This routine pre-scans a line to see if in fact there are any bit
; plots to send. The main routine will keep sending bytes here
; If a whole line of 0's are sent we avoid going through the
; set-up for bit-plot (i.e. slower movement) graphics when a CR/LF
; would take care of everything.
; If there IS something to send, PTFLAG is set, the current line
; position is set to 0, bit-plot is init., and bits are really sent to printer.
SEND    PROC    NEAR
        PUSH    AX      ; save the character
        PUSH    DS      ; DS saved cause it points to lines
        MOV     AX,CS   ; set seg for here
        MOV     DS,AX   ; This was some of my first stuff with the 8088
        POP     AX      ; and I see some needless complexity here now
        MOV     CS:DSTOR,AX     ; but it works and if I mess with it
        CMP     CS:PTFLAG,0FFH  ; its back to DEBUG.
        JNZ     NOSEND  ; if PTFLAG isn't FFh we are still scanning
        POP     AX      ; else get the char. in AL and print it
        CALL    SEND2   ; this is the real out to printer routine
        JMP     SHORT NOSET ; restore DS and return
NOSEND: POP     AX      ; This is the SCAN routine
        CMP     AL,0    ; get the char. > test for 0 > if so reset and go back
        JZ      NOSET
        MOV     AL,0FFH ; if <> 0
        MOV     CS:PTFLAG,AL ; set PTFLAG to go
        POP     AX      ;DISCARD RETURN
        CMP     CS:ONEOR2,1 ; check which (small or LARGE)
        JNZ     TWO     ; indent 6 or 13 depending on which routine
        CALL    INDENT  ; indent also sets up bit-plot mode
        CALL    NOSET   ; NOSET will restore DS to right pos.
        JMP     MAIN    ; and do the line for real.
TWO:    CALL    INDENT2 ; init. for LARGE
        MOV     SI,CS:WHERESI   ; SI set back to start of line
        CALL    NOSET   ; get right DS
        JMP     MAIN2A  ; back to beginning
NOSET:  PUSH    AX      ; routine to restore DS
        MOV     AX,CS:DSTOR
        MOV     DS,AX
        POP     AX
        RET
SEND    ENDP
LFCR    PROC    NEAR    ; send a regular CR/LF combo
        MOV     AL,13
        CALL    SEND2
        MOV     AL,10
        CALL    SEND2
        MOV     AX,0
        MOV     CS:PTFLAG,AL ; reset PTFLAG for next line
        MOV     AX,CS:DSTOR  ; restore DS
        MOV     DS,AX
        RET             ; onward
LFCR    ENDP
BREAK?  PROC    NEAR    ; Test for early exit
        PUSH    AX      ; don't lose any regs. here
        PUSH    DX
        MOV     AH,06H  ; call direct keyboard io (constat)
        MOV     DL,0FFH ; if we got a char. go check it.
        INT     21H
        JNZ     GOBACK? ; if zero flag clear we have a character
BCONT:  POP     DX      ; no char. return
        POP     AX
        RET
GOBACK?:
        CMP     AL,1BH  ; ESC
        JZ      BACK    ; so go back, else return
        JMP     SHORT BCONT     ; no ESC exit
BACK:   POP     DX      ; ESC exit This doesn't check for Ctrl-Break
        POP     AX      ; so if it is hit we save it for the caller to handle
        POP     AX      ;DISCARD RETURN
        JMP     DONE    ; and go back to orig. caller
BREAK?  ENDP
FLIPFLOP        PROC NEAR       ; This creates different combinations
        PUSH    CX      ; of a box of four bit-plot dots for one color dot.
        PUSH    BX      ; Don't lose any variables or loop counters
        PUSH    AX
        MOV     CL,2    ; AL has present bit-plot finished byte
        MOV     BX,0
        AND     AL,3    ; 00000011b
        CALL    FLIP
        ROR     BL,CL   ; 11000000b
        POP     AX      ; basically rotate bits around for
        PUSH    AX      ; o x
        CALL    R2      ; x o   color 1
        AND     AL,3    ; and
        CALL    FLIP    ; o x
        ROR     BL,CL   ; o x   color 2
        POP     AX      ; instead of
        PUSH    AX      ; x o
        CALL    R4      ; x o   color 1
        AND     AL,3    ; and
        CALL    FLIP    ; o x
        ROR     BL,CL   ; o x   color 2
        POP     AX      ; which aren't to convincing as
        CALL    R6      ; two different colors
        AND     AL,3
        CALL    FLIP
        ROR     BL,CL
        MOV     AL,BL
        POP     BX
        POP     CX
        RET
R6:     ROR     AL,CL
R4:     ROR     AL,CL
R2:     ROR     AL,CL
        RET
FLIP:   CMP     AL,3    ; make sure there are two dots for color 1 and 2
        JNZ     FLIP2
        OR      BL,3
FLIP2:  CMP     AL,2
        JNZ     FLIP3
        OR      BL,1
FLIP3:  CMP     AL,1
        JNZ     FLIP4
        OR      BL,1
FLIP4:  RET
FLIPFLOP        ENDP
REVERSE PROC    NEAR    ; take AL and make bit 0 bit 7 , 1 - 6, etc
        PUSH    DX      ; Save our counters and masks
        PUSH    CX
        MOV     DL,01H  ; 00000001B
        MOV     DH,80H  ; 10000000B
        MOV     AH,00H  ; start out blank
        MOV     CX,8    ; set counter for 8 times through
REV1:   TEST    AL,DL   ; see if bit is set
        JZ      REV2    ; if not skip next step
        OR      AH,DH   ; else set bit in AH
REV2:   SHL     DL,1    ; shift left test bit
        SHR     DH,1    ; shift right mask bit (pad other bits with 0)
        LOOP    REV1    ; do this 8 times
        MOV     AL,AH   ; and we have a reversed character.
        POP     CX      ; get back these
        POP     DX
        RET             ; and back to caller
REVERSE ENDP
LAST    DW      0
CSEG    ENDS
        END     INIT
