/* serial.c = serial port code, uses timer 2.
 *  Copyright (C) 2022 by Jed Marti
 *   This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see https://www.gnu.org/licenses/.
 */

#include <C8051F380.h>
#include <stdint.h>

#include "config.h"
#include "serial.h"


// Initialize UART0 for 48 MHz clock and baud rates 2400, 14400, 9600, 57600,
// 28800, 115.2k, 230.4k baud.
void initUART0(void)
{
#if SYSCLK == 48000000
  #if UART0_BAUD_RATE == 230400 || UART0_BAUD_RATE == 115200
  CKCON |= 0x08;                // Timer 1 clock source is SYSCLK.
  TMOD &= ~0xD0;                // Timer 1 in 8 bit auto-reload mode.
  TMOD |= 0x20;
  TH1 = -(SYSCLK / UART0_BAUD_RATE / 2);
  TL1 = TH1;

  #elif UART0_BAUD_RATE == 57600 || UART0_BAUD_RATE == 28800
  CKCON |= 0x01;                // Timer 1 clock source is SYSCLK / 4.
  TMOD &= ~0xD0;                // Timer 1 in 8 bit auto-reload mode.
  TMOD |= 0x20;
  TH1 = -((SYSCLK / 4) / UART0_BAUD_RATE / 2);
  TL1 = TH1;
 
  #elif UART0_BAUD_RATE == 14400 || UART0_BAUD_RATE == 9600
  TMOD &= !0xD0;                // Timer 1 8 bit auto reload SYSCLK / 12.
  TMOD |= 0x20;
  TH1 = -((SYSCLK / 12) / UART0_BAUD_RATE / 2);
  TL1 = TH1;

  #elif UART0_BAUD_RATE == 2400
  CKCON |= 0x02;                // Timer 1 clock source SYSCLK / 48.
  TMOD &= !0xD0;                // Timer 1 8 bit auto reload SYSCLK / 12.
  TMOD |= 0x20;
  TH1 = -((SYSCLK / 48) / UART0_BAUD_RATE / 2);
  TL1 = TH1;

  #else
  #error UART0: Baud rate not supported at 48 mhtz for 380 series.
  #endif
#else
  #error Only 48 MHz clock rate supported.
#endif
  TR1 = 1;                      // Enable Timer 1.
  REN0 = 1;                     // Enable UART0 receiver.
}


void putch(unsigned char v)
// Write a character. LED1 blinks while the write is busy.
{
  LED1 = 1;
  SBUF0 = v;
  while (TI0 == 0);
  TI0 = 0;
  LED1 = 0;
}



void putstring(char *s)
// Write a zero terminated string.
{
  while (*s != 0) putch(*s++);
}


void putbyte(uint8_t v)
// Send a single byte as zero suppressed 3 digit value.
{
  __bit z = 0;
  if (v >= 100) { putch(v / 100 + '0'); v %= 100; z = 1; }
  if (z || v >= 10) { putch(v / 10 + '0'); v %= 10; }
  putch(v + '0');
}



void putuint16(uint16_t v)
// Short unsigned int to the serial port. 0-131071.
{
  __bit z = 0;
  if (v >= 10000L) { putch(v / 10000 + '0'); v %= 10000L; z = 1; }
  if (z || v >= 1000) { putch(v / 1000 + '0'); v %= 1000; z = 1; }
  if (z || v >= 100) { putch(v / 100 + '0'); v %= 100; z = 1; }
  if (z || v >= 10) { putch(v / 10 + '0'); v %= 10; }
  putch(v + '0');
}


void putint16(int16_t v)
// Signed short int to the serial port.
{
  if (v < 0) { putch('-'); v = - v; }
  putuint16((uint16_t) v);
}


// Used for hexbyte.
static void puthexnib(uint8_t n)
{
  if (n >= 10) putch(n - 10 + 'A');
  else putch(n + '0');
}


void puthexbyte(uint8_t v)
// Single unsigned byte in hexadecimal.
{
  puthexnib(v >> 4);
  puthexnib(v & 0x0F);
}


void puthex16(uint16_t v)
// 16 bit value.
{
  putstring("0x");
  puthexnib(v >> 12);
  puthexnib((v >> 8) & 0x0F);
  puthexnib((v >> 4) & 0x0F);
  puthexnib(v & 0x0F);
}


void terpri(void)
// Terminate the output line.
{
  putch('\n');
}


uint8_t getch(void)
// Wait for a character and return it. LED1 on whilst waiting.
{
  uint8_t ch;
  LED1 = 1;
  while (RI0 == 0);
  ch = SBUF0;
  RI0 = 0;
  LED1 = 0;
  return ch;
}



uint8_t getline(char *buf, uint8_t sz)
// Get a line into the buffer with maximum size of sz - 1. Returns the number 
// of characters in the buffer. Back space possible. If buffer overflow, 
// terminate and return.
{
  uint8_t i;
  uint8_t c;

  --sz;

  for (i = 0; i < sz; )
  {
    c = getch();

    // CR signals end of line.
    if (c == 13)
    {
      buf[i] = 0;
      putch('\n');
      return i;
    }

    // Backspace if possible.
    else if (c == 8)
    {
      if (i > 0)
      {
        putch(8);
        putch(' ');
        putch(8);
        --i;
      }
    }

    // Delete line is ^U (21) or escape (27)
    else if (c == 21 || c == 27)
    {
      putstring(" ...\n");
      i = 0;
      putch('>');
    }

    // Otherwise save the character.
    else
    {
      buf[i++] = c;
      putch(c);
    }
  }
  buf[i] = 0;
  putch('\n');
  return i;
}



// EOF
