Date: Wed, 6 Jan 88 12:59 EST
From: P150BK19@VB.CC.CMU.EDU
Subject: Hercules-to-Epson Turbo Pascal code
To: hicks@WALKER-EMH.ARPA

Procedure HGCopy;

(* This procedure produces a hard copy of a Hercules screen on an
Epson-compatible printer (mode 1) using inline code, which is considerably
faster than the HardCopy procedure provided with the Turbo Graphix ToolBox.
On a Panasonic XK-P1080i, which can print 8-bit double-density graphics (mode
1) at 6 inches per second, this routine essentially keeps the printer fully
occupied.  About half of the speedup over the ToolBox version comes from
using inline code to map the graphics bits to a print row, and the other half
comes from using inline code to print the row itself (instead of a "write"
call for each and every byte of dots).  While the procedure uses variables
which are defined from the Graphix ToolBox, owning the ToolBox is not
strictly necessary (of course you must then supply your own drivers).  The
key variables are:

     GrafBase : segment address of active screen
                (hardware screen is $B000);
     XScreenMaxGlb : width of screen (in pixels) - 1;
     XMaxGlb : (XScreenMaxGlb - 7) / 8; width of screen in bytes - 1;

The screen is mapped to a print row which is 8 bits high, corresponding to 8
scan lines.  The Hercules scan lines are stored in 4 sectors, the low address
of which are separated by $2000.  Each print row thus contains two successive
scan lines from each sector.  It is also assumed that the screen has 352 scan
lines (as is used in the ToolBox, in spite of the Hercules specification of
348), corresponding to exactly 44 8-bit print rows. This can also be changed
directly in the main portion of the procedure.  *)


type

  LineType = array [0..XScreenMaxGlb] of Byte;

var
  PrintLine : LineType;
  PrntChrPtr,LinePtr,SectorPtr,ScanPtr,
    PrtLineNum, ByteNum : integer;
  WorkByte : Byte;

Procedure MapLine(LinePtr, XMaxLoc, XScreenMaxLoc : integer;
  var PrintLine : LineType);
     {inline procedure to map a single print line }

var
  ScanBitLoc : integer;

begin {MapLine}

  XMaxLoc := XMaxGlb; {get Local variable versions for inline code}
  XScreenMaxLoc := XScreenMaxGlb; {which utilizes stack segment}

Inline(
  $C4/$9E/>PrintLine/    {     LES     BX,>PrintLine[BP] }
                         { set print line to zero }
  $89/$D9/               {     MOV     CX,BX }
  $03/$8E/>XScreenMaxLoc/ {     ADD     CX,>XScreenMaxLoc[BP] }
  $31/$C0/               {     XOR     AX,AX }
                         { ZeroStringLoop: }
  $26/                   {     ES:     ; PrintLine in extra segment! }
  $88/$07/               {     MOV     [BX],AL }
  $43/                   {     INC     BX }
  $39/$CB/               {     CMP     BX,CX }
  $7E/$F8/               {     JLE     ZeroStringLoop }
                         {     SectorPtr := LinePtr }
  $8B/$BE/>LinePtr/      {     MOV     DI,>LinePtr[BP] ; SectorPtr }
                         {     for SectorBitLoc := 7 downto 4 do }
                         {     4 Sectors in memory }
  $B9/$07/$00/           {     MOV     CX,7 }
                         { SectorLoop: }
                         {     ScanBitLoc := SectorBitLoc }
  $89/$8E/>ScanBitLoc/   {     MOV     >ScanBitLoc[BP],CX }
                         {     ScanPtr := SectorPtr }
  $57/                   {     PUSH    DI }
  $51/                   {     PUSH    CX }
  $31/$C9/               {     XOR     CX,CX }
                         {     for ScanNum := 0 to 1 do }
                         {     2 Scan Lines per Sector }
                         { ScanLineLoop: }
  $C4/$9E/>PrintLine/    {     LES     BX,>PrintLine[BP] ; PrntChrPtr }
  $51/                   {     PUSH    CX }
  $31/$C9/               {     XOR     CX,CX }
                         {     for ByteNum := 0 to XMaxLoc do }
                         {     90 Bytes/Scan Line }
                         { ByteScanLoop: }
                         {     WorkByte := mem[GrafBase:ScanPtr] }
  $06/                   {     PUSH    ES }
  $A1/>GrafBase/         {     MOV     AX,[>GrafBase] }
  $8E/$C0/               {     MOV     ES,AX }
  $26/                   {     ES: }
  $8A/$05/               {     MOV     AL,[DI] }
  $07/                   {     POP     ES }
  $51/                   {     PUSH    CX }
  $BA/$07/$00/           {     MOV     DX,7 }
                         {     for BitNum := 7 downto 0 do } 
                         {     Scan single Byte }
                         { BitScanLoop: }
                         {     mem[PrntSeg:PrntChrPtr]
                               := mem[PrntSeg:PrntChrPtr] or
                         ( ( (WorkByte shr BitNum) and 1 ) shl ScanBitLoc ) }
  $88/$C4/               {     MOV     AH,AL }
  $89/$D1/               {     MOV     CX,DX }
  $D2/$EC/               {     SHR     AH,CL }
  $80/$E4/$01/           {     AND     AH,1 }
  $8B/$8E/>ScanBitLoc/   {     MOV     CX,>ScanBitLoc[BP] }
  $D2/$E4/               {     SHL     AH,CL }
  $26/                   {     ES: }
                         {     PrintLine in extra segment }
  $08/$27/               {     OR     [BX],AH }
                         {     PrntChrPtr := PrntChrPtr + 1 }
  $43/                   {     INC     BX }
  $4A/                   {     DEC     DX }
  $81/$FA/$00/$00/       {     CMP     DX,0 }
  $7D/$E6/               {     JGE     BitScanLoop }
  $59/                   {     POP     CX }
                         {     ScanPtr := ScanPtr + 1 }
  $47/                   {     INC     DI }
  $41/                   {     INC     CX }
  $3B/$8E/>XMaxLoc/      {     CMP     CX,>XMaxLoc[BP] }
  $7E/$CF/               {     JLE     ByteScanLoop }
  $59/                   {     POP     CX }
                         {     ScanBitLoc := ScanBitLoc - 4 }
  $8B/$9E/>ScanBitLoc/   {     MOV     BX,>ScanBitLoc[BP] }
  $81/$EB/$04/$00/       {     SUB     BX,4 }
  $89/$9E/>ScanBitLoc/   {     MOV     >ScanBitLoc[BP],BX }
  $41/                   {     INC     CX }
  $81/$F9/$01/$00/       {     CMP     CX,1 }
  $7E/$B4/               {     JLE     ScanLineLoop }
  $59/                   {     POP     CX }
                         {     SectorPtr := SectorPtr + $2000 }
  $5F/                   {     POP     DI }
  $81/$C7/$00/$20/       {     ADD     DI,$2000 }
  $49/                   {     DEC     CX }
  $81/$F9/$04/$00/       {     CMP     CX,4 }
  $7D/$9F);              {     JGE     SectorLoop }

end; {MapLine}

Procedure PrintByte (OneByte : Byte);

begin {PrintByte}
Inline(
  $31/$D2/               {     XOR     DX,DX }
  $B4/$00/               {     MOV     AH,0 }
  $8A/$86/>OneByte/      {     MOV     AL,>OneByte[BP] }
  $CD/$17);              {     INT     $17 }
end; {PrintByte}


Procedure PrintRow (XScreenMaxLoc : integer; var PrintLine : LineType);

begin {PrintRow}

Inline(
  $C4/$9E/>PrintLine/    {     LES     BX,>PrintLine[BP] }
  $89/$D9/               {     MOV     CX,BX }
  $03/$8E/>XscreenMaxLoc/ {    ADD     CX,>XScreenMaxLoc[BP] }
                         {     for ByteNum := 0 to XScreenMaxGlb do }
                         {PrintLoop: }
                         {    Write(Lst, Chr( PrintLine[ByteNum]) ) }
  $31/$D2/               {     XOR     DX,DX }
  $B4/$00/               {     MOV     AH,0 }
  $26/                   {     ES: }
  $8A/$07/               {     MOV     AL,[BX] }
  $CD/$17/               {     INT     $17 }
  $43/                   {     INC     BX }
  $39/$CB/               {     CMP     BX,CX }
  $7E/$F2);              {     JLE     PrintLoop }

end; {PrintRow}


begin { HGCopy }

  PrintByte(27); {set Line spacing to 9/inch}
  PrintByte(Byte('3'));
  PrintByte(24);

  LinePtr := 0;  {start with 0 offset from GrafBase}
  for PrtLineNum := 1 to 44 do  {44 total print Lines}
  begin

    MapLine(LinePtr, XMaxGlb, XScreenMaxGlb, PrintLine);
    {map the Bits with an inLine procedure}

    {now print the Line}

    PrintByte(27);  {force EPSON mode 1 for now}
    PrintByte(Byte('L'));
    PrintByte(Lo(XScreenMaxGlb + 1));
    PrintByte(Hi(XScreenMaxGlb + 1));

    PrintRow(XScreenMaxGlb, PrintLine); {print row of Bytes}

    PrintByte(10);
    LinePtr := LinePtr + 2 * (XMaxGlb + 1); {jump 2 scan lines / print row}

  end; {print Line Loop}

  PrintByte(27); {reset printer}
  PrintByte(Byte('2'));

end; {HGCopy}
