/* run.c - Parse and run a program.
 *  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 "lfxmit.h"
#include "serial.h"
#include "storage.h"
#include "fs.h"
#include "lex.h"



// The I space.
extern __xdata uint8_t  I[];                 // Instruction space (see program.c)
extern __xdata uint16_t PMAX;                // Last instruction (see program.c).
__xdata uint16_t        PC;
extern __bit            errorThrown;
extern uint8_t          SPTR;

// Set 1 to execute, 0 to ignore.
__bit doit;
__bit stopBit;

// Lexical interface.
extern __xdata uint8_t  ttype;
extern __xdata char     STR[];
extern __xdata uint8_t  infh;

void runStatement(void)
{
  if (stopBit || errorThrown) return;
  
  // puthex16(PC); putstring(": "); puthexbyte(I[PC]); terpri();
  SPTR = 0;
  switch (getPByte()) {

  // Ignore semicolons here, they may be left in place elsewhere.
  case SEMICOLON:
    incPC();
    return;

  // Stop sets the bit and returns.
  case STOP:
    if (doit)
    {
      putstring("STOP at ");
      puthex16(PC);
      terpri();
      stopBit = 1;
    }
    break;

  // The statements by their keywords.
  case FOR: runFOR(); break;
  case FREQUENCY:  runFrequency(); break;
  case IF: runIf(); break;
  case LET: runLet(); break;
  case PRINT: print(); break;
  case LCBRACKET: runProgn(); break;
  case CREATE: runCreateFile(); break;
  case OPEN: runOpenFile(); break;
  case SEND: runSend(); break;
  case READ: runRead(); break;
  case WRITE: runWrite(); break;
  case PUTCH: runPutch(); break;
  case CLOSE: runClose(); break;
  case DIRECTION: runDigDirection(); break;
  case PAUSE: runPause(); break;
  case SLEEP: runSleep(); break;
  case BYTEVECTOR: runByteVector(); break;
  case FLOATVECTOR: runFloatVector(); break;
  case CHAIN: runChain(); break;
  case WHILE: runWhile(); break;

  default: runLet1();
  }

  if (errorThrown)
  {
    putstring("At ");
    puthex16(PC);
    terpri();
  }
}



void runAfterLoad(void)
//     We've loaded a file. Reset the PC, indicate we're actually running and we're not
// stopped. Returns on an error or a STOP.
{
  PC = 0;                     // Programs start at 0.
  doit = 1;                   // They're executed (not skipped until later).
  stopBit = 0;                // Set when its time to stop.
  
 loop:
  runStatement();
  if (errorThrown || stopBit) return;
  goto loop;
}



void runProgram(void)
//     Top level run command:
//           run                  Must have loaded a program.
//           run file             Run a file.
// Load the file if necessary and it works. Punt if no such program. Run the statements
// using the code above.
{
  uint8_t fh;
  ntok();

  // Either a string or an identifier works.
  if (ttype == LEXID || ttype == LEXSTRING)
  {
    if ((fh = openFile(STR)) == 255)
    {
      putstring("Can't load ");
      putstring(STR);
      terpri();
      return;
    }
    infh = fh;
    loadProgramFile();
    closeFile(fh);
    infh = serialIn;
    if (errorThrown)
    {
      putstring("Load fails\n");
      return;
    }
  }

  // Error if no program.
  if (PMAX == 0)
  {
    putstring("No program\n");
    return;
  }

  // Make it so.
  runAfterLoad();
  
}



// EOF
