Unit Palette;

Interface

Type
  PalType     =  Array [0..768] of Byte;
Var
  FadePal     :  Array [0..768] of Real;
  Fadeend,
  FadeStep,
  FadeCount,
  FadeStart   :  Byte;
  FadeToPal   :  ^PalType;
  DoneFade    :  Boolean;

Procedure GetPCXPalettePas (PCXBuf,P:Pointer;PalOffset:Word);
Procedure GetPCXPaletteAsm (PCXBuf,P:Pointer;PalOffset:Word);

Procedure WritePalettePas  (Start,Finish:Byte;P:Pointer);
Procedure WritePaletteAsm  (Start,Finish:Byte;P:Pointer);

Procedure ReadPalettePas   (Start,Finish:Byte;P:Pointer);
Procedure ReadPaletteAsm   (Start,Finish:Byte;P:Pointer);

Procedure SetupFade        (Start,Finish:Byte;P:Pointer;Step:Byte);
Procedure FadePalette;
Procedure Oreo             (Start,Finish:Integer);

Implementation

Procedure CLI; Inline ($FA);
Procedure STI; Inline ($FB);

Procedure SetupFade (Start,Finish:Byte;P:Pointer;Step:Byte);
Var
  CurPal           :  Array [0..767] of Byte;
  ToPal            :  ^PalType;
  I,PalOfs,
  NumColors        :  Word;
  RealStep,
  RealToColor,
  RealCurColor     :  Real;
begin
  ToPal := Ptr (Seg(P^),Ofs(P^));
  ReadPaletteAsm (0,255,@CurPal);
  PalOfs := Start * 3;
  NumColors := (Finish - Start + 1) * 3;

  RealStep := Step;

  For I := 0 to NumColors-1 do begin
    RealCurColor := CurPal [PalOfs+I];
    RealToColor  :=  ToPal^[PalOfs+I];
    FadePal [PalOfs+I] := (RealCurColor - RealToColor) / RealStep;
    end;

  FadeStep  := 0;
  FadeCount := Step;
  FadeStart := Start;
  Fadeend   := Finish;
  FadeToPal := P;
  DoneFade  := False;
end;

Procedure FadePalette;
Var
  I,
  PalOfs,
  NumColors   :  Word;
  CurPal      :  Array [0..767] of Byte;
  Fact,
  RealToColor :  Real;
begin
  Inc (FadeStep);
  Fact := FadeCount - FadeStep;
  NumColors := (Fadeend - FadeStart + 1) * 3;
  ReadPaletteAsm (0,255,@CurPal);
  PalOfs := FadeStart * 3;

  For I := 0 to NumColors - 1 do begin
    RealToColor := FadeToPal^[PalOfs+I];
    CurPal[PalOfs+I] := Round (RealToColor + Fact * FadePal[PalOfs+I]);
    end;

  WritePaletteAsm (FadeStart,Fadeend,@CurPal);
  DoneFade := FadeStep = FadeCount;
end;

Procedure Oreo (Start,Finish:Integer);
Var
  I,PalOfs    :  Word;
  CurPal      :  Array [0..767] of Byte;
  Red,
  Blue,
  Green       :  Real;
  Gray        :  Byte;
begin
  ReadPaletteAsm (0,255,@CurPal);

  For I := Start to Finish do begin
    PalOfs := I * 3;
    Red   := CurPal[PalOfs + 0];
    Green := CurPal[PalOfs + 1];
    Blue  := CurPal[PalOfs + 2];

    Gray := Round ((0.30 * Red) + (0.59 * Green) + (0.11 * Blue));

    CurPal[PalOfs + 0] := Gray;
    CurPal[PalOfs + 1] := Gray;
    CurPal[PalOfs + 2] := Gray;
    end;
  WritePaletteAsm (Start,Finish,@CurPal);
end;

Procedure GetPCXPalettePas (PCXBuf,P:Pointer;PalOffset:Word);
Var
  I      :  Word;
  InByte :  Byte;
begin
  PCXBuf := Ptr (Seg(PCXBuf^),Ofs(PCXBuf^)+PalOffset);
  For I := 0 to 767 do begin
    InByte := Mem [Seg(PCXBuf^):Ofs(PCXBuf^)+I];
    InByte := InByte shr 2;
    Mem [Seg(P^):Ofs(P^)+I] := InByte;
    end;
end;

Procedure WritePalettePas (Start,Finish:Byte;P:Pointer);
Var
  I,
  NumColors   :  Word;
  InByte      :  Byte;
begin
  P := Ptr (Seg(P^),Ofs(P^)+Start*3);
  NumColors := (Finish - Start + 1) * 3;

  CLI;

  Port [$03C8] := Start;

  For I := 0 to NumColors do begin
    InByte := Mem [Seg(P^):Ofs(P^)+I];
    Port [$03C9] := InByte;
    end;

  STI;
end;

Procedure ReadPalettePas (Start,Finish:Byte;P:Pointer);
Var
  I,
  NumColors   :  Word;
  InByte      :  Byte;
begin
  P := Ptr (Seg(P^),Ofs(P^)+Start*3);
  NumColors := (Finish - Start + 1) * 3;

  CLI;

  Port [$03C7] := Start;

  For I := 0 to NumColors do begin
    InByte := Port [$03C9];
    Mem [Seg(P^):Ofs(P^)+I] := InByte;
    end;

  STI;
end;

Procedure GetPCXPaletteAsm (PCXBuf,P:Pointer;PalOffset:Word);
Assembler;
Asm
    push ds

    lds  si,PCXBuf
    mov  ax,PalOffset
    add  si,ax

    les  di,P

    mov  cx,768
  @@1:
    lodsb
    shr  al,1
    shr  al,1
    stosb
    loop @@1

    pop  ds
end;

Procedure WritePaletteAsm (Start,Finish:Byte;P:Pointer); Assembler;
Asm
    push ds

    lds  si,P

    cld

    xor  bh,bh               { P^ points to the beginning of the palette }
    mov  bl,Start            { data.  Since we can specify the Start and }
    xor  ax,ax               { Finish color nums, we have to point our }
    mov  al,Start            { Pointer to the Start color.  There are 3 }
    shl  ax,1                { Bytes per color, so the Start color is: }
    add  ax,bx               {   Palette Ofs = @P + Start * 3 }
    add  si,ax               { ds:si -> offset in color data }

    xor  ch,ch               { Next, we have to determine how many colors}
    mov  cl,Finish           { we will be updating.  This simply is: }
    sub  cl,Start            {    NumColors = Finish - Start + 1 }
    inc  cx

(*
    push      es
    push      dx
    push      ax

    xor       ax,ax                    { get address of status register }
    mov       es,ax                    {   from segment 0 }
    mov       dx,3BAh                  { assume monochrome addressing }
    test      Byte ptr es:[487h],2     { is mono display attached? }
    jnz       @@11                     { yes, address is OK }
    mov       dx,3DAh                  { no, must set color addressing }
  @@11:
    in        al,dx                    { read in status }
    jmp       @@21
  @@21:
    test      al,08h                   { is retrace on> (if ON, bit = 1) }
    jz        @@13                     { no, go wait For start }
  @@12:
                                       { yes, wait For it to go off }
    in        al,dx
    jmp       @@22
  @@22:
    test      al,08h                   { is retrace off? }
    jnz       @@12                     { no, keep waiting }
  @@13:
    in        al,dx
    jmp       @@23
  @@23:
    test      al,08h                   { is retrace on? }
    jz        @@13                     { no, keep on waiting }

    pop       ax
    pop       dx
    pop       es               *)

    mov  al,Start            { We are going to bypass the BIOS routines }
    mov  dx,03C8h            { to update the palette Registers.  For the }
    out  dx,al               { smoothest fades, there is no substitute }

    cli                      { turn off interrupts temporarily }
    inc  dx

  @@1:
    lodsb                    { Get the red color Byte }
    jmp  @@2                 { Delay For a few clock cycles }
  @@2:
    out  dx,al               { Write the red register directly }

    lodsb                    { Get the green color Byte }
    jmp  @@3                 { Delay For a few clock cycles }
  @@3:
    out  dx,al               { Write the green register directly }

    lodsb                    { Get the blue color Byte }
    jmp  @@4                 { Delay For a few clock cycles }
  @@4:
    out  dx,al               { Write the blue register directly }

    loop @@1

    sti                      { turn interrupts back on }
    pop  ds
end;

Procedure ReadPaletteAsm (Start,Finish:Byte;P:Pointer); Assembler;
Asm
    les  di,P

    cld

    xor  bh,bh               { P^ points to the beginning of the palette }
    mov  bl,Start            { buffer.  We have to calculate where in the}
    xor  ax,ax               { buffer we need to start at.  Because each  }
    mov  al,Start            { color has three Bytes associated With it }
    shl  ax,1                { the starting ofs is:            }
    add  ax,bx               {   Palette Ofs = @P + Start * 3  }
    add  si,ax               { es:di -> offset in color data   }

    xor  ch,ch               { Next, we have to determine how many   colors}
    mov  cl,Finish           { we will be reading.  This simply is:  }
    sub  cl,Start            {    NumColors = Finish - Start + 1     }
    inc  cx

    mov  al,Start            { We are going to bypass the BIOS routines }
    mov  dx,03C7h            { to read in from the palette Registers.   }
    out  dx,al               { This is the fastest method to do this.   }
    mov  dx,03C9h

    cli                      { turn off interrupts temporarily          }

  @@1:
    in   al,dx               { Read in the red color Byte               }
    jmp  @@2                 { Delay For a few clock cycles             }
  @@2:
    stosb                    { Store the Byte in the buffer             }

    in   al,dx               { Read in the green color Byte             }
    jmp  @@3                 { Delay For a few clock cycles             }
  @@3:
    stosb                    { Store the Byte in the buffer             }

    in   al,dx               { Read in the blue color Byte              }
    jmp  @@4                 { Delay For a few clock cycles             }
  @@4:
    stosb                    { Store the Byte in the buffer             }
    loop @@1

    sti                      { turn interrupts back on                  }
end;

end.
{

**********************************************
Here's the testing Program
**********************************************
}
Program MCGATest;

Uses
  Crt,Dos,MCGALib,Palette;

Var
  Stop,
  Start       :  LongInt;
  Regs        :  Registers;
  PicBuf,
  StorageBuf  :  Pointer;
  FileLength  :  Word;
  Pal,
  BlackPal    :  Array [1..768] of Byte;

Const
  NumTimes    = 100;

Procedure LoadBuffer (S:String;Buf:Pointer);
Var
  F           :  File;
  BlocksRead  :  Word;
begin
  Assign (F,S);
  Reset (F,1);
  BlockRead (F,Buf^,65000,FileLength);
  Close (F);
end;

Procedure Pause;
Var
  Ch     :  Char;
begin
  Repeat Until KeyPressed;
  While KeyPressed do Ch := ReadKey;
end;

Procedure Control;
begin
  SetGraphMode ($13);

  LoadBuffer ('E:\NAVAJO.PCX',PicBuf);

  GetPCXPaletteAsm (PicBuf,@Pal,FileLength-768);
  WritePalettePas (0,255,@Pal);
  DisplayPCX (0,0,PicBuf);

  FillChar (BlackPal,SizeOf(BlackPal),0);
  Pause;

  SetupFade (0,255,@BlackPal,20);
  Repeat FadePalette Until DoneFade;
  Pause;

  SetupFade (0,255,@Pal,20);
  Repeat FadePalette Until DoneFade;
  Pause;

  Oreo (0,255);
  Pause;

  SetupFade (0,255,@Pal,20);
  Repeat FadePalette Until DoneFade;
  Pause;
end;

Procedure Init;
begin
  GetMem (PicBuf,65500);
end;

begin
  Init;
  Control;
end.

