 opt nol
 lib environment
 lib macdefs
 opt lis
 sttl NEC Printer Drivers
 pag
 name necdrvr

 global neccup,necafu,necopn,neccls,necwrt,nectys

*
*   character equates used by drivers
*
bs         equ     $08                 necdbs character
nl         equ     $0A                 new line character
vt         equ     $0C                 vertical tab character
cr         equ     $0D                 carriage necrtn character
xo         equ     $12                 punch on character
sp         equ     $20                 necspc character

 pag
*
*   driver configuration equates
*   Kept as offsets from U stack pointer
*

necpos equ -16
  fdb     0                   requested character position
necold equ -14
  fdb     0                   previous character position
carage equ -12
  fdb     0                   current carage position
necfps equ -10
  fdb     0                   requested form position
nectrc equ -8
   fdb     0                   current nectrc position
necssp equ -6
   fdb     necqbf             necstb stuff pointer
necsfp equ -4
   fdb     necqbf             necstb fetch pointer
necsct equ -2
    fcb     0                  in-buffer necstb count
necbsy equ -1
    fcb     0


*
*   tty information table
*
necflg equ 0
necU  fcb     %00110000           control flag byte

rawnec    equ     %00000001           1 => Raw Mode I/O selected
necnlo    equ     %00010000           1 => Treat CR as new line
neclof    equ     %00100000           1 => issue new line on line overflow
necnop    equ     %01000000           1 => device is already open
necioe    equ     %10000000           1 => permanent I/O error

neclef equ 1
   fcb     6                   left margin, in 12'ths of an inch
necpch equ 2
  fcb     10                  pitch designation, in chars/inch
necdep equ 3
  fcb     6                   depth designation, in lines/inch
necfrm equ 4
   fcb     132                 form length, in 12'ths of an inch
necwid equ 5
  fcb     168                 form width, in 12'ths of an inch
*
*   driver variables and other stuff
*
pitch equ 6
     fcb     12                  character pitch value
depth equ 7
     fcb     8                   line depth value
margin equ 8
    fdb     48                  left margin offset
frmlen equ 10
  fdb     528                 length of standard form
normal equ 12
    fdb     0                   form normalization constant
rtlimt equ 14
  fdb     1686                maximum carriage position
*
NECSIZ    equ     64                  size of strobe buffer, in bytes
necqbf rzb NECSIZ
*
*   necadr descriptions and equates
*

d_lsp      equ     0                   lsp data register offset
d_msp      equ     1                   msp data register offset
cr_a       equ     2                   A-side control register offset
cr_b       equ     3                   B-side control register offset

s_tof      equ     %01000000           top of form necstb
s_rpf      equ     %01100000           right paper feed necstb
s_pfd      equ     %10000000           paper feed necstb
s_car      equ     %10100000           carage motion necstb
s_chr      equ     %11000000           character necstb
s_rst      equ     %11100000           restore necstb

back       equ     %00001000           down or left necstb modifier
*
*   necadr initialization equates
*
c_acr      equ     %00110110           basic A-side register values
c_lift     equ     %00001000                 ribbon lift modifier

c_bcr      equ     %00111110           basic B-side register values
c_irq      equ     %00000001                 irq enable modifier
c_ddr      equ     %00000100                 direction register select
c_stb      equ     %00001000                 Strobe line output
*
*   set up the interface and arm the interrupts
*
necopn     pshs    cc
           seti
 bsr setuy initialize: U=data; Y=NECADR
           lda     necflg,u             get device status flag
           bita    #necnop
           beq     necdop              do the open processing
           lda     #EBSY
           sta     uerror              tag the mistake
           puls    cc,pc
*
*   do the open on the device
*
necdop     ora     #necnop            indicate device is open
           anda    #$7f            insure no I/O error
           sta     necflg,u             stuff into flag byte
           bsr     neclro              clear out all the buffer stuff
*
*   configure the I/O channel
*
           lda     #c_acr              set up a-side data register
           sta     cr_a,y              stuff into control register
           ldb     #c_bcr-c_ddr        select the direction register
           stb     cr_b,y              stuff into control register
           ldb     #!0                 then assign all lines as
           stb     d_msp,y             output lines on b side
           ldb     #c_bcr+c_irq        select b-side data registers
           stb     cr_b,y              by writing control register
 tst 0,y
 tst 1,y
*
*   set up initial values for the variables
*
           jsr     necclc             perform the nectys calculations
           clra                        set positions to zero now
           clrb
           std     carage,u            stuff into carage position slot
           std     nectrc,u             and also into nectrc position
           std     necfps,u            indicate we are at top of form
*
*   lift the ribbon and restore the printer
*
           lda     #c_acr+c_lift+c_irq       then lift ribbon
           sta     cr_a,y              by setting control register
           ldd     #s_rst*256+0            load a restore necstb
           jsr     necstb              send it off to the printer
           puls cc,pc
*
*   do form feed and place printer at left margin
*
neccls bsr setuy initialize: U=data; Y=NECADR
           lda     necflg,u                  get the flag byte
           bita    #necnop                 see if device is open
           beq     necclx                  nope, don't need to close
           jsr     necvrt              issue a form feed
           lda     necflg,u              get status byte
           anda    #!necnop            indicate not open device
           sta     necflg,u
necclx     rts
*
*  setuy - setup U and Y registers
*
setuy ldu #necU point U register to variables
 ldy #NECADR get I/O device address
 rts
*
*   write a buffer in either raw mode or other
*
necwrt bsr setuy initialize: U=data; Y=NECADR
           lda     necflg,u            pick up the tty flag bytes
           bita    #rawnec            check for doing raw-mode I/O
           beq     cooked (bne)             if not, enter cooked processing
*
*   process next raw mode necstb word
*
eatraw     jsr     cpass               get msp of necstb byte
           bmi     necdon                if no more, done with it
           pshs    b                   save byte on the stack
           jsr     cpass               get lsp of necstb byte
           puls    a                   get back msp from stack
           bmi     necdon                if no more, forget it
           jsr     necstb              send strobe off to necadr
           bsr     necbfc             check buffer status
           tst     necflg,u              check for I/O error
           bpl     eatraw
*
*   permanent I/O error while processing
*
necfio     lda     #EIO                return failing status
           sta     uerror
neclro     clr     necbsy,u              clear out busy flag
           clr     necsct,u              zap the buffer count
           ldd     #necqbf             get the buffer address
           std     necsfp,u              stuff fetch pointer
           std     necssp,u              and also the store pointer
           rts
*
*   process the next character from the user buffer
*
steamed    bsr     necdch              process the character
           bsr     necbfc             check for room in the queue
           tst     necflg,u              check for I/O error
           bmi     necfio              if so, terminate the write
cooked     jsr     cpass               get character from buffer
           bpl     steamed             if one exists, put on to cook
necdon     rts
*
*   check necstb buffer for room for more stuff
*
necbfw bsr setuy initialize: U=data; Y=NECADR
           ldb     #TTYOPR             load priority
           jsr     sleep               quiesce the process for now
           puls    cc
necbfc     pshs    cc
           seti                        disable interrupts
           lda     necsct,u              get the buffer count
           cmpa    #NECSIZ/2-8         see if room left in buffer
           bhs     necbfw              nope, go wait some
           puls    cc,pc
*
*   process a character destined for the printer
*
necdch     cmpb    #sp                 check for a necspc character
           bhi     necdtt            if higher, active data character
           beq     necspc               if equal, space character
*
*   decode possible control characters
*
           cmpb    #cr                 check for carage necrtn
           beq     necrtn
           cmpb    #nl                 check for new line
           beq     necnlr
           cmpb    #vt                 check for vertical tab
           beq     necvrt
           cmpb    #bs                 check for necdbs
           beq    necdbs
           cmpb    #xo                 check for X-ON character
           beq    posit
           rts
*
*   data character - do positioning and print character
*
necdtt   andb    #%01111111          check for data bits
           bsr     posit               issue the position necstb
           lslb                        shift character left one bit
           lda     #s_chr              indicate a character necstb
           jsr     necstb              issue a character print strobe
*
*   necspc character just advances horizontal position
*
necspc      ldd     necpos,u            pick up character position
           std     necold,u            store into previous position
           addb    pitch,u               add in the pitch value
           adca    #0                  add carry, if any, into msp
           std     necpos,u            store position into fcb
*
*   check to see if still on form width
*
           ldd     rtlimt,u            pick up rightmost limit
           beq     necexi                if zero, just exit here
           cmpd    necpos,u            compare to character position
           bhs     necexi                if higher, then exit
*
*   line has overflowed - see if we should issue necrtn, etc.
*
           std     necpos,u            reset to right limit
           lda     necflg,u            pick up tty flag byte
           bita    #neclof            test line overflow flag
           beq     necexi                if not, just exit here
*
*   carage necrtn function
*
necrtn     ldd     margin,u              get left margin offset
           std     necpos,u            stuff into carage position
           std     necold,u            also reset previous position
           lda     necflg,u            pick up the tty flag byte
           bita    #necnlo            check for new line option
           beq     necexi
*
*   new line function -- set vertical position offset
*
necnlr    ldb     depth,u               get character depth
           sex                         convert into a 16-bit word
           addd    necfps,u            add into vertical position
           std     necfps,u            stuff back into position
           cmpd    frmlen,u            compare against form length
           blo     necexi                if still on page, leave it
*
*   we are overflowing a page boundary, normalize things
*
           addd    normal,u              add in normalize constant
           std     necfps,u         stuff the form position
 bra necvr2
*
*   vertical tab function sets up new top of form
*
necvrt     ldd     necfps,u          get current form pos
           beq     necvrx          if at top of form, forget it
           ldd     frmlen,u          get form length word
           beq     necnlr         if zero, do a new line
           std     necfps,u         stuff the form position
 ldd margin,u reset margin, too
 std necpos,u set carriage position
 std necold,u set old position, too
necvr2     bsr     posit          issue the position strobe
           clra                        set position to zero
           clrb
           std     necfps,u            stuff into form position
           std     nectrc,u             and stuff into nectrc position
necexi
necvrx     rts
*
*   necdbs function backs up positon
*
necdbs  ldd     necold,u            pick up previous carage position
           std     necpos,u            stuff back into slot
           rts
*
*  negate - negate value in d register then 'and' with "back"
*
negate pshs d
 clra
 clrb
 subd 0,s++
 ora #back
 rts
*
*   posit function issues necstbs to position the carage
*
posit      pshs    a,b                 save data registers on stack
           ldd     necfps,u            pick up forms position
           subd    nectrc,u             compare to nectrc position
           beq     nechpz              if equal, just do horizontal position
           aslb                  multiply quantity by two
           rola
           bpl     necvpz              if positive, do vertical position
 bsr negate make motion downward direction
*
*   perform the vertical position
*
necvpz     ora     #s_pfd              indicate a paper feed necstb
           bsr     necstb              then strobe the printer
           ldd     necfps,u            stuff into file control block
           std     nectrc,u             as current nectrc position
*
*   position the carage
*
nechpz     ldd     necpos,u            get character position
           subd    carage,u            subtract from carage position
           beq     xposit              if zero, just necexi position
           bpl     necdpq             if positive, do the position
 bsr negate indicate leftwards motion
*
*   perform the carage position
*
necdpq    ora     #s_car              set carage motion necstb
           bsr     necstb              send position strobe
           ldd     necpos,u            get character position
           std     carage,u            and indicate new carage position
xposit     puls a,b,pc
*
*   place a necstb word into the strobe buffer
*
necstb     pshs    cc                  save condition flags
           seti
           ldx     necssp,u             get necstb place pointer
           std     0,x++               stuff into the buffer
           cmpx    #necqbf+NECSIZ        check for end of buffer
           bne     necppy             if not, just set pointer
           ldx     #necqbf            point back at the buffer
*
*   store the new output pointer and check for interrupts active
*
necppy    stx     necssp,u             stuff back the pointer
           inc     necsct,u              increment the necstb count
 tst necbsy,u
 bne necsx1
           bsr     neccup              simulate a necadr interrupt
necsx1    puls cc,pc     cc
*
*   take an interrupt from the printer interface
*
neccup lbsr setuy initialize: U=data; Y=NECADR
           clr     necbsy,u              indicate no interrupt pending
           tst     d_lsp,y             clear any pending interrupt
           tst     necsct,u              test the necstb count
           beq     nechco              if zero, no more necstb words
*
*   get the next necstb word from the strobe queue
*
           ldx     necsfp,u             get the necstb input pointer
           ldd     0,x++               pick up the necstb word
           cmpx    #necqbf+NECSIZ        see if at end of buffer
           bne     necpfp             if not, go set pointer
           ldx     #necqbf            point at top of buffer
necpfp     stx     necsfp,u             stuff back into pointer slot
           bsr     nectag              tag the device
           inc     necbsy,u              indicate pending interrupt
*
*   fixemup necstb counts, etc
*
           lda     necsct,u              pick up the necstb count
           deca                        decrement it by one
           sta     necsct,u              stuff back into count field
           beq     nechco              if empty, wakeup
           cmpa    #26                 check for approx 26 necstbs
           bne     nechx1               which is 200 mSec, approx.
nechco     jsr     wakeup              wake up on the necadr
nechx1     rts
*
*   check interrupt processing
*
necafu lbsr setuy initialize: U=data; Y=NECADR
           tst     d_msp,y             clear the interrupt
           clr     necbsy,u
           clr     necsct,u              clear out strobe buffer
           lda     necflg,u              fix up flag bits
           ora     #necioe
           anda    #!necnop            indicate device not open
           sta     necflg,u
*
*   hard issue a restore to the device
*
           ldd     #s_rst*256
           bsr     nectag              send strobe to device
           lda     #c_acr              quiesce the interface
           sta     cr_a,y
           jsr     wakeup             in case task sleeping
           rts
*
*   issue the necstb to the printer
*
nectag     sta     d_msp,y             stuff the data stobe word
           stb     d_lsp,y
           lda     #c_bcr-c_stb        load control necstb
           sta     cr_b,y
           lda     #c_bcr+c_irq        bring necstb high and arm IRQ
           sta     cr_b,y
           rts
*
*   nectys and nectyg processing
*
nectys lbsr setuy initialize: U=data; Y=NECADR
           leax    0,x                 check for ttyset/nectyg
           bne     nectyg
*
*   before altering nectys table, perform limit check on parameters
*
           ldd     usarg1
           cmpa    #6                  check lower pitch limit
           blo     necoop
           cmpa    #24                 check upper pitch limit
           bhi     necoop
           cmpb    #4                  check lower depth limit
           blo     necoop
           cmpb    #32                 check upper depth limit
           bhi     necoop
           std     necflg+2,u
*
*   copy rest of nectys parameters into local table
*
           ldd     usarg0              get first two bytes
           std     necflg,u
           ldd     usarg2              get last user argument
           std     necflg+4,u
*
*   perform calculations on nectys parameters
*
necclc    lda     necfrm,u             pick up the form length
           ldb     #4                  set scale offset value
           mul                         calculate form length, in units
           std     frmlen,u
           lda     neclef,u             pick up the left margin offset
           ldb     #10                 set the units scale value
           mul                         calculate offset, in horiz units
           std     margin,u
           std     necpos,u            provide for a fixed left margin
           std     necold,u
*
*   calculate maximum width parameter
*
           lda     necwid,u            pick up the form width
           ldb     #10                 set scale offset value
           mul                         calculate width, in units
           beq     necstp              if zero, just set the width
           addd    margin,u              add in the left margin value
necstp     std     rtlimt,u
*
*   calculate pitch info from nectys parms
*
           ldd     #120*256+$FF            set up initial pitch constants
necpdv     incb                        increment pitch constant
           suba    necpch,u            subtract off the pitch limit
           bcc     necpdv              loop until divided
           stb     pitch,u               stuff the pitch value
*
*   calculate depth info from nectys parms
*
           ldd     #48*256+$FF             set up initial depth constants
necddv     incb                        increment the quotient value
           suba    necdep,u            subtract out the divisor
           bcc     necddv              loop until divided out
           stb     depth,u               stuff back into depth slot
*
*   calculate form normalization constant
*
           clra                        make depth double precision
           pshs    d                   save depth value on the stack
           ldd     frmlen,u            pick up the form length
necsnm    subd    0,s                 subtract off the depth value
           bcc     necsnm             loop back if not cleared
           addd    0,s++               add back to get normalizer value
           std     normal,u              stuff into form normalizer
           rts
*
*   oops, some value is out of range
*
necoop    lda     #EBDEV              set an error indicating out of range
           sta     uerror              stuff into error indicator
           rts
*
*   for nectyg command, necrtn the information
*
nectyg     ldd     necflg,u            set first two bytes
           std     0,x++
           ldd     necflg+2,u          set second two bytes
           std     0,x++
           ldd     necflg+4,u          set last user argument
           std     0,x++
           rts
