/* let.c - assignment statement.
 *  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 "storage.h"
#include "serial.h"


extern __bit              errorThrown;
extern uint16_t           DREG;
extern __xdata uint8_t    D[];

extern __xdata uint16_t   PC;
extern __bit doit;

extern __xdata SymtabEntry S[];
extern __xdata floatOrInt  IACC;
extern __xdata floatOrInt  IACCB;
extern __xdata floatOrInt  FACC;

// Morse code.
extern __xdata uint32_t    ditMilliseconds;
extern __xdata uint8_t     spaceDits;



static void parserhs(void)
//     The right hand side is common to more than one assignment. Parse it here, the caller
// takes care of the result.
{
  incPC();
  if (getPByte() != ASSIGN)
  {
    putstring("LET missing =\n");
    puthex16(PC); terpri();
    errorThrown = 1;
    return;
  }

  incPC();
  parseExpression();
}



static __bit boundsCheck(void)
// Bounds check. Returns 0 if in bounds, 1 if not so you can do an error message. Only used here.
// IACC is the index, IACCB the limit. 
{
  return (IACC.i < 0 || IACC.i > IACCB.i)?1:0;
}


void dumpSymtab(void);

void runLet1(void)
//     Lots of different syntaxes but:
//          LET <var> = <expression>;
//          LET <var>[<subscript>] = <expression>
// Caller has skipped LET or it wasn't there - the default from run.
{
  uint16_t var;

  var = getPByte();

  // Digital I/O
  if (var == DIG0 || var == DIG1 || var == RED || var == YELLOW || var == GREEN || var == BLUE)
  {
    parserhs();
    if (doit)
      if (var == DIG0) setDIG0();
      else if (var == DIG1) setDIG1();
      else if (var == RED) setRed();
      else if (var == YELLOW) setYellow();
      else if (var == GREEN) setGreen();
      else if (var == BLUE) setBlue();
    return;
  }

  // Morse code values.
  else if (var == DITMS || var == SPACEDITS)
  {
    parserhs();
    if (doit)
    {
      DREG = popST();
      if (tag() == DESC_INT)
      {
        getIACC();
        if (var == DITMS) ditMilliseconds = IACC.i;
        else if (var == SPACEDITS) spaceDits = (uint8_t) IACC.i;
      }
      else
      {
        putstring("LET morse-times not Int\n");
        errorThrown = 1;
      }
    }
    return;
  }

  // Generic variable.
  else if (var != VARIABLE)
  {
    putstring("LET missing variable\n");
    errorThrown = 1;
    return;
  }
  incPC();

  // If the variable is a vector, then look for the subscript.
  var = getPByte();
  DREG = S[var].addr;
  // If undefined, then DREG = 0xFFFF.
  
  if (tag() == DESC_BYTEVECTOR || tag() == DESC_FLOATVECTOR)
  {
    incPC();
    if (getPByte() != LSQB)
    {
      putstring("LET vector missing [\n");
      errorThrown = 1;
      return;
    }
    incPC();
    parseExpression();
    if (errorThrown) return;
    if (getPByte() != RSQB)
    {
      putstring("LET vector missing ]\n");
      errorThrown = 1;
      return;
    }
    
    if (doit)
    {
      // Get subscript and verify limit. IACCB has index.
      DREG = S[var].addr;      
      subscriptCheck(var);
      if (errorThrown) return;

      // Get base address of vector.
      DREG = S[var].addr;
      if (tag() == DESC_BYTEVECTOR)
      {
        var = IACCB.i + DREG + 4;
        parserhs();
        if (errorThrown || !doit) return;

        // Get the value to store.
        DREG = popST();
        switch (tag()) {
        case DESC_INT:
          getIACC();
          D[var] = IACC.i8.b8a;    // (uint8_t) IACC.i generates extra stuff.
          return;
        case DESC_FLOAT:
          getFACC();
          D[var] = (uint8_t) FACC.f;
          break;
        default:
          putstring("LET ByteVector not number\n");
          errorThrown = 1;
        }
        return;
      }

      else
      {
        var = (IACCB.i * 4) + DREG + 4;
        
        parserhs();
        if (errorThrown || !doit) return;

        // Get the value to store.
        DREG = popST();
        switch (tag()) {
        case DESC_INT:
          getIACC();
          *((float*) &D[var]) = (float) IACC.i;
          return;
        case DESC_FLOAT:
          getFACC();
          *((float*) &D[var]) = FACC.f;
          break;
        default:
          putstring("LET FloatVector not number\n");
          errorThrown = 1;
        }
        return;
      }
    }

    // Get rhs - we're skipping the right hand side for whatever reason.
    parserhs();
    return;
  }

  // Normal assignment.
  parserhs();
  if (errorThrown) return;
  if (!doit) return;
  S[var].addr = popST();
}



void runLet(void)
// This gets called when LET appears.
{
  incPC();
  runLet1();
}
  


// EOF
