procedure UNCRUNCH (var Addr1,Addr2; BlkLen:Integer);

(*
 This is the routine for displaying crunched TheDraw image files.  The
 crunched data format is a simple custom protocol for reproducing any image.
 The control codes below decimal 32 are reserved for this function.
 Characters 32 and above are written directly to the destination address.

 The following shows the format of a control code sequence.  Please note that
 not all functions use the optional bytes <x> or <y>.

 Data Structure:  <current byte>[<x>[<y>]]

    0..15 = New Foreground Color
   16..23 = New Background Color
       24 = Go down to next line, return to same horizontal position as when
            routine was started (akin to a c/r).
       25 = Displays <x> number of spaces.
       26 = Displays <x> number of <y>.  Also used to display ANY characters
            below #32.  This function is the only way to do this although it
            uses three bytes.  Otherwise the code would be interpreted as
            another command.
       27 = Toggles on/off the foreground attribute blink flag.
   28..31 = reserved

 ----------------------------------------------------------------------------

 To use this routine, call the procedure with the crunched image data as the
 first parameter, the display address as the second parameter, and the length
 of the crunched image data as the third parameter.

 Assume we have an ImageData file of a 40 character by 10 line block.  Also
 the following defintions.  ie:

   { TheDraw Pascal Crunched Screen Image }
   const                                           <- This CONST area is
     IMAGEDATA_WIDTH = 40;                            generated by TheDraw
     IMAGEDATA_DEPTH = 10;
     IMAGEDATA_LENGTH = 467;
     IMAGEDATA : array [1..467] of Char =
       (...list of image bytes here...);

   type ScreenType = array [0..3999] of Byte;
   var ScreenAddr : ScreenType absolute $B800:$0000;

   begin
     UnCrunch (IMAGEDATA,ScreenAddr[ (34*2) + (5*160) -162],IMAGEDATA_LENGTH);
   end;


 SCREENADDR is a variable mapped to the same location as the physical video
 addresses (via Turbo's absolute addressing).   The rather messy array offset
 tells UnCrunch where to start displaying the ImageData block.  The 34*2
 indicates the horizontal position number 34 with the 5*160 indicating line
 number 5.  This is similar to a Turbo GOTOXY (34,5) statement.

 The original horizontal starting offset is remembered by the uncrunch routine.
 The offset is restored upon moving down to the next line.  This permits a
 block to be displayed correctly anywhere on the screen.  ie:

               ÚÄ horizontal starting offset
               V
   +-------------------------------------------------+
   |                                                 |
   |                                                 | <- Assume this
   |                                                 |    is the video
   |           ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿               |    display.
   |           ³ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ³               |
   |           ³ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ³               |
   |           ³ÛÛ ImageData block ÛÛ³               |
   |           ³ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ³               |
   |           ³ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ³               |
   |           ³ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ³               |
   |           ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ               |
   |                                                 |
   |                                                 |
   |                                                 |
   +-------------------------------------------------+


 The ImageData block could just as well have been display in the upper-left
 corner of the screen with:
    UNCRUNCH (IMAGEDATA,ScreenAddr[ (1*2) + (1*160) -162],IMAGEDATA_LENGTH);

 Notice the array address changed to the equivilant of GOTOXY (1,1);

 To display the block in the lower-right corner you would use:
    UNCRUNCH (IMAGEDATA,ScreenAddr[ (40*2) + (15*160) -162],IMAGEDATA_LENGTH);

 In this example, the block is 40 characters wide by 10 lines deep.  Thus,
 to display such a large block, we must display the block at GOTOXY (40,15);

 That's it!  The routine was designed for easy use and understanding; however,
 for some people the best way is to experiment.  Create a program using the
 above examples, perhaps with a 40x10 block (or any size).  Good luck!
*)

begin
  inline (
    $1E/               {       PUSH    DS             ;Save data segment.}
    $C5/$B6/ADDR1/     {       LDS     SI,[BP+Addr1]  ;Source Address}
    $C4/$BE/ADDR2/     {       LES     DI,[BP+Addr2]  ;Destination Addr}
    $8B/$8E/BLKLEN/    {       MOV     CX,[BP+BlkLen] ;Length of block}
    $E3/$5B/           {       JCXZ    Done}
    $8B/$D7/           {       MOV     DX,DI          ;Save X coordinate for later.}
    $33/$C0/           {       XOR     AX,AX          ;Set Current attributes.}
    $FC/               {       CLD}
    $AC/               {LOOPA: LODSB                  ;Get next character.}
    $3C/$20/           {       CMP     AL,32          ;If a control character, jump.}
    $72/$05/           {       JC      ForeGround}
    $AB/               {       STOSW                  ;Save letter on screen.}
    $E2/$F8/           {Next:  LOOP    LOOPA}
    $EB/$4C/           {       JMP     Short Done}
                       {ForeGround:}
    $3C/$10/           {       CMP     AL,16          ;If less than 16, then change the}
    $73/$07/           {       JNC     BackGround     ;foreground color.  Otherwise jump.}
    $80/$E4/$F0/       {       AND     AH,0F0H        ;Strip off old foreground.}
    $0A/$E0/           {       OR      AH,AL}
    $EB/$F1/           {       JMP     Next}
                       {BackGround:}
    $3C/$18/           {       CMP     AL,24          ;If less than 24, then change the}
    $74/$13/           {       JZ      NextLine       ;background color.  If exactly 24,}
    $73/$19/           {       JNC     FlashBitToggle ;then jump down to next line.}
    $2C/$10/           {       SUB     AL,16          ;Otherwise jump to multiple output}
    $02/$C0/           {       ADD     AL,AL          ;routines.}
    $02/$C0/           {       ADD     AL,AL}
    $02/$C0/           {       ADD     AL,AL}
    $02/$C0/           {       ADD     AL,AL}
    $80/$E4/$8F/       {       AND     AH,8FH         ;Strip off old background.}
    $0A/$E0/           {       OR      AH,AL}
    $EB/$DA/           {       JMP     Next}
                       {NextLine:}
    $81/$C2/$A0/$00/   {       ADD     DX,160         ;If equal to 24,}
    $8B/$FA/           {       MOV     DI,DX          ;then jump down to}
    $EB/$D2/           {       JMP     Next           ;the next line.}
                       {FlashBitToggle:}
    $3C/$1B/           {       CMP     AL,27          ;Does user want to toggle the blink}
    $72/$07/           {       JC      MultiOutput    ;attribute?}
    $75/$CC/           {       JNZ     Next}
    $80/$F4/$80/       {       XOR     AH,128         ;Done.}
    $EB/$C7/           {       JMP     Next}
                       {MultiOutput:}
    $3C/$19/           {       CMP     AL,25          ;Set Z flag if multi-space output.}
    $8B/$D9/           {       MOV     BX,CX          ;Save main counter.}
    $AC/               {       LODSB                  ;Get count of number of times}
    $8A/$C8/           {       MOV     CL,AL          ;to display character.}
    $B0/$20/           {       MOV     AL,32}
    $74/$02/           {       JZ      StartOutput    ;Jump here if displaying spaces.}
    $AC/               {       LODSB                  ;Otherwise get character to use.}
    $4B/               {       DEC     BX             ;Adjust main counter.}
                       {StartOutput:}
    $32/$ED/           {       XOR     CH,CH}
    $41/               {       INC     CX}
    $F3/$AB/           {       REP STOSW}
    $8B/$CB/           {       MOV     CX,BX}
    $49/               {       DEC     CX             ;Adjust main counter.}
    $E0/$AA/           {       LOOPNZ  LOOPA          ;Loop if anything else to do...}
    $1F);              {Done:  POP     DS             ;Restore data segment.}
end; {UNCRUNCH}

