/*         $Id: logger.c 1.4 2003/05/10 01:57:06 leon Exp leon $         */
/*\
  SmartMedia logging via serial port with the AT89C2051 at 11.095MHz

  Author: Leon Kos, University of Ljubljana
  Compiler: KEIL C 5.0
  CODE size: approx 1700 bytes
  DATA Size: 23 bytes + stack + 1 bit

  64MBytes or more of ASCII log space.
  We assume that SmartMedia device is error free :-).

  Check 5th byte in spare area for each block to be sure.

  Logger works as pass-through serial ascii logger. No flow
  control is assumed. Note that at speeds higher then 9600 baud
  framing error can occur! I suggest serial RX/TX interrupt redesign.
  When escape command is received several buffer operation
  can be performed.

  The following escape commands are recognised:

  @I  Print usage statistics
  @P  Print log. Last page is flushed.
  @F  Fast print log at 57600 baud.
  @E  Erase log


\*/
   
#pragma SYMBOLS

#include <reg51.h>

/* Port 3 SmartMedia control pins */

sbit  CE_  = 0xB5;
sbit  WE_  = 0xB4;
sbit  RE_  = 0XB7;
sbit  CLE  = 0xB2;
sbit  ALE  = 0xB3;

#if 1 /* 64MB device */
#  define PAGES 131072UL
#  define BLOCKS 4096
#else /* 128MB device */
#  define PAGES 262144UL
#  define BLOCKS 8192
#endif


#define PAGE 528 /* We use also spare area for storage */
#define WRITE(x) {WE_ = 0; P1 = (x); WE_ = 1;}
#define PUTCHAR(c) {while (!TI); TI = 0; SBUF = (c);}



void print(char code *msg)
{
  while (*msg)
    {
      if (*msg == '\n')
        PUTCHAR(0x0d);
      PUTCHAR(*msg);
      msg++;
    }
}


void cmd(unsigned char command)
{
  CLE = 1;
  P1 = command;
  WE_ = 0;
  WE_ = 1;
  CLE = 0;
}

unsigned char status_read(void)
{
  unsigned char status;
  cmd(0x70);
  P1 = 0xff;
  RE_ = 0;
  status = P1;
  RE_ = 1;
  return status;
}

void start_address(unsigned long page)
{
  ALE = 1;
  WRITE(0);
  WRITE(page);
  WRITE(page>>8);
  WRITE(page>>16);
  ALE = 0;
}

/* RY/BY and CE_ pin connected together ! */
static void ready_not_busy(void)
{
  int i;
  CE_ = 1;
  for (i = 0; i < 20000; i++)
    if (CE_) break;
  CE_ = 0;
}

static void print_buffer(void)
{
  unsigned long page;

  for (page = 0; page < PAGES; page++)
    {
      int i;

      cmd(0x00); /*  read the page */
      start_address(page);
      P1 = 0xff;
      ready_not_busy(); /* wait for page transfer */
      for (i = 0; i < PAGE; i++)
        {
          RE_ = 0;
          if (P1 == 0xff) /* this is END mark */
            {
              RE_ = 1;
              return;
            }
          if (P1 != 0x00) /* empty bytes */
            PUTCHAR(P1);
          RE_ = 1;
        }
    }
}

/* Fill page with NULs and program it */
void flush(int i)
{
  int j;
  for (j = i; j < PAGE; j++)
    WRITE(0x00);
  cmd(0x10);
  ready_not_busy();
}


/* Returns first non empty page */
unsigned long append(void)
{
  unsigned long page;

  for (page = 0; page < PAGES; page++)
    {
      cmd(0x00); /*  read the page */
      start_address(page);
      P1 = 0xff;
      ready_not_busy(); /* wait for page transfer */
      RE_ = 0;
      if (P1 == 0xff)
        {
          RE_ = 1;
          return page;

        }
      RE_ = 1;
    }
  return page; /* overflow ! */
}

void print_long(unsigned long x)
{
  char buf[8];
  char i = 0;
  do /* print long type */
    {
      buf[i] = x % 10;
      x /= 10;
      i++;
    }
  while (x);

  while (i--)
    PUTCHAR(buf[i] + '0');

}

static void print_info(unsigned long pages)
{
  print("\n\nSmartMedia usage information:\n");
  print_long(pages);
  print(" of total ");
  print_long(PAGES);
  print(" pages used\n");
  pages *= 100;
  pages /= PAGES;
  print_long(pages);
  print("% of pages used\n");
}


void erase_all(void)
{
  unsigned int block;
  print("Erasing...");
  for (block = 0; block < BLOCKS; block++)
    {
      cmd(0x60);    /* Block erase */
      ALE = 1;
      WRITE(block);
      WRITE(block>>8);
      WRITE(block>>16);
      ALE = 0;
      cmd(0xD0);
      ready_not_busy();
      if (status_read() & 0x01)
        PUTCHAR('x');
    }
  print("DONE\n");
}


#if 0
#include <stdio.h>
static void test_format(void)
{
  unsigned int page;
  char buf[16];

  print("Formating pages...");
  for (page = 0; page < 1000; page++)
    {
      int i;
      sprintf(buf, "Page: %04x\r\n", page);
      cmd(0x80); /* page program */
      start_address(page);
      ready_not_busy();
      for (i = 0; buf[i]; i++)
        WRITE(buf[i]);
      for (i = 0; i < PAGE - 12; i++)
        WRITE(0x00);
      cmd(0x10);
      ready_not_busy();
      if (status_read() & 0x01)
        printf("Failed to program page %04x\n", page);
    }
  print("DONE\n");
}
#endif


void main()
{
  int i;
  long page;
  bit escape;

  /* initialize serial interface */
  SCON = 0x50;  /* Mode 1: 8bit UART and receiver enable */
  PCON &= 0x7F; /* Clear SMOD bit in power ctrl reg  */
  TMOD &= 0xCF; /* Clear M1 and M0 for timer 1 */
  TMOD |= 0x20; /* set M1 for 8 bit autoreload */
  TH1 = 0xFD;   /* Autoreload value for 9600 baud */
  TR1 = 1;      /* Start timer 1 */
  TI = 1;       /* Set transmit indicator to ready */

  /* Initialize bus */
  CLE = 0;
  ALE = 0;
  WE_ = 1;
  RE_ = 1;
  CE_ = 1;  /* Chip enable and Ready/Busy pin */
  P1 = 0xFF;
  
  CE_ = 0; /* Enable SmartMedia */
  cmd(0xFF); /* reset */
  ready_not_busy();
  page = append();

#if 0
  erase_all();
  test_format();
  print_buffer();
  while (1);
#endif

  CE_ = 1; /* Disable SmartMedia */

  i = 0;
  escape = 0; /* escape command received */
  while (1)
    {
      if (RI) /* pull receive buffer */
        {
          RI = 0;
          if (SBUF == '@') /* Escape command issued ? */
            {
              escape = 1;
              continue;
            }
          if (escape)
            {
              CE_ = 0;
              switch (SBUF)
                {
                 case 'F': /* Fast print */
                    PCON |= 0x80; /* SMOD = 1; */
                    TH1 = 0xff;   /* 57600 baud */
                    if (i)
                      {
                        flush(i);
                        page++;
                      }
                    print_buffer();
                    PCON &= 0x7f; /* SMOD = 0; */
                    TH1 = 0xfd;   /* 9600 baud */
                    i = 0;
                    break;
                  case 'P': /* Print */
                    if (i)
                      {
                        flush(i);
                        page++;
                      }
                    print_buffer();
                    i = 0; /* Start over with new page */
                    break;
                  case 'E': /* Erase */
                    erase_all();
                    page = 0;
                    i = 0;
                    break;
                  case 'I': /* Print info */
                    print_info(page);
                    break;
                 default:
                    print("Unknown escape command\n");
                }
              CE_ = 1;
              escape = 0;
              continue;
            }

          while (!TI); /* Wait for transmit to clear */
          TI = 0;
          SBUF = SBUF; /* Copy RX to TX */
          if (page == PAGES)
            {
              print("\n# SmartMedia logger full!\n");
              continue;
            }
          CE_ = 0;
          if (i == 0) /* Begin page write */
            {
              cmd(0x80);
              start_address(page);
              ready_not_busy();
            }
          WRITE(SBUF);
          i++;
          if (i == PAGE) /* program page */
            {
              cmd(0x10);
              i = 0;
              ready_not_busy();
              if (status_read() & 0x01)
                print("Error programming page!\n");
              page ++;
            }
          CE_ = 1;
        }
    }
}