/* spi.c - SPI interface for file system. Taken from intbasic3.
 *  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 "spi.h"
#include "serial.h"


// First two bytes in the sector top bytes.
__xdata uint16_t nsector;


static uint8_t spiCmd(uint8_t cmd)
//   The caller sent an SPI command. We wait for it to finish and then read
// the value returned into acc which is usually ignored.
{
  SPI0DAT = cmd;
  while (!SPIF);
  SPIF = 0;
  return SPI0DAT;
}



static void spiWaitBusy(void)
//     Busy wait for SPI data flash to finish. Check status of read/write
// and return. Though there are two status bytes, the top bit in each indicates
// that the device is busy.
{
  SPI_CS = 0;
  spiCmd(SPI_STATUS);
  spiCmd(0);
  while ((spiCmd(0) & 0x80) == 0);
  SPI_CS = 1;

  /*  uint8_t s;
  SPI_CS = 0;
  spiCmd(SPI_STATUS);
 loop:
  s = spiCmd(0);
  if (s != 0x88 && s != 0xA4) { puthexbyte(s); putch(' '); }
  if ((s & 0x80) == 0) goto loop;
  SPI_CS = 1;
  puthexbyte(s); terpri();
  */
}



void spiWriteSector(uint16_t s, uint8_t __xdata *buf)
// Write sector s from the buffer given that is assumed to be 256 bytes. This
// allows us up to 8 MB. Write the two upper bytes from nsector.
{
  LED0 = 1;                     // LED0 indicates activity.
  s *= 2;                       // Not sure why.
  SPI_CS = 0;                   // Turn chip on.
  spiCmd(SPI_WRITE_BUF);        // Write command with erase.
  spiCmd(s >> 8);               // High bits of sector address.
  spiCmd(s & 0xFF);             // Low bits of sector address.
  spiCmd(0);                    // Byte offset.
  for (s = 0; s < 256; ++s) spiCmd(*buf++);
  spiCmd(nsector >> 8);         // Extension bytes.
  spiCmd(nsector);
  SPI_CS = 1;                   // This starts the write.
  spiWaitBusy();                // Wait for completion.
  LED0 = 0;                     // Turn the LED off.
}



void spiReadSector(uint16_t j, uint8_t __xdata *buf)
// Read the sector in address DPTR into buffer sector. Returns next sector
// address.
{
  LED0 = 1;
  j *= 2;
  SPI_CS = 0;                  // Enable chip.
  spiCmd(SPI_READ);            // We're going to read.
  spiCmd(j >> 8);              // High sector address.
  spiCmd(j & 0xFF);            // Low sector address.
  spiCmd(0);                   // Byte offset.

  for (j = 0; j < 256; ++j) *buf++ = spiCmd(0);
  
  nsector = spiCmd(0) << 8;    // The next sector in to 2 bytes.
  nsector |= spiCmd(0);
  SPI_CS = 1;                   // Turn chip off.
  LED0 = 0;
}


void spiErase(void)
//     Do the full chip erase and wait for the result. This can be a few
// seconds for the 1 MB chip.
{
  putstring("Erasing file system...");
  LED0 = 1;                     // Show that we're busy.
  SPI_CS = 0;                   // Enable chip.
  spiCmd(SPI_ERASE1);           // The erase full chip code.
  spiCmd(SPI_ERASE2);
  spiCmd(SPI_ERASE3);
  spiCmd(SPI_ERASE4);
  SPI_CS = 1;                   // Start the erase.
  spiWaitBusy();
  LED0 = 0;
  terpri();
}



void spiID(void)
//   Display the device identifier.
{
  putstring("  Flash ID ");
  LED0 = 1;
  SPI_CS = 0;
  spiCmd(SPI_DEVID);
  puthexbyte(spiCmd(0));
  putch(' ');
  puthexbyte(spiCmd(0));
  putch(' ');
  puthexbyte(spiCmd(0));
  putch(' ');
  puthexbyte(spiCmd(0));
  SPI_CS = 1;
  terpri();
  LED0 = 0;
}


/*
void spi264(void)
{
  LED0 = 1;
  SPI_CS = 0;
  spiCmd(0x3D);
  spiCmd(0x2A);
  spiCmd(0x80);
  spiCmd(0xA7);
  SPI_CS = 1;
  LED0 = 0;
}
*/


// EOF
