DEFINT A-Z
TYPE PcxheaderTYPE
	Mfg AS BYTE
	Ver AS BYTE
	Enc AS BYTE
	Bpp AS BYTE
	Xmin AS INTEGER
	Ymin AS INTEGER
	Xmax AS INTEGER
	Ymax AS INTEGER
	Hres AS INTEGER
	Vres AS INTEGER
	Pal AS STRING * 48
	Resrv AS BYTE
	Colpl AS BYTE
	Bpl AS INTEGER
	Paltyp AS INTEGER
	Filler AS STRING * 58
END TYPE
DIM PCXMaxscrnptr AS SHARED WORD
DIM PCXHandle AS SHARED INTEGER
DIM PCXTextofst AS SHARED WORD
DIM PCXChunksize AS SHARED WORD
DIM PCXHeader AS SHARED PcxheaderTYPE
DIM PCXBuffer AS SHARED STRING * 32256
DIM TargetAddress AS SHARED WORD

'================= START EXAMPLE ===============
' REG 1, &H13
' CALL INTERRUPT &H10                  ' Switch to graphics mode
' ShowPCX "picture.pcx", ErrorCode%    ' Display image
' IF NOT ErrorCode% THEN               ' Check for error
'       WHILE INKEY$="":WEND
' ELSE
'       SOUND 40,2
' END IF
' REG 1, &H03
' CALL INTERRUPT &H10                  ' Switch back to text mode
'================== END EXAMPLE ================



' EXPECTS:
' PCXErr%  0 = Copy PCX to screen (segment &HA000)
'          x = Copy PCX to segment x

' RETURNS:
' PCXErr%  1 = File not found
'          2 = Wrong PCX version
'          3 = Not a 256 color PCX
'          4 = Image too small
'          5 = Image too large

SUB ShowPCX(BYVAL F$, PCXErr%) PUBLIC
  DIM Wid AS INTEGER
  DIM Dep AS INTEGER
  DIM PCXPalette AS STRING * 768

  PCXChunksize = 32256

  IF PCXErr% = 0 THEN TargetAddress = &HA000 ELSE TargetAddress = PCXErr%
  PCXErr% = 0

  IF LEN(DIR$(F$)) = 0 THEN
    ' File does not exist
    PCXErr% = 1
    EXIT SUB
  END IF

  PCXTextofst = VARPTR(PCXBuffer)

  ' Open the file (and allow other programs to read while opened)
  OPEN F$ FOR BINARY ACCESS READ SHARED AS #1

  ' Get DOS filehandle
  PCXHandle = FILEATTR(1,2)

  ' Read file header
  GET #1, , PCXHeader

  IF PCXHeader.mfg <> 10 AND PCXHeader.ver <> 5 THEN
    ' Unsupported format
    PCXErr% = 2
    CLOSE #1
    EXIT SUB
  END IF

  IF PCXHeader.bpp <> 8 OR PCXHeader.Colpl <> 1 THEN
    ' Not an 8-bit PCX    
    PCXErr% = 3
    CLOSE #1
    EXIT SUB
  END IF

  Wid = PCXHeader.xmax - PCXHeader.xmin
  Dep = PCXHeader.ymax - PCXHeader.ymin

  IF Wid < 319 THEN
    ' Image too small (have to work on this)
    PCXErr% = 4
    CLOSE #1
    EXIT SUB
  END IF

  IF Wid > 319 OR Dep > 199 THEN
    ' Image too large (can't solve this without loss of speed)
    PCXErr% = 5
    CLOSE #1
    EXIT SUB
  END IF

  ' Palette info at 3*256 bytes before the end of file
  Tmp& = LOF(1) - 768
  Seek# 1, Tmp&

  ' Read the entire palette into string
  GET$ 1, 768, PCXPalette

  ' Divide all bytes by 4 (maximal value for palettes is 63)
  Paloff?? = VARPTR(PCXPalette)
  !  mov     AX,DS                ; set es=ds to set up stosb
  !  mov     ES,AX
  !  mov     CX,768
  !  mov     AX,Paloff??
  !  mov     SI,AX                ; let si and di point to PCXpalette string
  !  mov     DI,AX
PalShift:
  !  lodsb                        ; al <- ds:si   si = si + 1
  !  shr     AL,1
  !  shr     AL,1                 ; divide by 4 quickly
  !  stosb                        ; es:di <- al
  !  loop    Palshift

  ' Load palettes using interrupt (though I prefer using OUTs to VGA port)
  Palptr = VARPTR(PCXPalette)
  Palseg = VARSEG(PCXPalette)
  !  mov     AX,&H1012                  ;set new palette
  !  mov     BX,0
  !  mov     CX,256
  !  mov     DX,Palptr
  !  mov     ES,Palseg
  !  int     &H10

  PCXMaxscrnptr = (Wid + 1) * (Dep + 1)

  ' Back to image start
  Seek# 1, 128
  CALL PCXshow

  CLOSE #1
END SUB


' Only call this routine from ShowPCX
SUB PCXshow()
  !  mov     AX,TargetAddress
  !  mov     ES,AX                ; ES points to VGA buffer
  !  mov     DI,0                 ; DI holds screen offset
  !  call    getchunk             ; returns DX=buffersize, SI=bufferoffset
pcx2:
  !  lodsb                        ; AL now holds the color byte
  !  dec     DX
  !  jnz     pcx2a
  !  call    getchunk
pcx2a:
  !  mov     BH,1                 ; BH holds counter, assume 1 for now
  !  mov     BL,AL
  !  and     BL,&HC0              ; check if the 2 most sig bits are set
  !  cmp     BL,&HC0
  !  jnz     pcx3                 ; if not, just display AL once
  !  and     AL,&H3F              ; counter is 6 least sig bits
  !  mov     BH,AL                  
  !  lodsb                        ; AL now holds the color byte
  !  dec     DX
  !  jnz     pcx3
  !  call    getchunk
pcx3:
  !  xor     CX,CX
  !  mov     CL,BH
  !  rep     stosb                ; display color AL, BH times
  !  cmp     DI,PCXMaxscrnptr
  !  jz      pcxdone
  !  jmp     pcx2
GetChunk:
  !  push    AX
  !  push    BX
  !  mov     AH,&h3F
  !  mov     BX,PCXHandle
  !  mov     DX,PCXTextofst
  !  mov     CX,PCXChunksize
  !  int     &H21
  !  mov     SI,DX
  !  mov     DX,AX                ; DX=length  SI=offset
  !  pop     BX
  !  pop     AX
  !  retn
PCXDone:
END SUB
