    !============================================================!
    !                                                            !
    !  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