UNIT Hardware;
{
  Control del nivel hardware del PC:
  Controlador de interrupciones, Controlador de DMA y Timers.
  Por Luis Crespo, FidoNet 2:343/108.21, Internet d8089110@est.fib.upc.es
}

INTERFACE

TYPE
  TBufProc = PROCEDURE;
  PByte=^Byte;

  PROCEDURE EOI;
  PROCEDURE EOISlave;
  PROCEDURE HabilitaIRQ(Num:Byte);
  PROCEDURE DeshabilitaIRQ(Num:Byte);

  PROCEDURE ProgKDMA(DMANum:Byte; Buffer:Pointer; Tamanyo:Word; AutoInit,DesdeMem:Boolean);
  PROCEDURE StopDMA(DMANum:Byte);

  PROCEDURE ProgTimer0(Divisor:word);
  PROCEDURE SetTimerFrec(VAR Frecuencia:word);


IMPLEMENTATION


  {--------------- Controlador de interrupciones ---------------}

  PROCEDURE EOI;
  {Indica un fin de interrupci¢n al controlador maestro}
  BEGIN
    Port[$20]:=$20;
  END;

  PROCEDURE EOISlave;
  {Indica un fin de interrupci¢n al controlador esclavo}
  BEGIN
    Port[$A0]:=$20;
  END;

  PROCEDURE HabilitaIRQ(Num:Byte);
  {Programa el PIC (o los PICs) para habilitar un nivel de interrupci¢n}
  VAR
    Mask:Byte;
  BEGIN
    IF Num<=7 THEN
    BEGIN
      Mask:=NOT(1 SHL Num);  {Bit a 0 --> interrupci¢n habilitada}
      ASM CLI END;
      Port[$21]:=Port[$21] AND Mask;
      ASM STI END;
    END
    ELSE BEGIN
      {Si la IRQ es>7, hay que programar el controlador esclavo}
      Num:=Num-8;
      Mask:=NOT(1 SHL Num);
      ASM CLI END;
      Port[$A1]:=Port[$A1] AND Mask;
      ASM STI END;
      HabilitaIRQ(2);
      {Y tambi‚n hay que habilitar la entrada en cascada en el master
       (probablemente ya estar  habilitada)}
    END;
  END;

  PROCEDURE DeshabilitaIRQ(Num:Byte);
  {Programa el PIC (o los PICs) para deshabilitar un nivel de interrupci¢n}
  VAR
    Mask:Byte;
  BEGIN
    IF Num<=7 THEN
    BEGIN
      Mask:=1 SHL Num;  {Bit a 1 --> interrupci¢n inhabilitada}
      ASM CLI END;
      Port[$21]:=Port[$21] OR Mask;
      ASM STI END;
    END
    ELSE BEGIN
      {Si la IRQ es>7, hay que programar el controlador esclavo}
      Num:=Num-8;
      Mask:=1 SHL Num;
      ASM CLI END;
      Port[$A1]:=Port[$A1] OR Mask;
      ASM STI END;
    END;
  END;


  {--------------- Controlador de DMA ---------------}

  PROCEDURE ProgKDMA(DMANum:Byte; Buffer:Pointer; Tamanyo:Word; AutoInit,DesdeMem:Boolean);
  {Programa el controlador de DMA (de 8 bits) para lanzar un buffer de
   memoria a perif‚rico o para leer de perif‚rico a memoria}
  CONST
    DMAPageReg: ARRAY [0..7] OF Word = ($87,$83,$81,$82,$00,$8B,$89,$8A);
  VAR
    Pagina:Byte;
    Desp:Word;
    DirFisica:LongInt;
    DMAMode:Byte;
  BEGIN
    DirFisica:=LongInt(Seg(Buffer^)) SHL 4 + Ofs(Buffer^);
    Pagina:=DirFisica SHR 16;
    Desp:=DirFisica AND $FFFF;
    ASM CLI END;
    StopDMA(DMANum); {Deshabilito el canal provisionalmente, para programarlo}
    Port[$0C]:=0;    {Inicializo el flip-flop para empezar por byte bajo}
    DMAMode:=$40 OR DMANum;
    IF AutoInit THEN DMAMode:=DMAMode OR $10;
    IF DesdeMem THEN DMAMode:=DMAMode OR 8 ELSE DMAMode:=DMAMode OR 4;
    Port[$0B]:=DMAMode;
    Port[DMAPageReg[DMANum]]:=Pagina;  {Pagina del buffer en memoria f¡sica}
    Port[2*DMANum]:=Lo(Desp);   {Desplazamiento del buffer en memoria f¡sica}
    Port[2*DMANum]:=Hi(Desp);
    Dec(Tamanyo);
    Port[2*DMANum+1]:=Lo(Tamanyo);  {Tama¤o del buffer}
    Port[2*DMANum+1]:=Hi(Tamanyo);
    Port[$0A]:=DMANum;  {Una vez programado, habilito el canal}
    ASM STI END;
  END;

  PROCEDURE StopDMA(DMANum:Byte);
  {Deshabilita un canal de DMA}
  BEGIN
    IF DMANum<4 THEN
      Port[$0A]:=DMANum OR 4
    ELSE
      Port[$D4]:=(DMANum-4) OR 4
  END;


  {--------------- Timer 0 ---------------}

  PROCEDURE ProgTimer0(Divisor:word);
  {Programa el Timer 0 para que cuente desde Divisor hasta 0}
  BEGIN
    ASM CLI END;     {Inhabilitamos interrupciones: secci¢n cr¡tica}
    Port[$43]:=$36;  {00111010: Timer 0, acceso secuencial y modo cont¡nuo}
    Port[$40]:=Lo(Divisor); {Byte bajo del contador}
    Port[$40]:=Hi(Divisor); {Byte alto del contador}
    ASM STI END;     {Fin de secci¢n cr¡tica}
  END;

  PROCEDURE SetTimerFrec(VAR Frecuencia:word);
  {Programa el Timer 0 para que genere "Frecuencia" interrupciones por segundo}
  VAR
    Divisor:Word;
  BEGIN
    Divisor:=1193180 DIV Frecuencia;
    Frecuencia:=1193180 DIV Divisor;
    ProgTimer0(Divisor);
  END;


END.