#ifndef  AMD186_HPP
#define  AMD186_HPP

// peripheral control block registers
//
#define  PCB_BASE         0xff00              /* Peripheral control block base address */

#define  PCB_RELREG       (PCB_BASE + 0xFE)   /* Peripheral control block relocation register */
#define  PCB_RESCON       (PCB_BASE + 0xF6)   /* Reset configuration register */
#define  PCB_PRL          (PCB_BASE + 0xF4)   /* Processor release level register */
#define  PCB_AUXCON       (PCB_BASE + 0xF2)   /* Auxiliary configuration */
#define  PCB_SYSCON       (PCB_BASE + 0xF0)   /* System configuration register */
#define  PCB_WDT          (PCB_BASE + 0xE6)   /* Watchdog timer control register */
#define  PCB_EDRAM        (PCB_BASE + 0xE4)   /* Enable RCU register */
#define  PCB_CDRAM        (PCB_BASE + 0xE2)   /* Clock prescalar register */
#define  PCB_D1CON        (PCB_BASE + 0xDA)   /* DMA 1 control register */
#define  PCB_D1TC         (PCB_BASE + 0xD8)   /* DMA 1 transfer count register */
#define  PCB_D1DSTH       (PCB_BASE + 0xD6)   /* DMA 1 destination address high register */
#define  PCB_D1DSTL       (PCB_BASE + 0xD4)   /* DMA 1 destination address low register */
#define  PCB_D1SRCH       (PCB_BASE + 0xD2)   /* DMA 1 source address high register */
#define  PCB_D1SRCL       (PCB_BASE + 0xD0)   /* DMA 1 source address low register */
#define  PCB_D0CON        (PCB_BASE + 0xCA)   /* DMA 0 control register */
#define  PCB_D0TC         (PCB_BASE + 0xC8)   /* DMA 0 transfer count register */
#define  PCB_D0DSTH       (PCB_BASE + 0xC6)   /* DMA 0 destination address high register */
#define  PCB_D0DSTL       (PCB_BASE + 0xC4)   /* DMA 0 destination address low register */
#define  PCB_D0SRCH       (PCB_BASE + 0xC2)   /* DMA 0 source address high register */
#define  PCB_D0SRCL       (PCB_BASE + 0xC0)   /* DMA 0 source address low register */
#define  PCB_MPCS         (PCB_BASE + 0xA8)   /* PCS and MCS auxiliary register */
#define  PCB_MMCS         (PCB_BASE + 0xA6)   /* Midrange memory chip select register */
#define  PCB_PACS         (PCB_BASE + 0xA4)   /* Peripheral chip select register */
#define  PCB_LMCS         (PCB_BASE + 0xA2)   /* Low memory chip select register */
#define  PCB_UMCS         (PCB_BASE + 0xA0)   /* Upper memory chip select register */
#define  PCB_SP0BAUD      (PCB_BASE + 0x88)   /* Serial port 0 baud rate divisor register */
#define  PCB_SP0RD        (PCB_BASE + 0x86)		/* Serial port 0 receive data register */
#define  PCB_SP0TD        (PCB_BASE + 0x84)		/* Serial port 0 transmit data register */
#define  PCB_SP0STS       (PCB_BASE + 0x82)		/* Serial port 0 status register */
#define  PCB_SP0CT        (PCB_BASE + 0x80)		/* Serial port 0 control register */
#define  PCB_PDATA1       (PCB_BASE + 0x7A)		/* PIO data 1 register */
#define  PCB_PDIR1        (PCB_BASE + 0x78)		/* PIO direction 1 register */
#define  PCB_PIOMODE1     (PCB_BASE + 0x76)		/* PIO mode 1 register */
#define  PCB_PDATA0       (PCB_BASE + 0x74)		/* PIO data 0 register */
#define  PCB_PDIR0        (PCB_BASE + 0x72)		/* PIO direction 0 register */
#define  PCB_PIOMODE0     (PCB_BASE + 0x70)		/* PIO mode 0 register */
#define  PCB_T2CON        (PCB_BASE + 0x66)		/* Timer 2 mode/control register */
#define  PCB_T2CMPA       (PCB_BASE + 0x62)		/* Timer 2 maxcount compare A register */
#define  PCB_T2CNT        (PCB_BASE + 0x60)		/* Timer 2 count register */
#define  PCB_T1CON        (PCB_BASE + 0x5E)		/* Timer 1 mode/control register */
#define  PCB_T1CMPB       (PCB_BASE + 0x5C)		/* Timer 1 maxcount compare B register */
#define  PCB_T1CMPA       (PCB_BASE + 0x5A)		/* Timer 1 maxcount compare A register */
#define  PCB_T1CNT        (PCB_BASE + 0x58)		/* Timer 1 count register */
#define  PCB_T0CON        (PCB_BASE + 0x56)		/* Timer 0 mode/control register */
#define  PCB_T0CMPB       (PCB_BASE + 0x54)		/* Timer 0 maxcount compare B register */
#define  PCB_T0CMPA       (PCB_BASE + 0x52)		/* Timer 0 maxcount compare A register */
#define  PCB_T0CNT        (PCB_BASE + 0x50)		/* Timer 0 count register */
#define  PCB_SP0CON       (PCB_BASE + 0x44)		/* Serial port 0 interrupt control register Master mode */
#define  PCB_SP1CON       (PCB_BASE + 0x42)		/* Serial port 1 interrupt control register Master mode */
#define  PCB_INT4CON      (PCB_BASE + 0x40)		/* INT4 control register Master mode */
#define  PCB_INT3CON      (PCB_BASE + 0x3E)		/* INT3 control register Master mode */
#define  PCB_INT2CON      (PCB_BASE + 0x3C)		/* INT2 control register Master mode */
#define  PCB_INT1CON      (PCB_BASE + 0x3A)		/* INT1 control register Master mode */
#define  PCB_T2INTCON     (PCB_BASE + 0x3A)		/* Timer 2 interrupt control register Slave mode */
#define  PCB_INT0CON      (PCB_BASE + 0x38)		/* INT0 control register Master mode */
#define  PCB_T1INTCON     (PCB_BASE + 0x38)		/* Timer 1 interrupt control register Slave mode */
#define  PCB_DMA1CON      (PCB_BASE + 0x36)		/* DMA 1 interrupt control register */
#define  PCB_INT6CON      (PCB_BASE + 0x36)		/* INT6 Slave & master */
#define  PCB_DMA0CON      (PCB_BASE + 0x34)		/* DMA 0 interrupt control register */
#define  PCB_INT5CON      (PCB_BASE + 0x34)		/* INT5 Slave & master */
#define  PCB_TCUCON       (PCB_BASE + 0x32)		/* Timer interrupt control register Master mode */
#define  PCB_T0INTCON     (PCB_BASE + 0x32)		/* Timer 0 interrupt control register Slave mode */
#define  PCB_INTSTS       (PCB_BASE + 0x30)		/* Interrupt status register Slave & master */
#define  PCB_REQST        (PCB_BASE + 0x2E)		/* Interrupt request register Slave & master */
#define  PCB_INSERV       (PCB_BASE + 0x2C)		/* In-service register Slave & master */
#define  PCB_PRIMSK       (PCB_BASE + 0x2A)		/* Priority mask register Slave & master */
#define  PCB_IMASK        (PCB_BASE + 0x28)		/* Interrupt mask register Slave & master */
#define  PCB_POLLST       (PCB_BASE + 0x26)		/* Poll status register Master mode */
#define  PCB_POLL         (PCB_BASE + 0x24)		/* Poll register Master mode */
#define  PCB_EOI          (PCB_BASE + 0x22)		/* End-of-interrupt register Master mode  */
#define  PCB_INTVEC       (PCB_BASE + 0x20)		/* Interrupt vector register Slave mode */
#define  PCB_SP1BAUD      (PCB_BASE + 0x18)		/* Serial port 1 baud rate divisor register */
#define  PCB_SP1RD        (PCB_BASE + 0x16)		/* Serial port 1 receive register */
#define  PCB_SP1TD        (PCB_BASE + 0x14)		/* Serial port 1 transmit register */
#define  PCB_SP1STS       (PCB_BASE + 0x12)		/* Serial port 1 status register */
#define  PCB_SP1CT        (PCB_BASE + 0x10)		/* Serial port 1 control register */

// DMAxCON Register bits
//
#define  DCON_BW          0x0001              /* 0=Byte, 1=Word Select */
#define  DCON_ST          0x0002              /* 0=Stop, 1=Start DMA Channel */
#define  DCON_CHG         0x0004              /* Change Start Bit (see below) */
#define  DCON_EXT         0x0008              /* External Interrupt Enable */
#define  DCON_TDRQ        0x0010              /* Timer 2 Synchronization */
#define  DCON_P           0x0020              /* Relative Priority */
#define  DCON_SYN0        0x0040              /* Synchronization Type */
#define  DCON_SYN1        0x0080              /* - see below for constants */
#define  DCON_INT         0x0100              /* Interrupt */
#define  DCON_TC          0x0200              /* Terminal Count */
#define  DCON_SINC        0x0400              /* Source Increment */
#define  DCON_SDEC        0x0800              /* Source Decrement */
#define  DCON_SMIO        0x1000              /* Source Address Space Select */
                                              /* 1=memory, 0=i/o space */
#define  DCON_DINC        0x2000              /* Destination Increment */
#define  DCON_DDEC        0x4000              /* Destination Decrement */
#define  DCON_DMIO        0x8000              /* Destination Address Space Select */
                                              /* 1=memory, 0=i/o space */
#define  DCON_UNSYNC      0
#define  DCON_SSYNC       DCON_SYN0
#define  DCON_DSYNC       DCON_SYN1

#define  DCON_START       (DCON_CHG | DCON_ST)
#define  DCON_STOP        (DCON_CHG)

// TxCON Register bits
#define  TCON_CONT        0x0001
#define  TCON_ALT         0x0002
#define  TCON_EXT         0x0004
#define  TCON_P           0x0008
#define  TCON_RTG         0x0010
#define  TCON_MC          0x0020
#define  TCON_RIU         0x1000
#define  TCON_INT         0x2000
#define  TCON_INH         0x4000
#define  TCON_EN          0x8000

#define  TCON_START       (TCON_INH | TCON_EN)
#define  TCON_STOP        (TCON_INH)


typedef struct {
  unsigned  srcl, srch;
  unsigned  dstl, dsth;
  unsigned  tc,   con;
} DmaRegs;


typedef struct {
  unsigned  length;     // length of transfer (in bytes/words)
  int       control;    // DxCON register bits
  union {
    void far *srcMem;   // source memory address
    unsigned  srcPort;  // source i/o port
  };
  union {
    void far  *dstMem;  // destination memory address
    unsigned  dstPort;  // destination i/o port
  };
} DmaInfo;


/**
 * AMD186ED PIO masks for the SC12. Use with the AMD_PIO
 * functions, generated by AMD_TestPIOs()
 */
static const unsigned long AMD_PIOMASK[14] = {
  0x00002000L, 0x00001000L, 0x00000004L, 0x00000008L,
  0x00020000L, 0x00080000L, 0x00040000L, 0x00800000L,
  0x00400000L, 0x00200000L, 0x00100000L, 0x08000000L,
  0x10000000L, 0x00000400L
};

static const unsigned int  AMD_PIOPIN[14] = {
  13, 12,  2,  3,
  17, 19, 18, 23,
  22, 21, 20, 27,
  28, 10
};

/**
 *  AMD PIO functions !!! USE WITH CAUTION !!!
 *
 *  You may have to enclose direct PIO writes in CLI/STI pairs
 *  to ensure that the BIOS doesn't interfere during the access.
 *  This may happen, because the current pio state gets read,
 *  processed and then written back.
 *
 *  Mode  Direction   Function
 *    0     0           Normal operation
 *    0     1           PIO input with pullup/pulldown
 *    1     0           PIO output
 *    1     1           PIO input w/o pullup/pulldown
 */
inline unsigned long AMD_GetPioData() {
  return ((unsigned long)inpw(PCB_PDATA0) <<  0) |
         ((unsigned long)inpw(PCB_PDATA1) << 16);
}

inline unsigned long AMD_GetPioDir() {
  return ((unsigned long)inpw(PCB_PDIR0) <<  0) |
         ((unsigned long)inpw(PCB_PDIR1) << 16);
}

inline unsigned long AMD_GetPioMode() {
  return ((unsigned long)inpw(PCB_PIOMODE0) <<  0) |
         ((unsigned long)inpw(PCB_PIOMODE1) << 16) ;
}


inline void AMD_PioHigh(const int pio) {
  if (pio<16)
    outpw(PCB_PDATA0, inpw(PCB_PDATA0) | (1<<pio));
  else
    outpw(PCB_PDATA1, inpw(PCB_PDATA1) | (1<<(pio-16)));
}


inline void AMD_PioLow(const int pio) {
  if (pio<16)
    outpw(PCB_PDATA0, inpw(PCB_PDATA0) & ~(1<<pio));
  else
    outpw(PCB_PDATA1, inpw(PCB_PDATA1) & ~(1<<(pio-16)));
}


inline int AMD_GetPio(const int pio) {
  if (pio<16)
    return inpw(PCB_PDATA0) & (1<<pio);
  else
    return inpw(PCB_PDATA1) & (1<<(pio-16));
}

extern void AMD_TestPios();
extern void AMD_SetPioData( unsigned long mask, unsigned long data );
extern void AMD_SetPioDir ( unsigned long mask, unsigned long dir  );
extern void AMD_SetPioMode( unsigned long mask, unsigned long mode );
extern void AMD_SetPcsWaitStates(int waitstates);
extern void AMD_StartDma(int channel, DmaInfo *dma);
extern void AMD_StopDma(int channel);
extern void AMD_GetDmaInfo(int channel, DmaInfo *dma);

#endif