/************************************************************
*          Generalized Printer Device Driving Program       *
*************************************************************
*                                                           *
* See introductory comments in header source file.          *
*                                                           *
* This module contains procedures that are specific to the  *
* handling of EPSON output files.                           *
*                                                           *
* P. Hazel, November 1984                                   *
* Last modified: June 1985                                  *
************************************************************/


SECTION "EPSON"

GET "LIBHDR"
GET "GTHDR"



/* These procedures support EPSON printers connected via BBC
micros. Variable vertical spacing is supported, but there is
no support for variable horizontal spacing. */



/***********************************************************
*            Initialize                                    *
***********************************************************/

LET setupepson(model) BE
$(proc
cmask |:= cmask.epson
epson.model := cfs(model, "FX80") -> epson.model.fx80, epson.model.lq1500
printpage := epson.printpage
font.device.id.default := epson.device.id.default
write.font.device.id := epson.write.font.device.id
epson.pagelength := 12    // default to 12"
out.leftmargin := 60      // default to 0.6"

/* Set scaling factors so as to convert RSU to EPSON units,
which are 1/216" in the veritcal direction. No horizontal
conversion is done, since there is no support for variable
horizontal spacing. */

out.hscale.n := 1
out.hscale.d := 1
out.hscale.round := (out.hscale.d/2) / out.hscale.n
out.vscale.n := epson.model = epson.model.fx80 -> 216, 180
out.vscale.d := 254000
out.vscale.round := (out.vscale.d/2) / out.vscale.n

/* If font is >= 0 it means we are processing plain input,
and a default font has been selected. Now we know what the
output device is, we can scale the values in DEMWIDTH and
DCHWIDTHS. (For this device, the scaling is null.) */

IF font >= 0 THEN
  $(1
  demwidth := fontcb!font.emwidth
  FOR i = 0 TO 255 DO dchwidths!i := chwidths!i
  $)1

$)proc



/***********************************************************
*         Select default EPSON font for logical font       *
***********************************************************/

/* This procedure yields a EPSON font identifier (four independent
bytes) for old-style GCODE logical fonts in the range 0-15; for other
logical fonts it just returns the logical font. */

AND epson.device.id.default(n) = VALOF
$(proc
LET t = TABLE 0,#X100,#X10000,#X10100,
  #X40000,#X40100,0,0,0,0,0,0,0,0,0,0

n := ABS n
RESULTIS n < 16 -> t!n, n
$)proc



/***********************************************************
*               Write device font identification           *
***********************************************************/

/* For a EPSON printer, the device font identification consists
of four independent bytes. The first is the "country code" for
the font; the second is the "print mode" (0-63), which indicates
bold, enlarged, etc; the third is a byte of flag bits, and the
fourth is unused. The flag bits are
  01  use "alternate" character set (usually italic)
  02  use proportional character set -- overrides print mode
  04  use subscript character set
  08  use superscript character set
The mode is usually set to 255 when the proportional option is on. */

AND epson.write.font.device.id(n) BE
$(proc
SWITCHON n >> 24 INTO
  $(1
  CASE 0: writes("USA "); ENDCASE
  CASE 1: writes("France "); ENDCASE
  CASE 2: writes("Germany "); ENDCASE
  CASE 3: writes("UK "); ENDCASE
  CASE 4: writes("Denmark "); ENDCASE
  CASE 5: writes("Sweden "); ENDCASE
  CASE 6: writes("Italy "); ENDCASE
  CASE 7: writes("Spain "); ENDCASE
  CASE 8: writes("Japan "); ENDCASE
  DEFAULT: writes("??? "); ENDCASE
  $)1


IF   (n & #X000100) ~= 0 THEN writes("alternate ")
TEST (n & #X000200) ~= 0 THEN writes("proportional") ELSE
  $(1
  TEST (n & #X010000) ~= 0 THEN writes("elite ") ELSE writes("pica ")
  IF   (n & #X040000) ~= 0 THEN writes("condensed ")
  IF   (n & #X080000) ~= 0 THEN writes("emphasized ")
  IF   (n & #X100000) ~= 0 THEN writes("double-strike ")
  IF   (n & #X200000) ~= 0 THEN writes("enlarged ")
  $)1
IF (n & #X000400) ~= 0 THEN writes("subscript ")
IF (n & #X000800) ~= 0 THEN writes("superscript ")
$)proc



/***********************************************************
*                 Print page routine                       *
***********************************************************/

/* This procedure is called to print the page pointed to by
its argument. The top and left margins are reset at the start
of every page in case they have been altered by the user. */

AND epson.printpage(p, firstflag) BE
$(proc
LET l = p!page.text
font := -1

selectoutput(main.output)
startprinter()
IF firstflag THEN epson.tof(TRUE)

/* Re-set the page length at the top of each page, in
case it has changed. We do this by setting the line
depth to 1/2" and then setting page length by number
of lines because it is not possible to get the necessary
binary zero through the PHX chip on a BBC micro in order
to set the length in inches. */

epson.vmi := epson.model = epson.model.fx80 -> 108, 90
writec("\E3\B\EC\B", epson.vmi, epson.pagelength*2)

/* Loop for each line */
/* ------------------ */

UNTIL l = 0 DO
  $(1
  epson.printline(l)
  l := l!line.next
  IF checkbreak() THEN
    $(2
    stopoutput()
    longjump(restartlevel, restartlabel)
    $)2
  $)1

epson.tof(FALSE)
stopprinter()
flushoutput()
selectoutput(ver.output)
$)proc



/***********************************************************
*              Do top of form things                       *
***********************************************************/

/* At top of form we issue a carriage return, just in case, then, for
an FX-80, move up a teeny bit, in case already at top of form, then issue a
formfeed. Moving up is not supported for an LQ1500, so this conditional
formfeed game cannot be played. Therefore, at the start of output, we
do NOT output a formfeed for this printer. It is up to the user to
get it right.
  Next reset the left margin (needed first time; subsequently
it can in principle be changed). Use pica spacing for tenths of
an inch. Underlining and bold face are then switched off, the
current font unset, and all position variables set to zero. */

AND epson.tof(firstflag) BE
$(proc
wrch('*C')    // carriage return

TEST epson.model = epson.model.fx80 THEN writec("\Ej\B\B", 5, ascii.formfeed)
  ELSE UNLESS firstflag DO writec("\B", ascii.formfeed)

writec("\E-0\EF")    // underline, bold off
writec("\E!\B", 64)  // default printing mode
IF epson.model = epson.model.lq1500 THEN writec("\E5")

$<MVS
writec("\El\B", out.leftmargin/10+1)    // left margin (Frig: can't send zero)
$>MVS

$<MVS'
writec("\El\B", out.leftmargin/10)
$>MVS'

epson.underline.flag := FALSE
epson.bold.flag := FALSE

font := -1
epson.hmi := -1
out.vpos := 0
out.rvpos := 0
out.hpos := 0
out.rhpos := 0
$)proc



/***********************************************************
*               Write out a line                           *
***********************************************************/

/* Each vertical movement is rounded to the nearest equivalent
in Epson units (1/216" or 1/180") and no attempt is made to adjust for
accumulated error. */

AND epson.printline(l) BE
$(proc
LET i = l!line.items
LET vpos = l!line.vpos
LET vmove = out.vscale(vpos - out.rvpos)
out.rvpos := vpos

/* The maximum single downwards movement is 126 Epson
units. We take care not to output a single downwards
movement whose binary argument is 27, because this
character is taken as an ESC by the current PHX chip,
and if the following character happens to be ESC,
the printer inadvertently gets switched off. */

UNTIL vmove < 127 DO  $( writec("\EJ\B", 126); vmove -:= 126 $)
IF vmove = 27 THEN  $( writec("\EJ\B", 20); vmove -:= 20 $)
IF vmove > 0 THEN writec("\EJ\B", vmove)

/* Loop for each item */
/* ------------------ */

UNTIL i = 0 DO
  $(1
  LET hpos = i!item.hpos
  LET hmove = hpos - out.rhpos
  IF hmove < 0 THEN
    $(2
    wrch('*c')
    out.hpos := 0
    hmove := hpos
    $)2

  UNLESS hmove = 0 DO epson.moveright(hmove)
  out.rhpos := hpos

  TEST i!item.count > 0 THEN epson.wrtextitem(i)
    ELSE IF i!item.count = 0 THEN epson.wrruleitem(i)
  i := i!item.next
  $)1

$)proc



/***********************************************************
*                  Output text item                        *
***********************************************************/

/* This procedure writes out the data in a text item, taking
note of font changes and character widths. OUT.RHPOS holds the
current position in RSU; OUT.HPOS holds it in characters. */

AND epson.wrtextitem(i) BE
$(proc
LET str = i + item.bytes
LET swidth = -1
LET ifont = i!item.bytes

/* Select initial font */
/* ------------------- */

UNLESS font = ifont DO
  $(1
  selectfont(ifont)
  epson.setfont()
  $)1

/* Process the item's characters */
/* ----------------------------- */

FOR j = 4 TO i!item.count - 1 DO
  $(1
  LET c = str%j
  TEST c = itesc.esc THEN
    $(2
    j +:= 1
    c := str%j

    SWITCHON c INTO
      $(3
      CASE itesc.esc:
      wrch(itesc.esc)    // NOT ACTUALLY CORRECT
      ENDCASE

      CASE itesc.cwidth:
      swidth := fourbytes(str,j+1)
      j +:= 4
      ENDCASE

      CASE itesc.newfont:
        $(4
        LET ifont = fourbytes(str, j+1)
        selectfont(ifont)
        epson.setfont()
        $)4
      j +:= 4
      ENDCASE

      DEFAULT:
        $(4
        LET hmove = fourbytes(str,j)
        j +:= 3
        out.rhpos +:= hmove
        epson.moveright(hmove)
        $)4
      ENDCASE
      $)3
    $)2

  ELSE

  /* Process normal character */
  /* ------------------------ */

    $(2
    LET distance = swidth >= 0 -> swidth, chwidths!c

$<MVS
    /* Special action is taken in MVS in order to make
    sure that a pound sterling sign appears as such,
    independent of the US/UK character set selection. */

    TEST c ~= poundsterling THEN wrch(c) ELSE writec("\EI1\B\EI0", 6)
$>MVS

$<MVS'
    wrch(c)
$>MVS'

    /* Update position variables; unset special width. */
    /* ----------------------------------------------- */

    out.rhpos +:= distance
    out.hpos +:= dchwidths!c
    swidth := -1
    $)2
  $)1

$)proc



/***********************************************************
*                    Move right                            *
***********************************************************/

/* We check the amount to move right for an exact multiple
(nearly) of the available space sizes (condensed, elite and
pica). Otherwise we use the currently set font. Note that
at the start of a page, a current font is not set. */

AND epson.moveright(d) BE
$(proc
LET dd = d + 200
TEST dd REM epson.elitewidth <= 200 THEN
  $(1
  TEST epson.hmi = epson.elitewidth THEN epson.xmr(d, epson.hmi)

  ELSE TEST (epson.hmi = 2*epson.elitewidth) &
    (((d + epson.elitewidth/2) / epson.elitewidth) & 1) = 0 THEN
      epson.xmr(d, epson.hmi)

  ELSE
    $(2
    writec("\E!\B", 1)    // force elite
    epson.xmr(d, epson.elitewidth)
    UNLESS font = -1 DO epson.smode()  // reset correct mode
    $)2
  $)1

ELSE TEST dd REM epson.picawidth <= 200 THEN
  $(1
  TEST epson.hmi = epson.picawidth THEN epson.xmr(d, epson.hmi)

  ELSE TEST (epson.hmi = 2*epson.picawidth) &
    (((d + epson.picawidth/2) / epson.picawidth) & 1) = 0 THEN
      epson.xmr(d, epson.hmi)

  ELSE
    $(2
    writec("\E!\B", 64)    // force pica
    epson.xmr(d, epson.picawidth)
    UNLESS font = -1 DO epson.smode()  // reset correct mode
    $)2
  $)1

ELSE TEST (dd REM epson.condensedwidth <= 200) | (font = -1) THEN
  $(1
  TEST epson.hmi = epson.condensedwidth THEN epson.xmr(d, epson.hmi)

  ELSE TEST (epson.hmi = 2*epson.condensedwidth) &
    (((d + epson.condensedwidth/2) / epson.condensedwidth) & 1) = 0 THEN
      epson.xmr(d, epson.hmi)

  ELSE
    $(2
    writec("\E!\B", 4)    // force condensed
    epson.xmr(d, epson.condensedwidth)
    UNLESS font = -1 DO epson.smode()    // reset correct mode
    $)2
  $)1

ELSE epson.xmr(d, epson.hmi)    // default
$)proc


/* Subsidiary Procedure */
/* -------------------- */

AND epson.xmr(d, w) BE
$(proc
LET whole = (d + w/2) / w
IF (whole = 0) & (d ~= 0) THEN whole := 1
FOR i = 1 TO whole DO
  $(1
  wrch(' ')
  out.hpos +:= w
  $)1
$)proc



/***********************************************************
*                  Output a rule item                      *
***********************************************************/


AND epson.wrruleitem(i) BE RETURN



/***********************************************************
*               SET up for a new font                      *
***********************************************************/

/* This procedure sets up the Epson according to the current
font's device identification word, and the font number's
underline and bold bits. A subsidiary procedure is called to
deal with the printing mode bits; this procedure is also used
by MOVERIGHT to reset the printing mode. */

AND epson.setfont() BE
$(proc
LET f = fontcb!font.device.id
LET c = (f >> 24) & 255

/* Set high bit for zero values (for PHX chip) */
/* -------------------------------------------- */

$<MVS  IF c = 0 THEN c := 64  $>MVS
$<PANOS IF c = 0 THEN c := 64 $>PANOS   // pro tem till sorted

/* Set country character set */
/* ------------------------- */

writec("\ER\B", c)

/* Set normal or alternate */
/* ----------------------- */

writeesc(); wrch((f&#X0100)=0 -> '5', '4')

/* Set printing mode */
/* ----------------- */

epson.smode()

/* Set subscript, superscript or normal */
/* ------------------------------------ */

TEST (f & #X0400) ~= 0 THEN writec("\ES1")
ELSE TEST (f & #X0800) ~= 0 THEN writec("\ES0")
ELSE writec("\ET")

/* Deal with underlining */
/* --------------------- */

TEST ((font & font.ulbit) = 0) THEN IF epson.underline.flag THEN
  $(1
  writec("\E-0")
  epson.underline.flag := FALSE
  $)1
ELSE UNLESS epson.underline.flag DO
  $(1
  writec("\E-1")
  epson.underline.flag := TRUE
  $)1

/* Deal with bold face */
/* ------------------- */

TEST ((font & font.boldbit) = 0) THEN IF epson.bold.flag THEN
  $(1
  writec("\EF")
  epson.bold.flag := FALSE
  $)1
ELSE UNLESS epson.bold.flag DO
  $(1
  writec("\EE")
  epson.bold.flag := TRUE
  $)1

$)proc


/* Subsidiary Procedure for Printing Mode */
/* -------------------------------------- */

/* The variable EPSON.HMI is set to the width of a space,
in RSU, for the font selected. ***NB: There is a kludge in
here to cope with the current impossibility of getting a
binary zero from the mainframe through the UDN and PHX
chip to the printer. If the printing mode should be zero,
we send 64. This works for an FX-80, but for an LQ1500
it sets italic mode. Hence in this case we follow it by
"italic off". */

AND epson.smode() BE
$(proc
LET f = fontcb!font.device.id
LET p = (f >> 16) & 63
IF p = 0 THEN p := 64

/* Set proportional or given mode, and thence the HMI */
/* -------------------------------------------------- */

TEST (f & #X0200) = 0 THEN
  $(1
  writec("\E!\B", p)
  IF (epson.model = epson.model.lq1500) & (p = 64) THEN writec("\E5")
  TEST (p & 1) ~= 0 THEN epson.hmi := epson.elitewidth
    ELSE TEST (p & 4) ~= 0 THEN epson.hmi := epson.condensedwidth
      ELSE epson.hmi := epson.picawidth
  IF (p & 32) ~= 0 THEN epson.hmi *:= 2
  $)1
ELSE
  $(1
  writec("\E!\B\Ep1", 64)
  epson.hmi := epson.picawidth
  $)1
$)proc

// End of EPSON section.
