{****************************************************************************}
{                                                                            }
{ MODULE:         Hardware                                                   }
{                                                                            }
{ DESCRIPTION:    An UNIT that provides general support to generic Hardware, }
{                 like the PIC, the DMA controller, etc...                   }
{                                                                            }
{ AUTHOR:         Juan Carlos Ar‚valo                                        }
{                                                                            }
{ MODIFICATIONS:  Nobody (yet ;-)                                            }
{                                                                            }
{ HISTORY:        18-Nov-1992 Documentation.                                 }
{                                                                            }
{ (C) 1992 VangeliSTeam                                                      }
{____________________________________________________________________________}

UNIT Hardware;

INTERFACE




PROCEDURE EnableIRQ   (i: WORD);
PROCEDURE DisableIRQ  (i: WORD);
FUNCTION  SetIRQVector(i: WORD; Vec: POINTER) : POINTER;

PROCEDURE DMASet   (Channel, Mode: WORD ; Buf: POINTER; Size: WORD);
PROCEDURE DMARawSet(Channel, Mode, Page: BYTE; Offs, Size: WORD);
PROCEDURE DMASetAsm;




IMPLEMENTATION

USES Dos;




{----------------------------------------------------------------------------}
{ PIC routines.                                                              }
{____________________________________________________________________________}

PROCEDURE EnableIRQ(i: WORD);
  BEGIN
    IF i < 8 THEN
      PORT[$21] := PORT[$21] AND (NOT (1 SHL i))
    ELSE
      BEGIN
        PORT[$A1] := PORT[$A1] AND (NOT (1 SHL (i-8)));
        PORT[$21] := PORT[$21] AND (NOT (1 SHL 2))
      END;
  END;


PROCEDURE DisableIRQ(i: WORD);
  BEGIN
    IF i < 8 THEN
      PORT[$21] := PORT[$21] OR (1 SHL i)
    ELSE
      BEGIN
        PORT[$A1] := PORT[$A1] OR (1 SHL (i-8))
      END;
  END;


FUNCTION SetIRQVector(i: WORD; Vec: POINTER) : POINTER;
  VAR
    p : POINTER;
    j : WORD;
  BEGIN
    IF i < 8 THEN j := i + $08
             ELSE j := i + $68;

    GetIntVec(j, p);
    SetIntVec(j, Vec);
    SetIRQVector := p;
  END;




{----------------------------------------------------------------------------}
{ DMA routines.                                                              }
{____________________________________________________________________________}

{
  AH = Channel
  CH = Mode
  CL = Page
  BX = Offset
  SI = Size
}

PROCEDURE DMA16Set; ASSEMBLER;
  CONST
    PageRegTable : ARRAY[0..3] OF WORD = ( $00, $8B, $89, $8A );
  ASM

        AND     AH,3

        SHR     CL,1
        RCR     BX,1
        ADD     CL,CL

        MOV  AL,AH;  ADD  AL,$04; OUT $D4,AL     { Disable DMA channel.                         }
        XOR  AL,AL;               OUT $D8,AL     { Clear BYTE POINTER flip-flop to lower byte.  }
        MOV  AL,AH;  ADD  AL,CH;  OUT $D6,AL     { DMA Mode register.                           }
        XOR  DH,DH
        MOV  DL,AH
        ADD  DX,DX                               
        ADD  DX,DX                               { Calculate DMA base port.                     }
        ADD  DX,$C0

        MOV  AL,BL;               OUT  DX,AL     { Offset of the buffer, low & high bytes.      }
        MOV  AL,BH;               OUT  DX,AL             

        PUSH DX

        MOV  DL,AH
        ADD  DX,DX                               
        MOV  BX,OFFSET PageRegTable
        ADD  BX,DX                               { Calculate page register port.                }
        MOV  DX,[BX]
        MOV  AL,CL;               OUT  DX,AL     { Set DMA page.                                }

        POP  DX
        INC  DX
        INC  DX                                  { Calculate DMA counter port.                  }

        MOV  BX,SI
        MOV  AL,BL;               OUT  DX,AL     { Size of the buffer minus 1, low & high byte. }
        MOV  AL,BH;               OUT  DX,AL             
        MOV  AL,AH;               OUT $D4,AL     { Enable DMA channel.                          }

  END;


PROCEDURE DMA8Set; ASSEMBLER;
  CONST
    PageRegTable : ARRAY[0..3] OF WORD = ( $87, $83, $81, $82 );
  ASM

        MOV  AL,AH;  ADD  AL,$04; OUT  $A,AL     { Disable DMA channel.                         }
        XOR  AL,AL;               OUT  $C,AL     { Clear BYTE POINTER flip-flop to lower byte.  }
        MOV  AL,AH;  ADD  AL,CH;  OUT  $B,AL     { DMA Mode register.                           }
        XOR  DH,DH
        MOV  DL,AH
        ADD  DX,DX                               { Calculate DMA base port.                     }

        MOV  AL,BL;               OUT  DX,AL     { Offset of the buffer, low & high bytes.      }
        MOV  AL,BH;               OUT  DX,AL             

        PUSH DX

        MOV  BX,OFFSET PageRegTable
        ADD  BX,DX                               { Calculate page register port.                }
        MOV  DX,[BX]
        MOV  AL,CL;               OUT  DX,AL     { Set DMA page.                                }

        POP  DX
        INC  DX                                  { Calculate DMA counter port.                  }

        MOV  BX,SI
        MOV  AL,BL;               OUT  DX,AL     { Size of the buffer minus 1, low & high byte. }
        MOV  AL,BH;               OUT  DX,AL             
        MOV  AL,AH;               OUT  $A,AL     { Enable DMA channel.                          }

  END;


PROCEDURE DMASetAsm; ASSEMBLER;
  ASM

                CMP     AH,2
                JZ      @@Fin
                CMP     AH,4
                JZ      @@Fin
                JB      @@8bit
                 CALL   DMA16Set
                JMP    @@Fin
@@8bit:          CALL   DMA8Set

@@Fin:

  END;


PROCEDURE DMARawSet(Channel, Mode, Page: BYTE; Offs, Size: WORD); ASSEMBLER;
  ASM

        MOV     AH,[Channel]
        MOV     CH,[Mode]
        MOV     CL,[Page]
        MOV     BX,[Offs]
        MOV     SI,[Size]

        CALL    DMASetAsm

  END;


PROCEDURE DMASet(Channel, Mode: WORD ; Buf: POINTER; Size: WORD);
  VAR
    Segm : WORD;
    Offs : WORD;
  BEGIN

    Segm := SEG(Buf^) AND $F000;
    Offs := SEG(Buf^) AND $0FFF;

    IF Offs > 65535 - OFS(Buf^) THEN
      BEGIN
        INC(Segm, $1000);
        Offs := WORD(Offs - 65536 + OFS(Buf^));
      END
    ELSE
      BEGIN
        Offs := Offs + OFS(Buf^);
      END;

    DEC(Size);

    DMARawSet(Channel, Mode, Segm SHR 12, Offs, Size);

  END;




END.
