!============================================================!
!                                                            !
!  EDWIN Device Driver for Bitmap Printers                   !
!                                                            !
!       Alan Thomson:  August 18, 1988                       !
!                                                            !
!============================================================!
!
! Rev 001 AET+JGH  Force form feeds between intermediate plots & epson init
!

from Edwin include Device
from Edwin include Icodes
from Edwin include Consts
from Edwin include Iprocs
from Edwin include pattern
from Imp   include Lognames
from Imp   include Heap
from Imp   include TextUtils

!
! Devices
!
const byte Epson      Printer = 1,
             Laserjet   Printer = 2,
             Printronix Printer = 3,
             Versatec   Printer = 4


const integer Max Models = 12
const integer array Models (1:Max Models) =
{EPSON}                     8060, 80120, 10060, 100120,
{LASERJET I}                2686, 2686150, 2686300,
{PRINTRONIX}                300,
{VERSATEC}                  80, 81, 82,
{LASERJET II}               33440

const byte FF = 12, CR = 13, LF = 10, Plot = 5, Escape = 27
const byte Zero = 0, Two = 2
const integer HP Start X = 800, HP Start Y = 1100
own string(255) New Page = ""
own integer Page Number = 1
const string (*) Both = "BOTH"
const string (*) Start = "START"
const string (*) End = "END"

own integer Horizontal Map = 1

record format PointFm(integer x,y)
record format DataFm(record(PointFm) p, record(DataFm) name Next)
own record(DataFm) name First Point == 0
own record(DataFm) name Next Point == 0
own string(80) NAME 

own integer SX = 0, SY = 0, Drawn = False, Colour = 1
own integer LX = 0, LY = 0
own integer WLX = 0, WLY = 0, WHX, WHY
own integer Start X, Start Y 
own integer Shade Mode = Solid
own integer Colour Mode = Or Mode
own integer Res = 0
own integer Scan Bytes
own integer Size Y,Size X, Heap Start, Total Size
own integer Device = 0

own integer NPts = 0


byte map BitMap(integer Y, X)
    result == Byte(HeapStart+(Size X*Y)+X)
end

routine Draw Line(integer TX,TY)
   ! This is algorithm 162 in the Collected Algorithms from CACM.
   ! It computes the code string required to move the pen of a
   ! digital incremental X-Y plotter from an initial point (SX,SY) to
   ! a terminal point (TX,TY) by the "best" approximation to the
   ! straight line between the points. The permitted elemental pen
   ! movement is to an adjacent point in a plane Cartesian point latice,
   ! diagonal moves permitted.

   integer A, B, D, E, F, T, I, XMove, YMove, X, Y
   const short array XCode(1:16) = 0,1,1,1,1,1,0,1,0,-1,-1,-1,-1,-1,0,-1
   const short array YCode(1:16) = 1,1,0,1,0,-1,-1,-1,-1,-1,0,-1,0,1,1,1
   ! PY,PX+PY,PX,PX+PY,PX,PX+NY,NY,PX+NY,NY,NY+NX,NX,NX+NY,NX,NX+PY,PY,NX+PY

   routine Mark
      ! make mark in line buffer - represented as a linear bit string
      byte integer name B
      integer Bit
      if Horizontal Map = 1 start
         B == Bitmap(SX,SY//8)
         if Device = Epson Printer start
            Bit = 16_01 << (SY&7)
         else
            Bit = 16_80 >> (SY&7)
         finish
      else
         B == Bitmap(SY,SX//8)
         Bit = 16_80 >> (SX&7)
      finish
      B = B&(~Bit) if Colour Mode = Replace Mode
      B = B ! Bit if Colour # 0
   end

   ! The following line can output diagnostics as CIF for plotting with ART
!   Oper Message ("L CP; W 0".Itos(SX,1).Itos(SY,1).Itos(TX,1).Itos(TY,1).";")
   Drawn = True
   Mark and return if SX = TX and SY = TY

   if SX < WLX then SX = WLX
   if SY < WLY then SY = WLY
   if TX > WHX then TX = WHX
   if TY > WHY then TY = WHY

   A = TX - SX
   B = TY - SY
   D = A + B
   T = B - A

   I = 0
   if B >= 0 then I = 2
   if D >= 0 then I = I + 2
   if T >= 0 then I = I + 2
   if A >= 0 then I = 8 - I else I = I + 10

   A = -A if A < 0
   B = -B if B < 0
   F = A + B
   D = B - A
   if D >= 0 then T = A and D = -D else T = B
   E = 0

   XMove = XCode(I - 1)
   YMove = YCode(I - 1)
   X = XCode(I)
   Y = YCode(I)
   cycle
      A = D + E
      B = T + E + A
      Mark
      if B >= 0 start
          E = A
          F = F - 2
          SX = SX + X
          SY = SY + Y
      else
          E = E + T
          F = F - 1
          SX = SX + XMove
          SY = SY + YMove
      finish
      exit if F <= 0
   repeat
   Mark
end

routine Test(integer name Low, High)
   integer i
   if Low > High start
      i = Low
      Low = High
      High = i
   finish
end

routine Draw Box(integer LX, LY, UX, UY)
   integer X, Y, Highbyte, High bits, Lowbyte, Low bits, Shade
   integer B, Disp, This Pat
   byte name Thisbyte

   LX = LX + 1
   UX = UX - 1
   return if UX < LX
   ! The following line can output diagnostics as CIF for plotting with ART
!   Oper Message ("L CA; W 0".Itos(LX,1).Itos(LY,1).Itos(UX,1).Itos(UY,1).";")
   Drawn = True
   Shade = Shade Mode<<4
      
   { This is a solid box, outline boxes are filtered out by EDWIN before driver }
   if Horizontal Map = 1 start

      Lowbyte = LY>>3
      Low bits = LY & 7
      Highbyte = UY>>3
      High bits = 7 - (UY & 7)
      for X = LX, 1, UX cycle
         This Pat = Patterns((X&16_F)!Shade)
         ! Special case of Low byte = Y = High Byte, ie. box of < 8 units
         if Low byte = High Byte start
            Thisbyte == BitMap(X,Low byte)
            if Device = Epson Printer start
               B = (16_FF<<Low bits)&255
               B = B>>High bits & B
            else
               B = 16_FF>>Low bits
               B = B<<High bits & B
            finish
            Thisbyte = Thisbyte&(~B) if Colour Mode = Replace Mode
            if Colour # 0 start
               Disp = 16_08!!((Low Byte&1)<<3)
               Thisbyte = Thisbyte!((This Pat>>Disp)&B)
            finish
         else
            ! Special case of Y = Low byte removed from the loop
            Thisbyte == BitMap(X,Low byte)
            if Device = Epson Printer start
               B = (16_FF<<Low bits)&255
            else
               B = 16_FF>>Low bits
            finish
            Thisbyte = Thisbyte&(~B) if Colour Mode = Replace Mode
            if Colour # 0 start
               Disp = 16_08!!((Low Byte&1)<<3)
               Thisbyte = Thisbyte!((This Pat>>Disp)&B)
            finish
            ! Main loop, zap byte at a time
            Y = Low Byte + 1
            while Y < High Byte cycle
               Thisbyte == BitMap(X,Y)
               Thisbyte = 0 if Colour Mode = Replace Mode
               if Colour # 0 start
                  Disp = 16_08!!((Y&1)<<3)
                  Thisbyte = Thisbyte!((This Pat>>Disp)&255)
               finish
               Y = Y + 1
            repeat
            ! Special case of Y = High byte removed from the loop
            Thisbyte == BitMap(X,High Byte)
            if Device = Epson Printer start
               B = 16_FF>>High bits
            else
               B = (16_FF<<(High bits))&255
            finish
            Thisbyte = Thisbyte&(~B) if Colour Mode = Replace Mode
            if Colour # 0 start
               Disp = 16_08!!((High Byte&1)<<3)
               Thisbyte = Thisbyte!((This Pat>>Disp)&B)
            finish
         finish
      repeat
   else

      Lowbyte = LX>>3
      Low bits = LX & 7
      Highbyte = UX>>3
      High bits = 7 - (UX & 7)
      for Y = LY, 1, UY cycle
         Disp = 16_08!!((Y&1)<<3)
         for X = Lowbyte, 1, Highbyte cycle
            Thisbyte == BitMap(Y,X)
            B = 16_FF
            if X = Lowbyte then B = B>>Low bits
            if X = Highbyte then B = (B<<High Bits)&255
            Thisbyte = Thisbyte&(~B) if Colour Mode = Replace Mode
            if Colour # 0 start
               This Pat = Patterns((X&16_F)!Shade)
               Thisbyte = Thisbyte!((This Pat>>Disp)&255)
            finish
         repeat
      repeat
   finish
end
        
include "polyfill.hpl"
routine Draw Polygon
   integer i
   record(DataFm) name pp
   record(PointFm) array Pts(1:NPts + 1)
   if Npts > 1 start
      pp == First Point
      Pts(1) = First Point_p
      SX = Pts(1)_x
      SY = Pts(1)_y
      for i = 2, 1, NPts cycle
         Pts(i) = pp_p 
         Draw Line(pp_p_x, pp_p_y)
         pp == pp_Next
      repeat
      Pts(NPts + 1) = Pts(1)
      Draw Line(Pts(1)_x, Pts(1)_y)
      Polyfill(NPts + 1, Pts)
   finish
   NPts = 0
end

routine Initialise(integer Version)
   string(80) Param, Value, Device Name
   real width, height
   integer index=0, i, VTRes
   integer Rotation = -1

   for i = 1,1,Max Models cycle
      Index = i and exit if Models(i) = Version
   repeat
   
   Dev Data_Type = Version
   Start X = HP Start X
   Start Y = HP Start Y

   if Index < 5 start
     DEV DATA_NAME = "an Epson printer"
     Device Name = "Epson"
     VTRes = 72

     if Version = 8060 start
        NAME = "FX80"
        Res = 60
        DEV DATA_DVX = 500
     else if Version = 80120
        NAME = "FX80HR"
        Res = 120
        DEV DATA_DVX = 1000
     else if Version = 10060
        NAME = "FX100"
        Res = 60
        DEV DATA_DVX = 795
     else
        NAME = "FX100HR"
        Res = 120
        DEV DATA_DVX = 1590
     finish
     Device = Epson Printer
     DEV DATA_DVY = 684
   else if Index < 8
     DEV DATA_NAME = "an HP 2686A LaserJet printer"
     Device Name = "HP 2686A LaserJet"
     Device = Laserjet Printer
     NAME = itos(Version,0)
     DEV DATA_DVX = 767
     DEV DATA_DVY = 590
     if Version = 2686 start
        Res = 100
     else if Version = 2686150
        Res = 150
     else
        Res = 300
     finish
     VTRes = Res
   else if Index = 8
     Horizontal Map = 0
     DEV DATA_NAME = "a Printronix printer"
     Device Name = "Printronix"
     Device = Printronix Printer
     NAME = itos(dev data_type,0)
     DEV DATA_DVX = 791
     DEV DATA_DVY = 779
     VTRes = 72
     Res = 60
   else if Index < 12
     DEV DATA_NAME = "a Versatec printer"
     Device Name = "Versatec"
     Device = Versatec Printer

     if Version = 80 start
        Res = 200
        Height = 10.55
        Scan Bytes = 264
     else if Version = 81
        Res = 100
        Height = 10.23
        Scan Bytes = 128
     else
        Res = 100
        Height = 10.55
        Scan Bytes = 132
     finish

     VTRes = Res
     NAME = itos(dev data_type,0)
     DEV DATA_DVY = Int(Res * Height)
     DEV DATA_DVX = Int(VTRes*36.7/2.54)
   else
     DEV DATA_NAME = "HP LaserJet Series II printer"
     Device Name = "HP Laserjet Series II"
     Device = Laserjet Printer
     NAME = itos(Version,0)
     DEV DATA_DVX = 1575
     DEV DATA_DVY = 1088
     Start X = 120
     Start Y = 0
     Res = 150
     VTRes = Res
   finish
   NAME = "EDWIN_".NAME

   begin
     on event 3,4 start
         Oper Message("Error in ".Device Name." paper size specification")
         -> continue
     finish

     Param = NAME."_X"
     value = translate(Param)
     if value # Param start
        Width = Stor(Value) / 25.4
        DEV DATA_DVX = Int(Res * Width)
     finish
     continue:
   end
  
   begin
     on event 3,4 start
         Oper Message("Error in ".Device Name." paper size specification")
         -> continue
     finish

     Param = NAME."_Y"
     value = translate(Param)
     if value # Param start
         Height= Stor(Value) / 25.4
         DEV DATA_DVY = Int(VTRes * Height)
     finish
     continue :
   end

   DEV DATA_MVX = DEV DATA_DVX
   DEV DATA_MVY = DEV DATA_DVY

   DEV DATA_ARF = Round(VTRes*100/Res)
   DEV DATA_NUM CHAR SIZES = 255
   DEV DATA_Y Units Per CM = VTRes/2.54
   DEV DATA_X Units Per CM = Res/2.54
   if device = Laserjet Printer start
      Start X = Int(Res/100 * Start X)
      Start Y = Int(Res/100 * Start Y)
   finish

   Read Patterns(Res)
  
   if Horizontal Map = 1 start
         Rotation = 1 if Device = Epson Printer
         Rotate Pattern(Patterns(i*16),Rotation) for i = 1, 1, Max Pat
   finish

   if Viewing = 0 start
      Set Device(NAME)
      TTMode(1)
   finish

   New Page = Translate(NAME."_NEWPAGE")
   ToUpper(New Page)
   if Device # Versatec Printer start
      TTput(FF) if New Page = Both or New Page = Start
      if Device = Epson Printer start
         unless Translate(Name."_INITIALISE") = "NO" start
            TTput (Escape) and TTput ('@') if Device = Epson Printer
         finish
      finish
   else if Viewing = 0
      TTput(0) for I = 1,1,Scan Bytes*Res*8
   finish
end

routine Esc(string(255) S)
   integer I
   TTPut(Escape)
   TTPut(Charno(s,i)) for i = 1, 1, Length(s)
end

routine OutPut BitMap
   integer X
   integer name Dummy

   routine Printronix Print
     integer LINE MAX = DEV DATA_MVX//8 + 1
     integer LIM = LINE MAX // 3 * 3
     string (255) LINE BUFF
     integer P, J, LB, I
     byte name B
     ! 3-bit inversion table
     constbyteintegerarray  inverse(0:7) =
        2_000, 2_100, 2_010, 2_110,  2_001, 2_101, 2_011, 2_111
     !    0      1      2      3       4      5      6      7

     for I = DEV DATA_MVY, -1, 0  cycle
       LB = ADDR (LINE BUFF)
       for J = ADDR(BitMap(I,0)), 3, ADDR(BitMap(I,LIM)) cycle
            P = (((BYTE INTEGER(J)<< 8) ! BYTE INTEGER(J+1)) << 8) ! BYTE INTEGER (J+2)
            BYTE INTEGER (LB + 4) = (P&63);   P = P >> 6
            BYTE INTEGER (LB + 3) = (P&63);   P = P >> 6
            BYTE INTEGER (LB + 2) = (P&63);   P = P >> 6
            BYTE INTEGER (LB + 1) = (P&63)
            LB = LB + 4
       repeat
       LENGTH(LINE BUFF) = LB - ADDR(LINE BUFF)
       if LIM#LINE MAX start
           P = 0
           for J = LIM+1, 1, LINE MAX cycle
                P = (P<<8)!BitMap(I,J)
           repeat
           P = P << (8*(3-(LINE MAX - LIM)))
           for J = 18, -6, 0 cycle
                LINE BUFF = LINE BUFF.TO STRING(((P>>J)&63))
           repeat
       finish
       ! Strip trailing spaces (represented by binary zero)
       B == LENGTH(LINE BUFF)
       B = B-1 while B>0 and CHARNO(LINE BUFF, B)=0

       ! Add various bits and characters required by Printronix printer
       for j = addr(line buff)+1,1,lb cycle
          p = byte integer(j)
          byte integer(j) = inverse(p&7)<<3 ! inverse(p>>3) ! 64
       repeat
       ttput(charno(line buff,j)) for J = 1, 1, length(line buff)
       ttput(plot); ttput (NL)
     repeat
   end

   routine Epson Print 
      integer Op = 'K', Line Feed = 24, n1,n2
      integer X, Y, right, N Dots
      
      Op = 'L' if Res = 120
      for Y = DEV DATA_MVY//8, -1, 0 cycle
         right = DEV DATA_MVX
         while right >= 0 cycle
            exit if Bitmap(Right,Y) # 0
            right = right - 1
         repeat
         if right # -1 start
            N Dots = Right+1
            n2 = N Dots//256
            n1 = N Dots - (n2 * 256)
            TTput(Escape); TTput(Op); TTput(n1); TTput(n2)
            TTPut(Bitmap(X,Y)) for X = 0, 1, Right
            TTPut(CR)
         finish
         TTput(Escape); TTput('J'); TTput(Line Feed)
      repeat
      ttput(CR)
   end

   routine Versatec Print
      byte name Data == Byte(HeapStart)
      integer Ypos, X, Y, Start = 0, Fill = 0
      
      Ypos = DEV DATA_MVY//8
      if Ypos < Scan Bytes -1 start
         Fill = Scan Bytes - (YPos+2)
      finish
      for X = 0, 1, DEV DATA_MVX cycle
         TTPut(Data[Y]) for Y = Start, 1, Ypos
         if Fill # 0 start
            TTPut(0) for Y = 1, 1, Fill
         finish
         Start = Start + Size X
         YPos = YPos + Size X
      repeat
   end

   routine Laser Print
      integer X, Y, Right

      Esc("*t".ItoS(Res,0)."R")
      Esc("&a".ItoS(Start X,0)."H")
      Esc("&a".ItoS(Start Y,0)."V")
      Esc("*r1A")
      for X = 0, 1, DEV DATA_MVX cycle
         ttput(CR)
         ttput(NL)
         Esc("&a-1R")
         Right = DEV DATA_MVY//8
         Right = Right - 1 while Right > 1 and Bitmap(X,Right) = 0
         Esc("*b".ItoS(1 + Right,0)."W")
         TTPut(Bitmap(X,Y)) for Y = 0, 1, Right
      repeat
      Esc("*rB")
      ttput(CR)
   end

   if Page Number > 1 start
      if Device # Versatec Printer start
         TTput(FF)
      else
         TTput(0) for X = 1,1,Scan Bytes*Res*8
      finish
   finish
   Page Number = Page Number + 1
   if Device = Epson Printer start
      Epson Print
   else if Device = Laserjet Printer
      Laser Print
   else if Device = Printronix Printer
      Printronix Print
   else if Device = Versatec Printer
      Versatec Print
   finish
   flush output
   Dummy == Integer(HeapStart)
   Dummy[X] = 0  for X = 0, 1, Total Size//4-1
   Drawn = False
end

routine Append(integer X, Y)
   if NPts = 0 start
      First Point_p_x = X
      First Point_p_y = Y
      Next Point == First Point
      NPts = 1
   finish
   return if X = SX and Y = SY
   if Next Point_Next == nil start
      Next Point_Next == new(Next Point)
      Next Point == Next Point_Next
      Next Point_Next == nil
   else
      Next Point == Next Point_Next
   finish
   Next Point_p_x = X
   Next Point_p_y = Y
   NPts = NPts + 1
   SX = X
   SY = Y
end

external routine BitMaps alias "EDWIN___Z" (integer Com, X, Y)
   integer name Dummy
   switch Att(0:Att Maximum)
   switch Command(0:Max Com)

   Draw Polygon if NPts # 0 and (Com = 10 or Com < 5)

   -> Command(Com)

   Command(Dev Initialise): 
     Initialise(X)
     First Point == new(Next Point)
     First Point_Next == nil
     if Horizontal Map = 1 start
        Size X = DEV DATA_MVY//8+1
        Size Y = DEV DATA_MVX+1
     else
        Size X = DEV DATA_MVX//8+1
        Size Y = DEV DATA_MVY+1
     finish
     Total Size = ((Size X*Size Y)+3)&(~3)
     HeapStart = Get Space(Total Size)
     Dummy == Integer(HeapStart)
     Dummy[X] = 0  for X = 0, 1, Total Size//4-1
     Drawn = False
     WHX = DEV DATA_MVX
     WHY = DEV DATA_MVY
     Page Number = 1
     return

   Command(Dev Terminate):
     if Drawn = True start
        OutPut BitMap
        if Device # Versatec Printer start
           TTput(FF) if New Page = Both or New Page = End
        else if Viewing = 0
           TTput(0) for X = 1,1,Scan Bytes*Res*8
        finish
     finish
     Flush Output
     Dispose(HeapStart)
     return

   Command(Dev Newframe):
     OutPut BitMap if Drawn = True
     return

   Command(Dev Move):
     Sx = X; Sy = Y
     return

   Command(Dev Line):
     if Shade Mode = 0 start
        Draw Line(X, Y)
     else 
        Append(X,Y)
     finish
     return

   Command(Dev Char):
     signal 14, 14

   Command(Dev Attribute):
     if 0 <= X <= Att Maximum start

       -> Att(X)

       Att(Att Colour): 
          Y = 1 if Y # 0
          Colour = Y
          return
       Att(Att Colour Mode):
          if Y = Replace Mode or Y = Or Mode then Colour Mode = Y
          return
       Att(Att Shade Mode):
          Y = Solid unless 0 <= Y < 32
          Shade Mode = Y
          return

       Att(*): 
     finish
     return

   Command(Dev Low WB): 
     WLX = X; WLY = Y
     return

   Command(Dev High WB):
     WHX = X; WHY = Y
     return

   Command(Dev Low Box):
     LX = X; LY = Y
     return

   Command(Dev High Box):
     Test(LX, X)
     Test(LY,Y)
     return if LX > WHX or X < WLX or LY > WHY or Y < WLY
     LX = WLX if LX < WLX
     LY = WLY if LY < WLY
     X = WHX if X > WHX
     Y = WHY if Y > WHY
     Draw Box(LX, LY, X, Y)
     ! Now outline it
     return if Colour = 0
     SX = LX
     SY = LY
     Draw Line(X,LY)
     Draw Line(X,Y)
     Draw Line(LX,Y)
     Draw Line(LX,LY)
     return

   Command(*): 

end
endoffile