*
*   File Name:  SPINWR:S
*
*                Spinwriter 5510/20
*               Multi-purpose Driver
*                   2.0 (mod 1)
*
*      Written by Steve and Joe Maguire  July, 1979
*                 P.O. Box 3742 DT
*                 Anchorage, AK 99510
*
*     *** NEW IMPROVED VERSION! (updated Aug 1980) ***
*
*           * with bi-directional printing *
*           * includes word processor mode *
*              * with space averaging  *
*
*    Equates:
*
*    Conditions
*
TRUE EQU 00001H
FALSE EQU 0FFFFH
*
BUSY EQU 0FFFFH
DONE EQU 00000H
*
*    Characters
*
CHNULL EQU 0  Null character
CHDEL EQU 7FH  Delete character
CHCR EQU 0DH  Carriage return
CHLF EQU 0AH  Linefeed
CHFF EQU 0CH  Formfeed
CHFLE EQU 1  Forced-line-end (no space averaging)
CHNOLF EQU 0FFH  No linefeed character
CHESC EQU 1BH  Escape character
CHSP EQU 20H  A space
CHBS EQU 8  Backspace on the Spinwriter
CHWS EQU 7EH  A tilde ("~")
CHCRT EQU 5EH  A caret ("^")
CHBEL EQU 7  Printer alarm (bell)
*
*    Control/Status Codes
*
CSDS EQU 007  device status request
CSWPM EQU 100  word processor mode
CSCPS EQU 101  multiple form feeds
CSSPS EQU 102  set page size
CSSHI EQU 103  set horz. inc.
CSSVI EQU 104  set vert. inc.
CSCB EQU 105  cancel buffer
*
*    I/O Port Assignments
*        IOSSP - serial status port
*        IOSDP - serial data port
*
IOSSP EQU 0F8H
IOSDP EQU 0F9H
IOSCS EQU 020H  clear to send (when 1) See note:
IOSBE EQU 080H  transmitter buffer empty (when 1)
*
* Note: Spinwriter connected to Reverse Channel
*
*     Initial Settings
*
IVI EQU 08  init. vert. inc. (6 lpi)
IHI EQU 12  init. horz. inc. (10 cpi)
IPZ EQU 60  init. page size
IFZ EQU 66  init. form size
*
*  The program origin
*
 ORG 07800H
*
*  The Device Header
*
DTRB DW 0  no reads
DTRNB DW 0
DTRLB DW 0
DTWBR DW WBLOCK  write a block of 1
DTWB DW WBLOCK  write a block of 1
DTREW DW DUMMY  rewind supported for BASIC
DTEOF DW EOF  end-file
DTCLO DW CLOSE  the close operation
DTSEK DW 0  seek not supported
DTCTL DW CTRL  control/status call
DTBLK DW 1  block size must be 1
DTITO DB 1  must be 1
DTINI DW INIT
DUMMY RET .
*
*  CTRL  process the Control/Status calls
*     7= Special Status Read (see 9-3 PTDOS manual)
*
*     CSDS  return:  DE=0  ==> printer driver BUSY
*                    DE=1  ==> printer driver DONE
*                    A = non zero always
*               If:  A =0  ==> printer not turned on
*                                (not used in this driver)
*
* 100 - Word Processor Mode
*
*     CSWPM - entry:   E =1  ==> word processor mode
*                      E =0  ==> not word processor mode
*             return:  A =1  ==> was in word processor mode
*                      A =0  ==> was not in WPM mode
*
*     (when the driver is initialized, the WPM is off)
*
* 101 - Multiple form-feeds
*
*     CSCPS - entry:   E = new formfeed count
*             return:  A = old value
*
* 102 - Set Page Size
*
*     CSSPS - entry:   D = new page size
*                      E = new form size
*             return:  D = old page size
*                      E = old form size
*
* 103 - Set Horizontal increment
*
*     CSSHI - entry:   E =  ew horizontal increment in 1/120 in.
*             return:  A = the old increment
*
*     (the initial value is set to 12, i.e., to 10  chars./inch)
*
* 104 - Set Vertical increment
*
*     CSSVI - entry:   E = new vertical increment in 1/48 in.
*             return:  A = the old increment
*
*     (the initial value is set to 8, i.e., 6 lines/inch)
*
* 105 - Driver Reset
*
*     CSCB - entry:   none
*            return:  none
*
CTRL CPI CSDS  Are we checking the device status?
 JNZ CTRL1
 CALL TASK  Let's see what drivers' doing
 CALL TI  Is the print driver's task done?
 MVI A,1
 JZ CTRL0  Zero says print task is DONE
 LXI D,0  Print driver is not done
 RET
*
CTRL0 LXI D,1  Print driver is ready
 RET
*
CTRL1 CPI CSWPM  Change WPM mode?
 JNZ CTRL2
 LXI H,MODE  Get mode byte
 MOV A,M  Old mode value
 MOV M,E  Store the new value
 RET
*
CTRL2 CPI CSCPS  Changing formfeeds?
 JNZ CTRL3
 LXI H,PERFS
 MOV A,M  Save old value
 MOV M,E  Move in the new
 RET
*
CTRL3 CPI CSSPS  Changing the page size?
 JNZ CTRL4
 MOV A,E  Move new form size to A
 CMP D  Page size cannot exceed form size
 JC CTRLE
 LXI H,FORM
 MOV A,M
 MOV M,E  Move in new form size
 MOV E,A  Save old form size
 LXI H,PAGE
 MOV A,M
 MOV M,D  Move in new page size
 MOV D,A  Save old page size
 RET
*
CTRL4 CPI CSSHI  Resetting the Horizontal Increment?
 JNZ CTRL5
 MOV A,E  Test for largest HMI
 CPI 16
 JNC CTRLE  Oops! too high for the Spinwriter
 LXI H,NHI  Point to HMI byte
 JMP CMI
*
CTRL5 CPI CSSVI  Resetting the Vertical increment?
 JNZ CTRL6
 MOV A,E  Check for illegal value
 CPI 17
 JNC CTRLE  Error if illegal
 LXI H,NVI  Point to the VMI byte
*
CMI MOV A,M  A must have the old increment value
 MOV M,E  Set flag with new value
 INX H
 MOV M,E  Replace old value with new
 RET
*
CTRL6 CPI CSCB  Cancel printing?
 JNZ CTRLE  Anything else is an error
 MVI A,TRUE
 STA CANCEL  Set cancel-buffer flag
 RET
*
CTRLE CALL TOD  See if printer ready
 JNZ CTRLE
 MVI A,CHBEL  Move in beep character
 CALL SOB
 CALL 0BCA7H  PTDOS error routine
 DB 17H
*
*   EOF -- write and end-file on the printer
*          Entry:  HL = block address
*                  DE = block size
*
EOF CALL CANTST  If buffers are cleared, exit
 RZ
 CALL WBLOCK  Else, write end-of-file mark
 RET
*
*  CLOSE -- close the printer driver
*           Write kut entire buffer if it is not canceled
*
CLOSE MVI A,FALSE  Be sure buffer is completely printed
 STA CANCEL
 CALL WAIT  Now, print it
 MVI A,CHCR  Put an extra CR to make it look pretty
 CALL SIB  BUSY says a character is ready
 CALL WAIT  Print it now
 RET
*
*   Task and Yield -- A Software interrupt:
*
*      When operating in the Word Processing Mode, the
*   driver must periodically give up control.  It must
*   keep track of what it was doing.  It does this by
*   switching stacks with the calling program through the
*   Task and Yield routines.  When called again,
*   it does just the opposite.  (This process is sometimes
*   called Spooling)
*
*   Task - this saves the state of the calling program and
*          restores the driver to EXACTLY as it was when it
*          last exited.
*
TASK PUSH B  Save the state of the calling program
 PUSH D
 PUSH H
 PUSH PSW
 LXI H,0  Compute the stack address
 DAD SP
 SHLD PTSTK  Now, save the value for future use
*
 LHLD TSKSTK  Restore the print driver registers
 SPHL .  Get the stack address
 POP PSW
 POP H
 POP D
 POP B
 RET .  Continue where the driver was last interrupted
*
*   Yield - this saves the state of the print driver and
*           restores the calling program to EXACTLY as it
*           was when it called.
*
YIELD PUSH B  Save the integrity of the driver
 PUSH D
 PUSH H
 PUSH PSW
 LXI H,0  Compute the stack address
 DAD SP
 SHLD TSKSTK  We mustn't lose it
*
 LHLD PTSTK  Restore the state of the calling program
 SPHL .  Get the stack address
 POP PSW  Now, do the restoring
 POP H
 POP D
 POP B
 RET .  Go to where "TASK" was called originally
*
*   WBLOCK -- write a block of 1 character
*             HL = address of block
*             DE = block size
*
*   There are two modes of operation.  They are:
*
*   1)  Word processing mode (mainly for WordWizard)
*       In this mode, the driver returns to the calling
*       program in 1/10 of a second whether or not it has
*       finished a print operation.
*
*       *  A C/R automatically issues a line feed.
*       *  Line feeds are absorbed.
*       *  Space-averaging is done.
*
*       In the word processing mode, all lines except those
*       ending with a 01H (an inverted "L") are space-
*       averaged.  A tilde (~) is used to indicate a
*       mandatory double space.  A caret ("^") is used to
*       indicate a mandatory absolute space.
*
*       *  Efficient logic-seeking bi-directional printing.
*       *  The driver prints all non-special control codes
*          as question marks.
*
*   2)  Non-Word processing mode (default operation)
*       *  In this mode, the driver finishes the complete
*          print operation before returning to the calling
*          program.
*       *  A tilde (~) is printed as a tilde - not a blank.
*       *  A C/R is not automatically followed by a LF.
*       *  Line feeds are not absorbed.
*       *  Efficient logic-seeking bi-directional printing.
*
WBLOCK CALL TMODE  Are we in the word processing mode?
 JNZ WBLK1  Yes, so go process accordingly
*
*  Here, we are in the NON Word Processing Mode.
*
WBLK0  CALL TI  Wait for printer task to finish
 JZ WBLK2  DONE, send next character
 CALL TASK  Keep printing
 JMP WBLK0  Loop back
*
*  Here, we are in the Word Processing Mode.
*
WBLK1 CALL TI  Test the input status
 JNZ WBLRET  Return if status is BUSY
*
WBLK2 MOV A,M  Get a character
 CALL SIB  Set input busy for printing
 CALL TASK  Go do it now
*
*  Return to WBLOCK caller with a normal return
*
WBLRET XTHL
 INX H
 INX H
 INX H
 XTHL
 LXI H,0  No read
 RET
*
*  SERVER  this is the main loop of the driver,  It
*          waits for PTDOS code to set the 'STATUS'
*          to BUSY indicating that there is a character
*          ready to be processed.  It then processes
*          the character and sends the proper info to the
*          printing mechanism.
*
SERVER MVI A,CHCR  Send out a CR to reset the printer
 CALL PUTC
 MVI A,TRUE  Set the line feed flag to TRUE
 STA LFFG
*
SERV0 CALL GETC  Get a character from PTDOS
 CALL PROCESS  Print the character
 JMP SERV0  Loop forever
*
*  PROCESS  This processes the character.  Some characters
*           are not sent to the printer mechanism; they
*           are handled specially
*           A = character to be processed
*
PROCESS MOV B,A  Save character for later
 CPI CHCR  A carriage return?
*
 JZ PRINT  If so, print out the buffer
*
 CPI CHLF  A line feed?
 JZ LFEED  Process the line feed
 CPI CHFF  A form feed?
 JZ FFEED  Do the form feed
 CPI CHNOLF  No linefeed next time?
 JZ NOLFD  If not, go say so
 CPI CHFLE  Forced end of line?
 JZ SPCAV  Go process if so
 CPI CHDEL  No deletes please!
 RZ
 CPI CHNULL  Ignore nulls
 RZ
 CPI 20H  Any control characters?
 JNC PRO2  No, put character in buffer
 MVI B,'?'  Oddballs get a ?
*
PRO2 LDA EOLINE  Increment end of line
 INR A
 STA EOLINE
 LHLD TXTPT  Get text pointer positinn
 MOV M,B  *** Character in B ***
 INX H  Increment it
 MVI M,0  Store end mark
 SHLD TXTPT  Store pointer
 RET .  Go back now
*
LFEED CALL TMODE  Are we in WPM?
 RNZ .  Yes, so ignore linefeed
*
LFD1 LDA LFFG  Do we print the linefeed?
 CPI CHNOLF  Let's check
 MVI A,0  Only one LF skip per CHNOLF
 STA LFFG  Next time, we print it
 RZ .  Z=0 says no, so exit
 MVI A,CHLF  Now, print 1 linefeed
 CALL PUTC
 LDA PAGE  How many lines left on current page?
 DCR A  Let's see
 STA PAGE
 RNZ .  If there is a line left we're okay
 LDA FORM  Oops!  No more lines left
 STA PAGE  Set number of lines to a full page
 RET
*
* FFEED  In the WPM mode, the remaining lines in the current
*        page are printed out.  If the driver is in the non-
*        WPM a simple formfeed is printed.
*
FFEED CALL TMODE  Word processing mode?
 JNZ FFWPM  Sure enough
*
 MVI A,CHFF  Get formfeed character
 CALL PUTC  Print it
 JMP FFBOTH
*
*  We are in the WPM, therefore we print the formfeed line by
*  line.  If already at top of form, don't do anything.
*
FFWPM LDA PAGE  Lines left on page
 MOV B,A
 LDA FORM  Lines per page
 CMP B
 JZ FFBOTH  If the're the same, formfeed complete
 CALL LFD1  Print a blank line
 JMP FFWPM  Do it
*
FFBOTH LDA FORM  Set for top of form
 STA PAGE
 XRA A  Clear No linefeed flag
 STA LFFG
 RET
*
*  NOLFD  set printing so that no linefeed occurs.  This lets
*         us do over-striking and underlining.
*
NOLFD STA LFFG
 RET
*
*  SPCAV  this routine informs the word processing mode about
*         space averaging.
*         Entry: A = 0  ==> space average
*         A = anything else  ==> don't space average
*
SPCAV STA SPACE
 RET
*
*  PRINT  This routine prints the entire buffer.
*
*  The next routine strips leading and lagging blanks from the
*  buffer before sending them out the serial port.  This speeds
*  throughput and is especially noticed with low baud rates.
*
*  Set the new horizontal/vertical motion indexes if necessary
*  before stripping blanks
*
PRINT LDA NHI  See if new Horizontal motion index
 ORA A  If zero, no change
 JZ SVMI
*
 CALL NEWMI  Let's change it
 XRA A  Re-initialize these bytes
 STA NHI  New Horizontal motion index
 STA HDPOS  We must print forward
*
 MVI A,CHCR  Carriage return ensures proper head position
 CALL PUTC
*
*  Check vertical motion index
*
SVMI LDA NVI  See if new Vertical motion index
 ORA A  Same as above
 JZ P0
 ADI 0FH  Add bias for VMI
 CALL NEWMI  Go process it
 XRA A
 STA NVI
*
*  Strip all leading blanks
*
P0 LXI H,BUFFER+1  Point to 1st char positinn
*
 MVI D,0  Blanks counter
 MVI A,CHSP  This is a blank
*
P1 CMP M  Is the character a blank?
 JNZ P2  No, so stop counting
 INR D  Keep counting, maybe more
 INX H
 JMP P1
*
P2 DCX H
 MVI M,0  Put in the marker
 MOV A,D
 STA BOLINE  Store the beginning of line
*
*  Strip the lagging blanks
*
 LHLD TXTPT  Point to last buffer item
 LDA EOLINE  get character count
 MOV D,A
 MVI A,CHSP
*
P3 DCX H
 DCR D  D is the blank counter
 CMP M  Is the character a blank?
 JZ P3  Yes, keep counting
*
 XRA A  Is the line only blanks and/or <CR> ?
 CMP M  If so, exit right away with an LF if necessary
 JZ PRLN4
*
P4 INX H  Back up the pointer
 MVI M,0  Store end marker
 MOV A,D
 STA EOLINE  Store new count
 SHLD TXTPT  Store new buffer end
*
*
*  Now, calculate the number of blanks and gaps.
*  Next, calculate the space average and store it
*  in an array behind the buffer.
*
*  Upon completion:  HL = old line pointer
*                    DE = new line pointer
*                    B  = number of spaces
*                    C  = number of gaps
*
AVG CALL TMODE  Word processing mode?
 JZ DRCTN
 LDA SPACE  Do we space average?
 ORA A
 JNZ DRCTN  Non-zero say no!
*
SPCGAP LDA BOLINE  Get location of 1st non-blank
 MOV E,A  Calculate the offset
 XRA A
 MOV D,A
 LXI H,BUFFER  Point to the character buffer
 DAD D  Add offset to get address of the old line
 INX H
 MOV E,L  Set new-line pointer equal to the old
 MOV D,H
 MOV B,A  Set number of spaces to zero
 MOV C,A  Set number of gaps, also
*
*  Start counting the gaps and spaces
*
SPC0 MOV A,M  Get the character
 CPI CHSP  Is it a blank?
 JNZ SPC2  No, go check for something else
*
SPC1 INR B  Yes, add one to blank count
 INX H  Bump buffer pointer
 MOV A,M  We must skip over extra blanks
*
*  Skipping over extra blanks "squeezes" the line
*
 CPI CHSP  Is this character a blank?
 JZ SPC1  Yes, so skip it
 DCX H  Bump pointer back to last space
 INR C  Add one to the GAP count
 MVI A,CHSP  Put in the blank
*
SPC2 CPI CHWS  Is it a tilde?
 JNZ SPC3  No
 INR B  Yes, increment the blank count
 INR C  It also counts as a gap
 MVI A,CHSP  It is printed as a blank
*
SPC3 STAX D  Store character in the new line
 INX D  Bump the new line pointer
 INX H  Bump the old line pointer
 ORA A  Are we done, a NOP says yes
 JNZ SPC0  No, keep counting
 XCHG
 DCX H  Back up over the NOP
 SHLD TXTPT  Save this location
 CALL YIELD  Break for lunch
*
*  The new line has been made; the gaps counted; spaces too
*
 MOV A,C  Store the number of gaps
 STA GAPS
 CMP B  If spaces & gaps are equal
 JZ NOAVG  no averaging required
*
*  Calculate the total number of 1/120" increments per blank
*  Number = No. spaces * HMI
*
INCRMT LXI H,0  Initialize the answer
 LDA HMI  Get the HMI
 MOV E,A
 MVI D,0
 MOV A,B  Get number of spaces
*
INC0 DAD D  Multiple add
 DCR A  Done?
 JNZ INC0  Nope, keep going
*
*  Number of increments is in HL
*
INC1 XCHG .  Now it's in DE
 LHLD TXTPT  Get the array address
 INX H
 LDA GAPS  Dimension Array(Gaps)
*
INC2 MVI M,0  Zero the array
 INX H
 DCR A  Done zeroing?
 JNZ INC2  Not yet, keep going
*
*  Fill the array with space-averaging data
*
FILL LDA GAPS  Get length of array
 MOV B,A  Save it
 LHLD TXTPT  Get array address minus 1
*
FL1 INX H  Add 1 to get to proper element
 MOV A,M  Add 1 increment to the element
 INR A
 MOV M,A  Store new value
 DCX D  Array filled?
 MOV A,E
 ORA D  Let's see
 JZ DRCTN  Yes, done.  Go calculate direction
 DCR B  At end of array?
 JNZ FL1  No, go to next element
 JMP FILL  At end.  Re-start at the beginning
*
*  Determine whether to print forward or backward
*
*   Calculate most efficient direction to print (logic-seeking)
*   (NOTE:  because of a bug in the internal tabbing routine
*   of the Spinwriter, direct tabbing is not allowed if you
*   change the horizontal motion index.  Since this driver
*   is capable of motion index changes, it MUST print blanks
*   to offset the columns properly.  The bug with the Spinwriter
*   is that it reads its panel switches when calculating the
*   proper column.  It does not use the setting sent by the
*   driver)  (G9CUR software package)
*
DRCTN LDA HDPOS  Get print head position
 PUSH PSW  Save it
 LXI H,BOLINE  Point to beginning of line
 SUB M  Find the difference
 CM ABSVAL  Take the absolute value
 MOV B,A  Save it in B
 INX H  Now, get the end of line
 POP PSW  Get head position back
 SUB M  Find the difference
 CM ABSVAL  Take the absolute value
 SUB B  Compare distance to BOL and EOL
 JP MFWRD  If plus, forward is faster
*
*  Print backwards
*
MBWRD MVI H,CHSP  A space to offset the column
 MVI L,CHBS  A backspace to offset the column
 MVI D,'<'  Print backwards
 LDA EOLINE  Get end of line
 CALL ALIGN  Go process the data
 LDA BOLINE  Position of 1st buffer character
 ORA A  Is it in the zero position?
 JZ MB2  We can't TAB left of margin
 DCR A
*
MB2 STA HDPOS  Update head position
 LHLD TXTPT  Point the end of buffer
*
 PUSH H  Save this character pointer
 MVI D,0  Now, calculate the end of location ARRAY
 LDA GAPS  Get the number of elements
 MOV E,A
 INX H
 DAD D  Add add it to the location of the first element
 SHLD TXTPT
 POP H
*
 LXI D,-1  Set up to read buffer backwards
 JMP PRLINE  Print the buffer
*
*   Print forward
*
MFWRD MVI H,CHBS  Set the data
 MVI L,CHSP
 MVI D,'>'  Print forward
 LDA BOLINE  Get beginning of line
 CALL ALIGN
 LDA EOLINE  Get last character position
 INR A
 STA HDPOS  Save new head position
*
 LXI H,BUFFER  Point to buffer start
 LDA BOLINE  Compute location of 1st non-blank character
 MVI D,0
 MOV E,A
 DAD D
 LXI D,1  Set up to read buffer forward
*
*   Print the buffer
*
PRLINE DAD D  Bump HL
 MOV A,M  Move character in buffer to A
 ORA A  End of buffer?
 JZ PRLN4  Yes, so go clean up
*
 CPI CHWS  Is it a tilde?
 JNZ PRLN  No, so print as is
*
 CALL TMODE  Yes it is a tilde
 JZ PRLN  If in the word processing mode,
*
 MVI A,CHSP  it must be changed to a blank
*
PRLN CPI CHCRT  If it is a caret,
 JNZ PRLN0
*
 CALL TMODE  Check and see it space-averaging is switched on
 MVI A,CHCRT  If not, print the caret,
 JZ PRLN0
*
 MVI A,CHSP  Otherwise, print a non-spreaded blank.
 JMP PRLN3  Bypass the space-averager
*
PRLN0 CPI CHSP  Is it a blank?
 JNZ PRLN3
 LDA SPACE  Is space averaging switched off?
 ORA A  Zero says no
 MVI A,CHSP
 JNZ PRLN3  Averaging switched off
*
 CALL TMODE  In the word processing mode?
 MVI A,CHSP  Z flag set means no
 JZ PRLN3
*
*  This routine prints a "spreaded" blank or, in other words,
*  it prints an average space.  It gets its data from an array
*  of pre-calculated values.  These values are the number of
*  1/120" increments for the current space.  If the current line
*  is being printed forward, then these values are read from the
*  start of the array to the end.  If the current line is being
*  printed backwards, the array will be read from end to start.
*  This allows for correct overstriking, etc. Doing the space-
*  averaging by this method results in shorter and faster code
*  plus fewer storage bytes.
*
 PUSH H  Save these values
 LHLD TXTPT  Point to the array
 DAD D  Add the offset
 SHLD TXTPT  Bump pointer for next time
 LDA HMI  Get the current HMI value
 MOV C,A
 MOV A,M  Get the data from the array
*
*  This routine prints whole blanks as whole blanks.  That is,
*  if there are 12 increments for the current space and the
*  Horizontal motion index is 11, there is one whole blank and
*  one remainder.
*
PRLN1 SUB C  Are we out of whole blanks?
 JM PRCDE  Negative result say Yes
 PUSH PSW  No, print a blank
 MVI A,CHSP
 CALL PUTC
 POP PSW
 JZ PREXT  No partial blanks either, so exit
 JMP PRLN1  Keep blanking
*
PRCDE ADD C  Get correct remainder
*
*  Now, print the partial blank
*
 CALL NEWMI  Change the HMI
 MVI A,CHSP  Now, print the partial space
 CALL PUTC
 LDA HMI  Get normal pitch spacing back
 CALL NEWMI
*
PREXT POP H  Get the values back
 JMP PRLINE
*
PRLN3 CALL PUTC  Print the character
 JMP PRLINE
*
PRLN4 CALL TMODE  Are we in the WPM?
 JZ CLRBUF  No, so no linefeed
 CALL LFD1  Yes, print a linefeed
 JMP CLRBUF  Go clean up the buffer
*
*  ALIGN  This simulates tabbing buy printing out blanks
*         to the proper column.
*  Entry:  H = BACKSPACE or SPACE  (depends on print direction)
*          L = SPACE or BACKSPACE  (   "    "    "       "    )
*          D = Direction of printing
*          A = BOLINE or EOLINE    (depends on print direction)
*
ALIGN MOV E,A  Save BOLINE/EOLINE
 MVI A,CHESC  Send out "direction" command
 CALL PUTC
 MOV A,D  D has the direction. (i.e., "<" or ">")
 CALL PUTC
*
*  Calculate which way to go to reach the column
*
 LDA HDPOS  Get last head position
 SUB E   Calculate the direction to print
 RZ
 MOV E,A
 MOV C,H  Move in the char to space properly
 JP AL1  Go print offset blanks
*
 CMA .  Get the absolute value
 INR A  The twos complement
 MOV E,A
 MOV C,L  Move in the correct character
*
AL1 MOV A,C  Print the character
 CALL PUTC
 MOV A,E  Are we done?
 DCR A
 RZ .  Yes, return to caller
 MOV E,A  Nope, keep going
 JMP AL1
*
CLRBUF LXI H,BUFFER+1  Clear buffer
 SHLD TXTPT
 XRA A
 STA EOLINE  Reset counters
 STA BOLINE
 STA SPACE
 STA BUFFER+1
 RET
*
*  Used only for setting vertical and horizontal movement
*
NEWMI ADI 40H  Add bias for Spinwriter
 PUSH PSW  Save index
 MVI A,CHESC  Send the escape code
 CALL PUTC
 MVI A,']'  Send the operation code
 CALL PUTC
 POP PSW  Send the index
 CALL PUTC  Go do it
 RET .  All done
*
*  Twos complement routine
*
ABSVAL CMA .  Take the absolute value
 INR A
 RET
*
*   WAIT  wait for the printer driver to completely finish ALL
*         of its printing.  This routine is used only in the
*         non-word processing mode and in closing the driver.
*
WAIT CALL TI  Test the input status
 RZ .  Return when the driver is DONE
 CALL TASK  BUSY now, let it get DONE
 JMP WAIT  keep trying
*
*  PUTC  put a character out to the printer mechanism
*        Entry:  A = the character to be sent
*
PUTC PUSH PSW  Save the character
 CALL CANTST  Character canceled?
 JNZ PUTC0  Nope, so go print it
 POP PSW  Afraid so, ignore it
 RET
*
PUTC0 CALL TOD  Is the printer ready?
 JZ PUTC1  Yes, it is
 CALL YIELD  Not yet, let PTDOS run
 JMP PUTC0
*
PUTC1 POP PSW  Get the character
 ANI 07FH  Mask off the parity
 CALL SOB  Set BUSY and send the character
 RET
*
*  GETC  get a character from the PTDOS task
*        Return:  A = the character sent
*
GETC CALL SID  First set input DONE
*
GETC0 CALL TI  Test input status
 JNZ GETC1  Yes, the character is ready
 CALL YIELD  Let PTDOS run awhile
 JMP GETC0
*
GETC1 LDA DATA  Get character that was passed
 RET
*
*  CANTST - test the cancel flag
*
CANTST LDA CANCEL
 CPI TRUE
 RET
*
*  TMODE - test Word Processor mode
*          set Z if false
*
TMODE MOV B,A
 LDA MODE
 ORA A
 MOV A,B
 RET
*
*  SID  set input DONE.  This sets the input (to the printer
*       driver) status semaphore to the DONE state.  This
*       indicates to the PTDOS task that the printer driver
*       is ready to process another character.
*
SID MVI A,DONE
 STA STATUS
 RET
*
*  SIB  set input BUSY.  This sets the input (to the printer
*       driver) status semaphore to the BUSY state.  This
*       tells the PTDOS task that it cannot send the printer
*       driver a character.  PTDOS code must wait for the
*       semaphore to go DONE.
*
SIB STA DATA  Store character for driver
 MVI A,BUSY
 STA STATUS  make the printer driver BUSY
 RET
*
*  SOB  set output BUSY.  This sets the hardware semaphore
*       to its BUSY state and starts the printer mechanism
*       going.  There is no software complement (sod-set
*       output DONE) because the printer hardware dose this.
*
SOB OUT IOSDP  send out character
 RET
*
*  TI  test input status.  This tests the input (from the
*      PTDOS task) status for DONE and sets the Z flag
*      to 1 for DONE, 0 for BUSY.  Carry flag is always 0.
*
TI LDA STATUS
 CPI DONE
 RET
*
*  TOD  test output for DONE.  This tests the printer
*       mechanism status semaphore for a DONE condition.
*       The Z flag is set to 1 if DONE, 0 if not
*
TOD IN IOSSP  read in status port
 ANI 0A0H  This is the Sol CTS & TBE
 CPI 0A0H
 JNZ TOD1  NZ=DONE
*
 XRA A  Printer ready
 RET
*
TOD1 ORI 1  Printer not ready
 RET
*
NOAVG MVI A,0FFH
 STA SPACE
 JMP DRCTN
*
*   INIT  initialize the driver
*
INIT LXI H,SERVER  Get server's start address
 SHLD IRETADR
 LXI H,ISTKPTR  Get server's stack address
 SHLD TSKSTK
*
 XRA A  Clear A for initializing variables
 STA MODE  Set to non-word processing mode
 STA CANCEL  Cancel mode is false
 STA STATUS  Set input status to DONE
 STA HDPOS  Set initial head pos to left margin
 STA BOLINE  Initialize beginning byte
 STA EOLINE  Initialize end byte
*
 LXI H,0  Clear HL
 SHLD BUFFER  Clean up the buffer
*
 MVI A,IPZ  Set initial page size
 STA PAGE
*
 MVI A,IFZ  Set initial form size
 STA FORM
*
 MVI A,IVI  Set initial vert increment
 STA VMI
 STA NVI  Set flag for change
*
 MVI A,IHI  Set initial horiz increment
 STA HMI
 STA NHI  Set Flag for change
*
 LXI H,BUFFER+1  Set the text pointer
 SHLD TXTPT
*
 CALL TASK
*
 RET
*
*  Storage area
*
STATUS DS 1  print driver BUSY/DONE flag
PTSTK DS 2  PTDOS stack address
TSKSTK DS 2  Driver stack address
MODE DS 1  Mode flag: WPM or non WPM
LFFG DS 1  linefeed-after-CR flag
DATA DS 1  the character storage buffer
CANCEL DS 1  cancel flag
NVI DS 1
VMI DS 1  Vetical Motion Index
NHI DS 1
HMI DS 1  Horizontal Motion Index
HDPOS DS 1  current head position
BOLINE DS 1  beginning of line pointer
EOLINE DS 1  end of line pointer
TXTPT DS 2  buffer pointer
SPACE DS 1  space average on/off
GAPS DS 1  number of gaps in a line
PAGE DS 1  lines on current page
FORM DS 1  lines per page
PERFS DB 0
*
*  STACKS
*
 DS 45        Stack area
ISTKPTR DS 8  init stack address
IRETADR DS 2  init RET addr
*
BUFFER DS 2
 DS 250  Space for character buffer and array
*
MARK EQU $   to see the driver end address
*
 END

