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}