/* For free support for VSIDE, please visit www.vsdsp-forum.com */

// Starting point template for creating VSOS3 libraries and device drivers.
// This will create a <projectname>.DL3 file, which you can copy to 
// your VSRV Developer Board's system disk's SYS subdirectory.

// If init(), main() or fini() are not needed, remove them from the solution.
// There's no need to have any unneeded functions in the library.

#include <vo_stdio.h>
#include <volink.h>     // Linker directives like DLLENTRY
#include <apploader.h>  // RunLibraryFunction etc
#include <string.h>
#include <stdlib.h>
#include <kernel.h>
#include <clockspeed.h>
#include <scheduler.h>
#include <uartspeed.h>
#include <vsrv.h>
#include <ringbuf.h>
#include <imem.h>
#include <vsos.h>
#include <consolestate.h>


int hexMode = 0;
int slowTx = 0;

/* If one below a power of two, memory usage efficiency is best */
#define UARTRV_RX_BUFFER_SIZE (4096-1)
/* If one below a power of two, memory usage efficiency is best */
#define UARTRV_TX_BUFFER_SIZE (256-1)
__mem_y char __align uartRvRxBuffer[UARTRV_RX_BUFFER_SIZE];
__mem_y char __align uartRvTxBuffer[UARTRV_TX_BUFFER_SIZE];

struct UartRv {
  FILE *origIn, *origOut;
  u_int16 origIntEna0Mask;
  u_int32 origRxVec, origTxVec;
  __mem_y char *rxRd, *rxWr, *txRd, *txWr;
} uartRv = {
  0, 0, 0, 0, 0, uartRvRxBuffer, uartRvRxBuffer,
  uartRvTxBuffer, uartRvTxBuffer
};



extern u_int16 IntUartRvRxVector;
extern u_int16 IntUartRvTxVector;

void IntUartRvTxC(void) {
  while (!(PERIP(UARTRV_STATUS) & UART_ST_TXFULL) && uartRv.txWr != uartRv.txRd) {
    PERIP(UARTRV_DATA) = *uartRv.txRd;
    uartRv.txRd = ringmodY(uartRv.txRd, MAKEMODF(UARTRV_TX_BUFFER_SIZE));
  }
}

void IntUartRvRxC(void) {
  *uartRv.rxWr = PERIP(UARTRV_DATA);
  uartRv.rxWr = ringmodY(uartRv.rxWr, MAKEMODF(UARTRV_RX_BUFFER_SIZE));
}



void UartRvPutChar(register char c) {
  __mem_y char *nextTxWr =
    ringmodY(uartRv.txWr, MAKEMODF(UARTRV_TX_BUFFER_SIZE));

  while (nextTxWr == uartRv.txRd) {
    Delay(1);
  }

  Disable();
  if (!(PERIP(UARTRV_STATUS) & UART_ST_TXFULL) && uartRv.txWr == uartRv.txRd) {
    PERIP(UARTRV_DATA) = c;
  } else {
    *uartRv.txWr = c;
    uartRv.txWr = nextTxWr;
  }
  Enable();
}


void SendChar(register int c) {
  if (slowTx) {
    static u_int32 lastTime = 0;
    static int longPause = 0;
    u_int32 now = ReadTimeCount();
    u_int32 needTime = longPause ? TICKS_PER_SEC*2 : TICKS_PER_SEC/10;
  
    if (now - lastTime > 5*TICKS_PER_SEC) {
      longPause = 1;
    } else {
      longPause = 0;
      if (now - lastTime < needTime) {
	//      printf("(%ld/%ld)", needTime-(now-lastTime), needTime);
	Delay(needTime - (now-lastTime));
      }
    }
    lastTime = ReadTimeCount();
  }
  if (hexMode) {
    printf("\ntx %02x ", c);
  }
  UartRvPutChar(c);
}


char UartRvGetChar(void) {
  char ch;
  while (uartRv.rxRd == uartRv.rxWr) {
    Delay(1);
  }
  ch = *uartRv.rxRd;
  uartRv.rxRd = ringmodY(uartRv.rxRd, MAKEMODF(UARTRV_RX_BUFFER_SIZE));
  return ch;
}

extern SIMPLE_FILE uartRvInFile;

IOCTL_RESULT UartRvIoctl(register __i0 VO_FILE *self, s_int16 request, IOCTL_ARGUMENT arg) {
  if (request == IOCTL_TEST) {
    int res;
    if ((void *)self == &uartRvInFile) {
      if ((res = uartRv.rxWr-uartRv.rxRd) < 0) res += UARTRV_RX_BUFFER_SIZE;
    } else {
      if ((res = uartRv.txWr-uartRv.txRd) < 0) res += UARTRV_TX_BUFFER_SIZE;
    }
    return res;
  }
  return S_ERROR;
};

u_int16 UartRvWrite(register __i0 VO_FILE *self, void *buf, u_int16 sourceIndex, u_int16 bytes) {
  SendChar(*((u_int16 *)buf)); //Send character to raw UART port
  return 1;
};

u_int16 UartRvRead(register __i0 VO_FILE *self, void *buf, u_int16 destinationIndex, u_int16 bytes) {
  char ch = UartRvGetChar();
  /* Hack: Force vo_fgetc() to get 16-bit value in case UTF16 code doesn't
     fit into values 0..255 */
  if (destinationIndex == 1 && bytes == 1) {
    destinationIndex = 0;
    bytes = 2;
  } else {
    destinationIndex = bytes = 1;
  }
  MemCopyPackedBigEndian(buf, destinationIndex, &ch, destinationIndex, bytes);
  return 1;
};

extern u_int16 IntMUartRvRxVector;
extern u_int16 IntUartRvTxVector;

void UartRvInit(char *parameters) {
  uartRv.origIntEna0Mask = PERIP(INT_ENABLE0_HP) | ~(INTF_UARTRV_TX|INTF_UARTRV_RX);
  uartRv.origRxVec = ReadIMem((0x20+INTV_UARTRV_RX));
  uartRv.origTxVec = ReadIMem((0x20+INTV_UARTRV_TX));
#if 0
  uartRv.origIn = vo_stdin;
  uartRv.origOut = vo_stdout;
#endif
}

// Library finalization code. This is called when DropLibrary is called
void UartRvFini(void) {
  PERIP(INT_ENABLE0_HP) &= uartRv.origIntEna0Mask;
  WriteIMem((0x20+INTV_UARTRV_RX), uartRv.origRxVec);
  WriteIMem((0x20+INTV_UARTRV_TX), uartRv.origTxVec);
#if 0
  vo_stdin = uartRv.origIn;
  vo_stdout = uartRv.origOut;
#endif
}





const FILEOPS ConsoleInFileOperations = { NULL, NULL, UartRvIoctl, UartRvRead, UartRvWrite };
//DLLENTRY(uartInFile)
SIMPLE_FILE uartRvInFile = {__MASK_PRESENT | __MASK_OPEN | __MASK_READABLE | __MASK_WRITABLE | __MASK_FILE, NULL, &ConsoleInFileOperations};

const FILEOPS ConsoleOutFileOperations = { NULL, NULL, UartRvIoctl, UartRvRead, UartRvWrite };
//DLLENTRY(uartOutFile)
SIMPLE_FILE uartRvOutFile = {__MASK_PRESENT | __MASK_OPEN | __MASK_READABLE | __MASK_WRITABLE | __MASK_FILE, NULL, &ConsoleOutFileOperations};







#define MAX_ESC_SEQ_LEN 5

char escSeqBuf[MAX_ESC_SEQ_LEN];
int escSeqLen = 0;

struct EscSeq {
  int len;
  int retCode;
  char seq[MAX_ESC_SEQ_LEN];
} escSeq[] = {
  {3, -16, {0x1b, 0x4f, 0x51,   -1,   -1 }}, /*  0:  F2 */
  {3, -15, {0x1b, 0x4f, 0x52,   -1,   -1 }}, /*  1:  F3 */
  {3, -14, {0x1b, 0x4f, 0x53,   -1,   -1 }}, /*  2:  F4 */
  {5, -16, {0x1b, 0x5b, 0x31, 0x32, 0x7e }}, /*  3:  F2 */
  {5, -15, {0x1b, 0x5b, 0x31, 0x33, 0x7e }}, /*  3:  F3 */
  {5, -14, {0x1b, 0x5b, 0x31, 0x34, 0x7e }}, /*  3:  F4 */
  {5, -13, {0x1b, 0x5b, 0x31, 0x35, 0x7e }}, /*  3:  F5 */
  {5, -12, {0x1b, 0x5b, 0x31, 0x37, 0x7e }}, /*  4:  F6 */
  {5, -11, {0x1b, 0x5b, 0x31, 0x38, 0x7e }}, /*  5:  F7 */
  {5, -10, {0x1b, 0x5b, 0x31, 0x39, 0x7e }}, /*  6:  F8 */
  {5,  -9, {0x1b, 0x5b, 0x32, 0x30, 0x7e }}, /*  7:  F9 */
  {5,  -8, {0x1b, 0x5b, 0x32, 0x34, 0x7e }}, /*  8: F12 */
  {3,  -7, {0x1b, 0x5b, 0x41,   -1,   -1 }}, /*  9: CUp */
  {3,  -6, {0x1b, 0x5b, 0x42,   -1,   -1 }}, /* 10: CDn */
  {3,  -5, {0x1b, 0x5b, 0x43,   -1,   -1 }}, /* 11: CRg */
  {3,  -4, {0x1b, 0x5b, 0x44,   -1,   -1 }}, /* 12: CLf */
};

void PurgeEscBuf(register int dump) {
#if 0
  printf("*PurgeEscBuf %d*", escSeqLen);
#endif
  if (dump) {
    int i;
    for (i=0; i<escSeqLen; i++) {
      SendChar(escSeqBuf[i]);
    }
  }
  escSeqLen = 0;
}

int HandleEscBuf(register int c) {
 do_again:
  /* Do we collect data to our escape buffer? */
  if (escSeqLen || c == 0x1b) {
    int i, canBeAny = 0, match = -1;
    struct EscSeq *es = escSeq;
    escSeqBuf[escSeqLen++] = c;
    for (i=0; i<sizeof(escSeq)/sizeof(escSeq[0]); i++) {
      if (!memcmp(escSeqBuf, es->seq, escSeqLen)) {
	/* Our current escape sequency may be a part of a longer one. */
	canBeAny = 1;
	if (escSeqLen == es->len) {
	  /* Match: this is the escape sequence */
	  c = es->retCode;
	  PurgeEscBuf(0);
	  goto finally;
	}
      }
      es++;
    }
    /* If this escape sequence cannot be a part of any of the ones we
       recognize, dump it with the exception of the last, imcompatible
       character, that will be handled again. */
    if (!canBeAny/* || escSeqLen > MAX_ESC_SEQ_LEN*/) {
      escSeqLen--;
      PurgeEscBuf(1);
      /* After clearing buffer, we still have the current character
	 unhandled, and as it might be ESC, make a second round for c. */
      goto do_again;
    } else {
      c = -1; /* Buffer is being filled, no output yet */
    }
  }

 finally:
  return c;
}



ioresult main(char *parameters) {
  char *p = parameters;
  int nParam;
  int verbose = 1, i;
  int retCode = S_ERROR;
  u_int16 divider = 0;
  int quit = 0;
  u_int32 lastInputCharTime = 0;
  
  UartRvInit(NULL);
  PurgeEscBuf(0);

#if 0
  divider = GetUartRegister(clockSpeed.cpuClkHz, 1500000);
  PERIP(UARTRV_DIV) = divider;
  PERIP(UARTRV_DATA) = 'a';
  while (ioctl(stdin, IOCTL_TEST, NULL) == 0) {
    if (PERIP(UARTRV_STATUS) & UART_ST_RXFULL) {
      printf("Got char 0x%x\n", PERIP(UARTRV_DATA));
    }
  }
#endif
  
  nParam = RunProgram("ParamSpl", parameters);

  for (i=0; i<nParam; i++) {
    if (!strcmp(p, "-v")) {
      verbose = 1;
    } else if (!strcmp(p, "+v")) {
      verbose = 0;
    } else if (!strncmp(p, "-s", 2)) {
      u_int32 speed = strtol(p+2, NULL, 0);
      SetUartSpeed(speed, 10, UARTRV_STATUS);
      printf("Set UARTRV speed to %ld bps, div 0x%04x\n",
	     speed, PERIP(UARTRV_DIV));
    } else if (!strcmp(p, "-h")) {
      printf("Usage: Term [-v|+v|-a|-b|-s|+s|-h]\n"
	     "-v|+v\tVerbose on|off\n"
	     "-sx\tSet bit speed to x bps\n"
	     "-h\tShow this help\n"
	     "\nConsole keys:\n"
	     "\tF4 - Exit\n"
	     "\tF5 - Toggle Hex Mode\n"
	     "\tF6 - Toggle Slow TX mode\n"
	     );
      goto finally_ok;
    } else {
      printf("E: Unknown parameter \"%s\".\n", p);
      goto finally;
    }
    p += strlen(p)+1;
  }

  /* in */
#if 1
  WriteIMem((0x20+INTV_UARTRV_RX), ReadIMem((u_int16)(&IntUartRvRxVector)));
  PERIP(INT_ENABLE0_HP) |= INTF_UARTRV_RX;
  //vo_stdin = &uartMuxInFile;
#endif
  
  /* out */
#if 1
  WriteIMem((0x20+INTV_UARTRV_TX), ReadIMem((u_int16)(&IntUartRvTxVector)));
  PERIP(INT_ENABLE0_HP) |= INTF_UARTRV_TX;
  //vo_stdout = &uartRvOutFile;
#endif

  
  divider = GetUartRegister(clockSpeed.cpuClkHz, 4000000);
  divider = 12;
#if 0
  printf("divider = 0x%04x\n", divider);
  PERIP(UARTRV_DIV) = divider;
#endif
#if 0
  PERIP(VSRV1_CF) |= VSRV1_CF_MUX;
#endif
  
  while (!quit) {
#if 0
    while (PERIP(UARTRV_STATUS) & UART_ST_RXFULL) {
      int c = PERIP(UARTRV_DATA);
      if (hexMode) {
	printf("RVRX %02x\n", c);
      } else {
	printf("%c", c);
      }
    }
#else
    int n;
    while ((n = UartRvIoctl(&uartRvInFile, IOCTL_TEST, NULL) > 0)) {
      int i;
      for (i=0; i<n; i++) {
	printf("%c", UartRvGetChar());
      }
    }
#endif
      
#if 1
    if (ReadTimeCount() - lastInputCharTime > TICKS_PER_SEC && escSeqLen) {
      PurgeEscBuf(1);
    }
    if (ioctl(stdin, IOCTL_TEST, 0) > 0) {
      int c = fgetc(stdin);
      lastInputCharTime = ReadTimeCount();
      c = HandleEscBuf(c);
      switch (c) {
      case -14:
	printf("*EXIT TERMINAL*\n");
	quit = 1;
	break;
      case -16:
#if 0
	printf("INT_ENA1:0 = %04x:%04x\n",
	       PERIP(INT_ENABLE1_HP)|PERIP(INT_ENABLE1_LP),
	       PERIP(INT_ENABLE0_HP)|PERIP(INT_ENABLE0_LP));
	printf("origRx %08lx, origTx %08lx\n",
	       uartRv.origRxVec, uartRv.origTxVec);
	printf("rxRd %04x, rxWr %04x, txRd %04x, txWr %04x\n",
	       uartRv.rxRd, uartRv.rxWr, uartRv.txRd, uartRv.txWr);
#endif
#if 0
	printf("MUX VSDSP\n");
	PERIP(VSRV1_CF) |= VSRV1_CF_MUX;
#endif
	break;
      case -15:
#if 0
	printf("MUX RV\n");
	PERIP(VSRV1_CF) &= ~VSRV1_CF_MUX;
#endif
	break;
      case -13:
	hexMode = 1-hexMode;
	printf("hexMode = %d\n", hexMode);
	break;
      case -12:
	slowTx = 1-slowTx;
	printf("slowTx = %d\n", slowTx);
	break;
      case -7:
	SendChar(0x10); /* CUp */
	break;
      case -6:
	SendChar(0x0e); /* CDn */
	break;
      case -5:
	SendChar(0x06); /* CRg */
	break;
      case -4:
	SendChar(0x02); /* CLf */
	break;
#if 0
      case 'V'-('A'-1):
	PERIP(UARTRV_DIV) = divider+1;
	printf("Slower speed 0x%04x\n", PERIP(UARTRV_DIV));
	break;
      case 'W'-('A'-1):
	PERIP(UARTRV_DIV) = divider;
	printf("Nominal speed 0x%04x\n", PERIP(UARTRV_DIV));
	break;
      case 'U'-('A'-1):
	PERIP(UARTRV_DIV) = divider-1;
	printf("Slower speed 0x%04x\n", PERIP(UARTRV_DIV));
	break;
#endif
      case -1:
	/* Esc buf handled the character already. */
	break;
      default:
	SendChar(c);
	break;
      }
    }
#endif
#if 0
    if (PERIP(UARTRV_STATUS) & UART_ST_RXFULL) {
      int c = PERIP(UARTRV_DATA);
      if (hexMode) {
	printf("RVRX! %02x\n", c);
      } else {
	printf("%c", c);
      }
    }
#endif
#if 0
    if (PERIP(UARTRV_STATUS) & UART_ST_FRAMERR) {
      static int n = 0;
      printf("RV FRAME ERROR %d!\n", n++);
      PERIP(UARTRV_STATUS) &= ~UART_ST_FRAMERR;
    }
#endif
#if 0
    printf("%04x %04x\n", PERIP(UARTRV_STATUS), PERIP(UARTRV_STATUS));
#endif
  }

 finally_ok:
  retCode = S_OK;
 finally:
  UartRvFini();

  return retCode;
}

