#include "config.h"
#include "scsi_defs.h"

/*
 *  Data uint8_t to GPIOB register setting value and parity table
*/

// Parity bit generation
#define PTY(V)   ((1^((V)^((V)>>1)^((V)>>2)^((V)>>3)^((V)>>4)^((V)>>5)^((V)>>6)^((V)>>7)))&1)

// Set DBP
#define DBP(D)    ((((uint32_t)(D)<<16)|(PTY(D)<<11)) ^ SCSI_DB_MASK)

#define DBP8(D)   DBP(D),DBP(D+1),DBP(D+2),DBP(D+3),DBP(D+4),DBP(D+5),DBP(D+6),DBP(D+7)
#define DBP32(D)  DBP8(D),DBP8(D+8),DBP8(D+16),DBP8(D+24)

// BSRR register control value that simultaneously performs DB set, DP set, and REQ = H (inactrive)
const uint32_t db_bsrr[256]={
  DBP32(0x00),DBP32(0x20),DBP32(0x40),DBP32(0x60),
  DBP32(0x80),DBP32(0xA0),DBP32(0xC0),DBP32(0xE0)
};
// Parity bit acquisition
#define PARITY(DB) ((db_bsrr[DB & 0xff] & 0x0800) >> 11)

// Macro cleaning
#undef DBP32
#undef DBP8
//#undef DBP
//#undef PTY

#if USE_DB2ID_TABLE
/* DB to SCSI-ID translation table */
const uint8_t db2scsiid[256]={
  0xff,
  0,
  1,1,
  2,2,2,2,
  3,3,3,3,3,3,3,3,
  4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
};
#endif

// Put DB and DP in output mode
inline void SCSI_DB_OUTPUT(uint8_t d) {
  GPIOB_PDOR = db_bsrr[d];
}

// Put DB and DP in input mode
inline void SCSI_DB_INPUT()  {
  GPIOB_PDDR = 0x00000000;
  GPIOB_PDOR = SCSI_DB_MASK;
}

/*
 * Bus reset interrupt.
 */
void onBusReset(void)
{
#if SUPPORT_SASI
  // SASI I / F for X1 turbo has RST pulse write cycle +2 clock ==
  // I can't filter because it only activates about 1.25us
  if(!(m_sel->m_quirks & QUIRKS_SASI)) {
#endif
    if(digitalRead(RST)) return;
    delayMicroseconds(20);
    if(digitalRead(RST)) return;
#if SUPPORT_SASI
  }
#endif

  SCSI_DB_INPUT();

  LOGN("BusReset!");
  m_isBusReset = true;
}


/*
 * Read by handshake.
 */
inline uint8_t readHandshake(void)
{
  SET_REQ_ACTIVE();
  while(!GET_ACK()) { if(m_isBusReset) { return 0; } }
  uint8_t r = readIO();
  SET_REQ_INACTIVE();
  while(GET_ACK()) { if(m_isBusReset) { return 0; } }
  return r;  
}

inline void readHandshakeBlock(uint8_t *d, uint16_t len)
{
  while(len) {
    SET_REQ_ACTIVE();
    while(!GET_ACK()) { if(m_isBusReset) { return; } }
    *d++ = readIO();
    len--;
    SET_REQ_INACTIVE();
    while(GET_ACK()) { if(m_isBusReset) { return; } }
  }
}

/*
 * Write with a handshake.
 */
inline void writeHandshake(uint8_t d)
{
  GPIOB_PDDR = SCSI_DB_MASK;
  SCSI_DB_OUTPUT(d);

  SET_REQ_ACTIVE();
  while(!GET_ACK()) { if(m_isBusReset) { return; } }
  SET_REQ_INACTIVE();
  SCSI_DB_INPUT();
  while(GET_ACK()) { if(m_isBusReset) { return; } }
}

inline void writeHandshakeBlock(const uint8_t *d, uint16_t len)
{
  GPIOB_PDDR = SCSI_DB_MASK;
  while(len) {
    SCSI_DB_OUTPUT(*d++);
    SET_REQ_ACTIVE();
    while(!GET_ACK()) { if(m_isBusReset) { return; } }
    len--;
    SET_REQ_INACTIVE();
    while(GET_ACK()) { if(m_isBusReset) { return; } }
  }
  SCSI_DB_INPUT();
}

/*
 * Data in phase.
 *  Send len uint8_ts of data array p.
 */
void writeDataPhase(int len, const uint8_t* p)
{
  //LOGN("DATAIN PHASE");
  SET_MSG_INACTIVE();
  SET_CD_INACTIVE();
  SET_IO_ACTIVE();

#if READ_SPEED_OPTIMIZE
  writeHandshakeBlock(p, len);
#else
  for (int i = 0; i < len; i++) {
    if(m_isBusReset) {
      m_phase = PHASE_BUSFREE;
      return;
    }
    writeHandshake(p[i]);
  }
#endif
  if(m_isBusReset) {
    m_phase = PHASE_BUSFREE;
  }
}

/* 
 * Data in phase.
 *  Send len block while reading from SD card.
 */
void writeDataPhaseSD(uint32_t adds, uint32_t len)
{
#if READ_SPEED_OPTIMIZE
  uint32_t bigread = (MAX_BLOCKSIZE / m_sel->m_blocksize);
#endif
  uint32_t i = 0;

  //LOGN("DATAIN PHASE(SD)");
  uint32_t pos = adds * m_sel->m_blocksize;
  m_sel->m_file.seek(pos);

  SET_MSG_INACTIVE();
  SET_CD_INACTIVE();
  SET_IO_ACTIVE();

  while(i < len) {
      // Asynchronous reads will make it faster ...
#if READ_SPEED_OPTIMIZE
    if((len-i) >= bigread) {
      m_sel->m_file.read(m_buf, MAX_BLOCKSIZE);
      writeHandshakeBlock(m_buf, MAX_BLOCKSIZE);
      i += bigread;
    } else {
      m_sel->m_file.read(m_buf, m_sel->m_blocksize * (len-i));
      writeHandshakeBlock(m_buf, m_sel->m_blocksize * (len-i));
      i = len;
    }
#else
    m_sel->m_file.read(m_buf, m_sel->m_blocksize);
    for(unsigned int j = 0; j < m_sel->m_blocksize; j++) {
      if(m_isBusReset) {
        m_phase = PHASE_BUSFREE;
        return;
      }
      writeHandshake(m_buf[j]);
    }
#endif
    if(m_isBusReset) {
      m_phase = PHASE_BUSFREE;
      return;
    }
  }
}

/* 
 * Data in phase.
 *  Send len block while reading from SD card.
 */
void writeDataPhaseRaw(uint32_t adds, uint32_t len)
{
#if READ_SPEED_OPTIMIZE_RAW
  uint32_t bigread = (MAX_BLOCKSIZE / m_sel->m_blocksize);
#endif
  uint32_t i = 0;

  //LOGN("DATAIN PHASE(RAW)");
  uint32_t pos = ((adds * m_sel->m_blocksize) / 512) + m_sel->m_firstSector;

  SET_MSG_INACTIVE();
  SET_CD_INACTIVE();
  SET_IO_ACTIVE();

  while(i < len) {
      // Asynchronous reads will make it faster ...
#if READ_SPEED_OPTIMIZE_RAW
    if((len-i) >= bigread) {
      sd.card()->readSectors(pos, m_buf, (MAX_BLOCKSIZE / 512));
      writeHandshakeBlock(m_buf, MAX_BLOCKSIZE);
      i += bigread;
      pos += (MAX_BLOCKSIZE / 512);
    } else {
      sd.card()->readSectors(pos, m_buf, ((m_sel->m_blocksize * (len-i)) / 512));
      writeHandshakeBlock(m_buf, m_sel->m_blocksize * (len-i));
      i = len;
    }
#else
    sd.card()->readSectors(pos, m_buf, (m_sel->m_blocksize / 512));
    pos++;
    for(unsigned int j = 0; j < m_sel->m_blocksize; j++) {
      if(m_isBusReset) {
        m_phase = PHASE_BUSFREE;
        return;
      }
      writeHandshake(m_buf[j]);
    }
#endif
    if(m_isBusReset) {
      m_phase = PHASE_BUSFREE;
      return;
    }
  }
}

/*
 * Data out phase.
 *  len block read
 */
void readDataPhase(int len, uint8_t* p)
{
  //LOGN("DATAOUT PHASE");
  SET_MSG_INACTIVE();
  SET_CD_INACTIVE();
  SET_IO_INACTIVE();

#if WRITE_SPEED_OPTIMIZE
  readHandshakeBlock(p, len);
#else
  for(int i = 0; i < len; i++)
    p[i] = readHandshake();
#endif
  if(m_isBusReset) {
    m_phase = PHASE_BUSFREE;
  }
}

/*
 * Data out phase.
 *  Write to SD card while reading len block.
 */
void readDataPhaseSD(uint32_t adds, uint32_t len)
{
#if WRITE_SPEED_OPTIMIZE
  uint32_t bigread = (MAX_BLOCKSIZE / m_sel->m_blocksize);
#endif
  uint32_t i = 0;

  //LOGN("DATAOUT PHASE(SD)");
  uint32_t pos = adds * m_sel->m_blocksize;
  m_sel->m_file.seek(pos);
  SET_MSG_INACTIVE();
  SET_CD_INACTIVE();
  SET_IO_INACTIVE();

  while(i < len) {
#if WRITE_SPEED_OPTIMIZE
    if((len-i) >= bigread) {
      readHandshakeBlock(m_buf, MAX_BLOCKSIZE);
      m_sel->m_file.write(m_buf, MAX_BLOCKSIZE);
      i += bigread;
    } else {
      readHandshakeBlock(m_buf, m_sel->m_blocksize * (len-i));
      m_sel->m_file.write(m_buf, m_sel->m_blocksize * (len-i));
      i = len;
    }
#else
    for(unsigned int j = 0; j <  m_sel->m_blocksize; j++) {
      if(m_isBusReset) {
        return;
      }
      m_buf[j] = readHandshake();
    }
    m_sel->m_file.write(m_buf, m_sel->m_blocksize);
#endif
  }
  m_sel->m_file.flush();
}

/*
 * Data out phase.
 *  Write to SD card while reading len block.
 */
void readDataPhaseRaw(uint32_t adds, uint32_t len)
{
#if WRITE_SPEED_OPTIMIZE_RAW
  uint32_t bigread = (MAX_BLOCKSIZE / m_sel->m_blocksize);
#endif
  uint32_t i = 0;

  //LOGN("DATAOUT PHASE(RAW)");
  uint32_t pos = ((adds * m_sel->m_blocksize) / 512) + m_sel->m_firstSector;

  SET_MSG_INACTIVE();
  SET_CD_INACTIVE();
  SET_IO_INACTIVE();

  while(i < len) {
#if WRITE_SPEED_OPTIMIZE_RAW
    if((len-i) >= bigread) {
      readHandshakeBlock(m_buf, MAX_BLOCKSIZE);
      sd.card()->writeSectors(pos, m_buf, (MAX_BLOCKSIZE / 512));
      i += bigread;
      pos += (MAX_BLOCKSIZE / 512);
    } else {
      readHandshakeBlock(m_buf, m_sel->m_blocksize * (len-i));
      sd.card()->writeSectors(pos, m_buf, ((m_sel->m_blocksize * (len-i)) / 512));
      i = len;
    }
#else
    for(unsigned int j = 0; j <  m_sel->m_blocksize; j++) {
      if(m_isBusReset) {
        return;
      }
      m_buf[j] = readHandshake();
    }
    sd.card()->writeSectors(pos, m_buf, (m_sel->m_blocksize / 512));
    i++;
    pos += (m_sel->m_blocksize / 512);
#endif
  }
  m_sel->m_file.flush();
}

/*
 * Data out phase.
 *  Verify SD card while reading len block.
 */
void verifyDataPhaseSD(uint32_t adds, uint32_t len)
{
#if WRITE_SPEED_OPTIMIZE
  uint32_t bigread = (MAX_BLOCKSIZE / m_sel->m_blocksize);
#endif
  uint32_t i = 0;

  //LOGN("DATAOUT PHASE(SD)");
  uint32_t pos = adds * m_sel->m_blocksize;
  m_sel->m_file.seek(pos);
  SET_MSG_INACTIVE();
  SET_CD_INACTIVE();
  SET_IO_INACTIVE();

  while(i < len) {
#if WRITE_SPEED_OPTIMIZE
    if((len-i) >= bigread) {
      readHandshakeBlock(m_buf, MAX_BLOCKSIZE);
      //m_sel->m_file.verify(m_buf, MAX_BLOCKSIZE);
      i += bigread;
    } else {
      readHandshakeBlock(m_buf, m_sel->m_blocksize * (len-i));
      //m_sel->m_file.verify(m_buf, m_sel->m_blocksize * (len-i));
      i = len;
    }
#else
    for(unsigned int j = 0; j <  m_sel->m_blocksize; j++) {
      if(m_isBusReset) {
        return;
      }
      m_buf[j] = readHandshake();
    }
    //m_sel->m_file.verify(m_buf, m_sel->m_blocksize);
#endif
  }
}


/*
 * Data out phase.
 *  Verify SD card while reading len block.
 */
void verifyDataPhaseRaw(uint32_t adds, uint32_t len)
{
#if WRITE_SPEED_OPTIMIZE_RAW
  uint32_t bigread = (MAX_BLOCKSIZE / m_sel->m_blocksize);
#endif
  uint32_t i = 0;

  //LOGN("DATAOUT PHASE(RAW)");
  //uint32_t pos = ((adds * m_sel->m_blocksize) / 512) + m_sel->m_firstSector;
  SET_MSG_INACTIVE();
  SET_CD_INACTIVE();
  SET_IO_INACTIVE();

  while(i < len) {
#if WRITE_SPEED_OPTIMIZE_RAW
    if((len-i) >= bigread) {
      readHandshakeBlock(m_buf, MAX_BLOCKSIZE);
      i += bigread;
    } else {
      readHandshakeBlock(m_buf, m_sel->m_blocksize * (len-i));
      i = len;
    }
#else
    for(unsigned int j = 0; j <  m_sel->m_blocksize; j++) {
      if(m_isBusReset) {
        return;
      }
      m_buf[j] = readHandshake();
    }
#endif
  }
}
