'=========================================================================== ' Subject: LOAD 16 COLOR PCX Date: Unknown Date ' Author: Greg Turgeon Code: PB ' Origin: comp.lang.basic.misc Packet: EGAVGA.ABC '=========================================================================== ' Contains LoadPCX16, a 16 color PCX image file ' loader for VGA graphics mode 12h ' 'This file contains the following routines: ' 'SUB LoadPCX16(pcx$, Sline%, Col%) 16 color PCX file loader 'SUB LoadColorPCX16 loads 16 color PCX data to VGA DAC regs ' 'IMPORTANT: LoadPCX16 calls the routine VideoOff to blank the screen 'while the PCX image is being loaded. If you disable this feature, 'be sure to rem out both calls to VideoOff. ' 'ALSO IMPORTANT: LoadPCX16 allows an image smaller than full-screen '(640 X 480) to be repositioned on screen. However, instead of the 'coordinate system (X,Y), with X and Y identifying pixel positions, 'LoadPCX16 employs (Sline%, Col%) with Sline% identifying a vertical 'screen pixel position and Col% following the text mode convention '(in this case 0-79) for horizontal positioning. The routine performs 'error-checking for available repositioning space on-screen. ' 'As now written, the demo requires VGA. LoadPCX16 calls the routine 'LoadColorPCX16, which employs BIOS calls available only on color VGA 'hardware. Error checking for repositioning also assumes VGA mode 12h '(640 X 480) only. ' 'I first wrote LoadPCX16 in BASIC (PB 3.0c). On my 386SX 16, loading the 'test file (a fairly complex image originally generated by FRACTINT) 'from a RAM drive took over 90 seconds. The routine as presented here, 'converted almost entirely to assembly, loads the same image in 1.8 seconds. ' 'My thanks to Murray Moffatt for his patience and persistence while 'testing LoadPCX16. ' 'Greg Turgeon - CIS: 76470,2417 $LIB GRAPH ON $LIB VGA ON DECLARE FUNCTION GetStrLoc&( BYVAL AllocHandle% ) 'must be declared DEFINT A - Z %yes = -1: %no = 0 '''create variable to load w/PCX file header data TYPE PCXheader Mfg AS BYTE Version AS BYTE Encoding AS BYTE BitsPerPixel AS BYTE Xmin AS INTEGER Ymin AS INTEGER Xmax AS INTEGER Ymax AS INTEGER HorizontalRes AS INTEGER VerticalRes AS INTEGER Pal AS STRING * 48 Reserved AS BYTE NumColrPlanes AS BYTE BytesPerSLine AS INTEGER PalInfo AS INTEGER Filler AS STRING * 58 END TYPE DIM PIX1 AS SHARED PCXheader '''use command$ to identify pix to load pcx$ = UCASE$( COMMAND$ ) IF ISFALSE( LEN( DIR$( pcx$ ))) THEN PRINT: PRINT "Cannot find PCX file "; pcx$ END END IF CALL LoadPCX16( pcx$, Sline%, Col% ) END '=========================== SUB LoadPCX16( FileName$, BYVAL Sline%, BYVAL Col% ) PCXfile = FREEFILE OPEN FileName$ FOR BINARY AS PCXfile DOShandle% = FILEATTR( PCXfile, 2 ) 'DOS handle needed for asm FileBytes& = LOF( PCXfile ) '''load header into var & verify that PCX file is correct format get# PCXfile,, PIX1 IF PIX1.Mfg < > 10 OR PIX1.Version < > 5 THEN 'Mfg 10 = ZSoft, Version 5 = 3. CLOSE PCXfile PRINT: PRINT "mfg: "; PIX1.mfg, "Version"; PIX1.Version PRINT "Incorrect PCX version" EXIT SUB END IF PixWidth% = PIX1.Xmax - PIX1.Xmin PixHeight% = PIX1.Ymax - PIX1.Ymin PRINT PRINT "Width: "; PixWidth%, "Height:"; PixHeight% PRINT "Encoding type:"; PIX1.Encoding PRINT "Bits per pixel per plane:"; PIX1.BitsPerPixel PRINT "Horizontal resolution of originating system:"; PIX1.HorizontalRes; PRINT " Vertical resolution:"; PIX1.VerticalRes PRINT "Number of color planes:"; PIX1.NumColrPlanes PRINT "Number of bytes per scan line per plane:"; PIX1.BytesPerSLine PRINT "Palette info (color/bw = 1, grayscale = 2):"; PIX1.PalInfo PRINT "File size: "; FileBytes&; " bytes" SLEEP SCREEN 12 '''error checking: don't reposition image unless there's room MaxX% = 639: MaxY% = 479 IF Sline% > ( MaxY% - PixHeight% ) - 2 THEN Sline% = 0 IF Col% > (( MaxX% - PixWidth% ) \ 8 ) THEN Col% = 0 PixBPerLine% = PIX1.BytesPerSLine 'create for asm CALL LoadColorPCX16 'load PIX1.Pal colors SEEK PCXfile, 128 'start of screen data ChunkSize% = FRE( t$ ) 'create largest buffer possible FileBuffer$ = STRING$( ChunkSize%, 0 ) '(reduce size to smooth out display if ! push WORD ptr FileBuffer$ ! CALL getstrloc; now dx: ax = LOC, cx = length ! mov FBytesSeg??, dx; SAVE SEG & addr of FileBuffer$ ! mov FBytesPtr??, ax '''establish offset if repositioning image ! mov ax, Sline% ! mov dx, 80 ! mul dx ! add ax, Col% ! mov Mover??, ax '''determine how many bytes per line for the current video mode '''bytes per line will = screen column figure in BIOS data area ! xor bx, bx ! mov es, bx ! mov bx, &h44A ! mov ax, es: [bx] ! mov BPerLine%, ax ! CALL LoadChunk ; load FileBuffer$ ! mov ScreenLine%, -1 ; start AT - 1 TO allow FOR inc TO 0 '''begin loading pix to screen NewLine: ! inc ScreenLine% ! mov dx, ScreenLine% ! cmp dx, PixHeight% ; IF ScreenLine% > PixHeight%, THEN PixDone ! jle LineOK ! jmp PixDone LineOK: ! mov ax, BPerLine% ; Addr?? = BPerLine% * ScreenLine% ! imul dx ! mov di, ax ; di = target SCREEN address FOR loading ! add ax, PixBPerLine% ; LineEnd?? = Addr?? + PixBPerLine%( PIX1.BytesPerSLi ! mov LineEnd??, ax '''si = ptr to position in FileBuffer$, Plane% = target video plane ! mov Plane%, 0 ; begin each LINE w / plane 0 ! CALL SelectPlane NewByte: ! cmp Plane%, 3 ; done WITH ALL 3 planes? ! ja NewLine ; IF yes ! CALL GetNextByte ; IF no, load a BYTE into al FROM FileBuffer$ ! mov ah, al ; make a copy of NextByte? ! AND al, 192 ; IF top 2 BITS NOT set, THEN load the one BYTE ! cmp al, 192 ; IF set, THEN it 's a repeater, so load the ! je RepByte ; bytes AND assume continuing ON same LINE ! mov al, ah ; RESTORE al = NextByte?, AND load BYTE ! push di ; SAVE di( stosb increases di ) ! mov dx, &h0A000 ; BASE video SEG ! add di, Mover?? ; add ANY repositioning value ! stosb ; load the BYTE TO SCREEN ! pop di ! inc di ; update position FOR loading ! mov ax, LineEnd?? ; check: AT the END of a SCREEN line? ! cmp ax, di ! ja NewByte ; IF no ! mov ax, ScreenLine% ; IF yes, THEN move back TO ! mov bx, BPerLine% ; ! imul bx ; start of LINE AND switch ! mov di, ax ; ! inc Plane% ; TO NEXT video plane ! CALL SelectPlane ! jmp NewByte RepByte: '''coming in, ah = NextByte? ! mov al, ah ; RESTORE al = NextByte? ! AND al, 63 ; CLEAR BITS 6&7 TO leave the ! mov cl, al ; number of times TO REPEAT ! xor ch, ch ! CALL GetNextByte ; load the COLOR BYTE into al DoTheReps: ! push di ! mov dx, &h0A000 ! mov es, dx ; di already = address ! add di, Mover?? ; add ANY repositioning value ! stosb; load TO video ! pop di ! inc di ! cmp di, LineEnd?? ; AT END of line? ! je NextPlane ; IF yes, GOSUB NextPlane DoNextRep: ! LOOP DoTheReps ; IF no ! jmp NewByte NextPlane: ! push ax ! push dx ! mov ax, ScreenLine% ; move back TO start of LINE ! mov dx, BPerLine% ; AND ! imul dx ; move TO NEXT video plane ! mov di, ax ! inc Plane% ! pop dx ! pop ax ! CALL SelectPlane ! jmp DoNextRep PixDone: '''reset all planes ! mov ax, &h0F02 ! mov dx, &h3C4 ! OUT dx, ax CLOSE PCXfile SLEEP SCREEN 0 EXIT SUB GetNextByte: '''don't push ax; it sends back NextByte? ! push bx ! push cx ! push es ! mov es, FBytesSeg?? ! mov bx, FBytesPtr?? ! add bx, si; si = FileBuffer$ BYTE ptr, so bx now - > NextByte? ! mov al, BYTE ptr es: [bx]; now al = NextByte? ! inc si; increase FileBuffer$ ptr ! dec BuffPtr%; decrease ptr FOR countdown ! jnz ChunkNotDone; IF more IN FileBuffer$ ! CALL LoadChunk; IF empty, THEN GET more ChunkNotDone: ! pop es ! pop cx ! pop bx ! retn LoadChunk: ! push ax ! push bx ! push cx ! push dx ! push ds 'if FileBytes& =< ChunkSize% then ChunkSize% = FileBytes& ! mov ax, FileBytes&[00] ! mov dx, FileBytes&[02] ! cmp dx, 0 ; IF dx < > 0 THEN FileBytes& must ! jg SameSize ; be > ChunkSize% ! cmp ax, ChunkSize% ! jle SameSize ; IF FileBytes& < ChunkSize%, THEN make ! mov ChunkSize%, ax ; ChunkSize% = FileBytes& FOR final pass SameSize: ! mov bx, FBytesSeg?? ! mov ds, bx ! mov dx, FBytesPtr?? ! mov bx, DOShandle% ! mov cx, ChunkSize% ! mov ah, &h3F; reload FileBuffer$ ! INT &h21 ! jnc ReCalc ErrorHandler: ! mov ChunkSize%, ax ! pop ds ! pop dx ! pop cx ! pop bx ! pop ax CLOSE LOCATE 1, 1 IF ISTRUE( ChunkSize% ) THEN SOUND 800, .5: PRINT "Error: "; ChunkSize% END IF getkey SCREEN 0 END ReCalc: ! mov ax, FileBytes&[00] ; recalculate size of remaining FileBytes& ! mov dx, FileBytes&[02] ! mov bx, ChunkSize% ; subtract portion already loaded TO SCREEN ! SUB ax, bx ! sbb dx, 0 ! mov FileBytes&[02], dx ! mov FileBytes&[00], ax ! xor si, si ; si = FileBuffer$ ptr FOR loading; start AT 0 ! mov ax, ChunkSize% ! mov BuffPtr%, ax ; ptr FOR countdown ! pop ds ! pop dx ! pop cx ! pop bx ! pop ax ! retn SelectPlane: ! push ax ! push bx ! push cx ! push dx ! mov ax, 1; determine 2 ^ plane ! cbw ! mov cx, Plane% ! shl ax, cl ! mov ah, al; ah now = plane desired ! mov dx, &h3C4; plane SELECT ! mov al, 2 ! OUT dx, ax ! pop dx ! pop cx ! pop bx ! pop ax ! retn END SUB '=========================== SUB LoadColorPCX16 NumBytes?? = LEN( PIX1.Pal ) Addr1?? = VARPTR( PIX1.Pal ) '''palette regs actually index -> DAC regs '''build array of the DAC regs to which palette regs (0-15) are indexed REDIM temp?( 0: 15 ) RESTORE DefaultDACregs FOR a? = 0 TO 15: READ temp?( a? ): NEXT a? DACValSeg?? = VARSEG( temp?( 0 )): DACValPtr?? = VARPTR( temp?( 0 )) '''reduce PCX 0-255 color values to 0-63 ! push ax ! push bx ! push cx ! push dx ! push es ! push si ! push di; make both ds: si & es: di - > PIX1.Pal ! mov ax, ds; ALL fixed length strings are IN ds ! mov es, ax ! mov ax, Addr1?? ! mov si, ax ! mov di, ax ! mov cx, NumBytes?? ! cld; increment Reducer: ! lodsb ! shr al, 1; \ 4 TO reduce ! shr al, 1 ! stosb ! LOOP Reducer '''load each DAC reg ! mov si, Addr1??; now ds: [si] = PIX1.Pal ! mov es, DACValSeg?? ! mov di, DACValPtr??; es: [di] = temp%( 0 ) ! mov cx, 16 ! mov ax, &h1010 ! xor bx, bx LoadRegs: ! push cx ! mov bl, BYTE ptr es: [di]; pal REG ! mov dh, BYTE ptr ds: [si]; red ! inc si ! mov ch, BYTE ptr ds: [si]; green ! inc si ! mov cl, BYTE ptr ds: [si]; blue ! inc si ! push bp ! INT &h10 ! pop bp ! pop cx ! inc di; NEXT pal REG ! LOOP LoadRegs ! pop di ! pop si ! pop es ! pop dx ! pop cx ! pop bx ! pop ax ERASE temp? EXIT SUB DefaultDACregs: DATA 0, 1, 2, 3, 4, 5, 20, 7, 56, 57, 58, 59, 60, 61, 62, 63 END SUB