PCX Decoder (Assembler, nur 320x200x256)

Diskussion zum Thema Programmierung unter DOS (Intel x86)
Antworten
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

PCX Decoder (Assembler, nur 320x200x256)

Beitrag von zatzen »

Hallo!
Für meine eigenen Zwecke brauche ich einen einfachen PCX-Decoder.
Das ist eigentlich nichts wirklich besonderes, aber ich habe das Vorgehen, fest ausgerichtet auf 320x200 Pixel und 256 Farben, einmal in einer Pascal-Unit umgesetzt und mir Mühe gegeben, das ganze in Assembler so knapp und optimal wie möglich zu halten. Vielleicht ist es für den ein oder anderen ja interessant.
Grüße,
Zatzen

Code: Alles auswählen

{$G+}
Unit PCX;

(* PCX File Format Decoder Version 1 by Zatzen 15 Jan 2022 *)
(* only supports a fixed frame of 320x200 Pixels and 256 indexed colors *)
(* data must be RLE encoded *)
(* currently only works for PCX files not bigger than 65656 bytes *)

(****************************)
(* Example Program / Usage: *)
(****************************)
(* load_PCX('NAMENLOS.PCX');*)
(* decode_pcxdata;          *)
(* MCGA;                    *)
(* set_palette_256;         *)
(* display_image;           *)
(* release_memory;          *)
(* REPEAT UNTIL KeyPressed; *)
(* TextMode_3;              *)
(****************************)


INTERFACE


Procedure Load_PCX(fn: String);
Procedure decode_pcxdata;
Procedure set_palette_256;
Procedure MCGA;
Procedure TextMode_3;
Procedure display_image;
Procedure release_memory;


IMPLEMENTATION


Var 
 pcxdata_src: Pointer;
 pcxdata_upk: Pointer;
 pcxdata_siz: WORD;


Procedure load_PCX(fn: String);
Var
 f: File;
begin
 Assign(f, fn);
 Reset(f, 1);
 pcxdata_siz := FileSize(f) - 128;
 GetMem(pcxdata_src, pcxdata_siz);
 Seek(f, 128);
 BlockRead(f, pcxdata_src^, pcxdata_siz);
 Close(f);

 ASM (* shift palette data from 8 to 6 bits *)

   LES DI, pcxdata_src
   ADD DI, pcxdata_siz
   SUB DI, 768
   MOV BX, 767
   @LOOP:
     MOV AL, ES:[DI+BX]
     SHR AL, 2
     MOV ES:[DI+BX], AL
     DEC BX
   JNS @LOOP

 END;

end;


Procedure unpak_pcx; ASSEMBLER;
ASM

  XOR CX, CX

  MOV DX, DS

  LES DI, pcxdata_upk
  LDS SI, pcxdata_src

  MOV BX, 64000

  @LOOP:

    LODSB
    TEST AL, 192
    JZ @SKIP

      AND AL, 63
      MOV CL, AL

      LODSB
      SUB BX, CX
      REP STOSB

      JMP @SKIP2

    @SKIP:

      STOSB
      DEC BX

  @SKIP2:

  JNZ @LOOP

  MOV DS, DX

END;


Procedure decode_pcxdata;
begin
 GetMem(pcxdata_upk, 64000);
 unpak_pcx;
end;


Procedure set_palette_256; ASSEMBLER;
ASM

  MOV AX, 01012H
  XOR BX, BX
  MOV CX, 256
  LES DX, pcxdata_src
  ADD DX, pcxdata_siz
  SUB DX, 768
  INT 10H

END;


Procedure MCGA; ASSEMBLER;
ASM

  MOV AX, 013H
  INT 010H

END;


Procedure TextMode_3; ASSEMBLER;
ASM

  MOV AX, 003H
  INT 010H

END;


Procedure display_image; ASSEMBLER;
ASM

  MOV DX, DS

  MOV AX, 0A000H
  MOV ES, AX
  XOR DI, DI

  LDS SI, pcxdata_upk

  MOV CX, 32000
  REP MOVSW

  MOV DS, DX

END;


Procedure release_memory;
begin
 FreeMem(pcxdata_src, pcxdata_siz);
 FreeMem(pcxdata_upk, 64000);
end;


begin
end.
mov ax, 13h
int 10h

while vorne_frei do vor;
mkarcher
LAN Manager
Beiträge: 204
Registriert: Fr 5. Jun 2020, 19:38

Re: PCX Decoder (Assembler, nur 320x200x256)

Beitrag von mkarcher »

Da kann man noch tricksen. Zum Beispiel kannst Du Pseudo-MMX(TM) auf die Paletten-Umrechnung machen, wobei Du immer zwei Werte auf einmal umrechnest, und mit dem AND-Befehl die rübergerutschten Bits entfernst:

Code: Alles auswählen

ASM
  LES DI, pcxdata_src
  ADD DI, PCXDATA_SIZ
  SUB DI, 768
  MOV SI,DI
  MOV CX, 384
  @LOOP:
    LODS AX,ES:[SI]
    SHR AX,2
    AND AL,63
    STOSW
  LOOP @LOOP
END;
Es ist nicht klar, ob Du auf Geschwindifkeit oder auf Größe optimieren willst. Wenn Du auf Geschwindigkeit auf Prozessoren vor dem Pentium optimieren willst, solltest Du den Dekompressor so formulieren, dass er im häufigsten Fall (vermutlich ist das der Nicht-RLE-Fall, also AL < 192) keine Sprünge ausßer der Schleife enthält, und den Schleifenfuß doppelt implementieren, um Dir einen weiteren Sprung zu sparen. Also eher so:

Code: Alles auswählen

  @LOOP:

    LODSB
    TEST AL, 192
    JNZ @REPEAT
      STOSB
      DEC BX
  JNZ @LOOP
  JMP @DONE

    @REPEAT:
      AND AL, 63
      MOV CL, AL

      LODSB
      SUB BX, CX
      REP STOSB
  JNZ @LOOP
  @DONE:
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: PCX Decoder (Assembler, nur 320x200x256)

Beitrag von zatzen »

Hallo!

Danke für die Ergänzung!

Tatsächlich habe ich eher Wert auf kompakten Code gelegt, das ganze war aber auch eher eine Spaßaufgabe dass ich es möglichst optimal schreiben wollte, wobei ich es für PCX in Assembler irgendwie sogar bequemer finde als wenn man das in Pascal formuliert.
Es ist eine Weile her (25 Jahre +), dass ich zuletzt viel mit PCX-Dateien gearbeitet habe, und das war damals in QuickBasic und da habe ich einfach ein externes Programm über die Shell gestartet das man im Grafikmodus verlassen konnte, so dass der Bildschirm sozusagen stehen blieb.

Deinen ersten Teil übernehme ich gerne bei der nächsten Gelegenheit!
mov ax, 13h
int 10h

while vorne_frei do vor;
Antworten