/* relations.c - execution of relations.
 *  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 "lfxmit.h"
#include "serial.h"
#include "storage.h"

extern __xdata uint16_t   ST[];
extern uint16_t           DREG;
extern __xdata uint8_t    D[];
extern __xdata floatOrInt IACC;
extern __xdata floatOrInt IACCB;
extern __xdata floatOrInt FACC;
extern __xdata floatOrInt FACCB;

// All relations return 1 or 0. We create both of these at startup so they aren't continually
// filling up dynamic space.
extern __xdata uint16_t   iONE;
extern __xdata uint16_t   iZERO;

extern __bit            errorThrown;
extern __bit            doit;


void lessp(void)
// Returns 1 on stack if LHS < RHS for numeric types. If not executing, just punt.
{
  // If not executing, just punt.
  if (!doit) return;
  
  // RHS value.
  DREG = popST();
  switch (tag()) {

  // ??? < Integer.
  case DESC_INT:
    getIACC();

    DREG = popST();
    switch (tag()) {
    // Integer < Integer
    case DESC_INT:
      getIACCB();
      if (IACCB.i < IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;

    // Float < Integer
    case DESC_FLOAT:
      getFACC();
      if (FACC.f <  (float) IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;

    // Others illegal.
    default:
      badType("??? < Int ");
    }
    break;

  // ??? < Float
  case DESC_FLOAT:
    getFACC();
    DREG = popST();
    switch (tag()) {

    // Integer < Float
    case DESC_INT:
      getIACC();
      if (((float) IACC.i) < FACC.f) pushST(iONE);
      //      if (FACC.f < (float) IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;

    // Float < Float
    case DESC_FLOAT:
      getFACCB();
      if (FACCB.f < FACC.f) pushST(iONE);
      else pushST(iZERO);
      break;

      // >      // ??? > Float is an error.
    default:
      badType("??? < float");
    }
    break;

  // Bad all around.
  default:
    badType("??? < ???");
  }
}



void leq(void)
// Returns 1 on stack if LHS <= RHS for numeric types. If not executing, just punt.
{
  // If not executing, just punt.
  if (!doit) return;

  // RHS value.
  DREG = popST();
  switch (tag()) {

  // ??? <= Integer.
  case DESC_INT:
    getIACC();

    DREG = popST();
    switch (tag()) {
    case DESC_INT:
      getIACCB();
      if (IACCB.i <= IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;

    case DESC_FLOAT:
      getFACC();
      if (FACC.f <=  (float) IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;
      
    default:
      badType("??? <= Int ");
    }
    break;

  case DESC_FLOAT:
    getFACC();
    DREG = popST();
    switch (tag()) {
    case DESC_INT:
      getIACC();
      if (((float) IACC.i) <= FACC.f) pushST(iONE);
      else pushST(iZERO);
      break;
      
    case DESC_FLOAT:
      getFACCB();
      if (FACCB.f <= FACC.f) pushST(iONE);
      else pushST(iZERO);
      break;
    default:
      badType("??? <= float");
    }
    break;

  default:
    badType("??? <= ???");
  }
}



void greaterp(void)
// Returns 1 on stack if LHS > RHS for numeric types. If not executing, just punt.
{
  // If not executing, just punt.
  if (!doit) return;

  // RHS value.
  DREG = popST();
  switch (tag()) {

  // ??? > Integer.
  case DESC_INT:
    getIACC();

    DREG = popST();
    switch (tag()) {
    case DESC_INT:
      getIACCB();
      if (IACCB.i > IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;

    case DESC_FLOAT:
      getFACC();
      if (FACC.f >  (float) IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;
      
    default:
      badType("??? > Int ");
    }
    break;

  case DESC_FLOAT:
    getFACC();
    DREG = popST();
    switch (tag()) {
    case DESC_INT:
      getIACC();
      if (((float) IACC.i) > FACC.f) pushST(iONE);
      else pushST(iZERO);
      break;
      
    case DESC_FLOAT:
      getFACCB();
      if (FACCB.f > FACC.f) pushST(iONE);
      else pushST(iZERO);
      break;
    default:
      badType("??? > float");
    }
    break;

  default:
    badType("??? > ???");
  }
}




void geq(void)
// Returns 1 on stack if LHS >= RHS for numeric types. If not executing, just punt.
{
  // If not executing, just punt.
  if (!doit) return;

  // RHS value.
  DREG = popST();
  switch (tag()) {

  // ??? >= Integer.
  case DESC_INT:
    getIACC();

    DREG = popST();
    switch (tag()) {
    case DESC_INT:
      getIACCB();
      if (IACCB.i >= IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;

    case DESC_FLOAT:
      getFACC();
      if (FACC.f >=  (float) IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;
      
    default:
      badType("??? >= Int ");
    }
    break;

  case DESC_FLOAT:
    getFACC();
    DREG = popST();
    switch (tag()) {
    case DESC_INT:
      getIACC();
      if (((float) IACC.i) >= FACC.f) pushST(iONE);
      else pushST(iZERO);
      break;
      
    case DESC_FLOAT:
      getFACCB();
      if (FACCB.f >= FACC.f) pushST(iONE);
      else pushST(iZERO);
      break;
    default:
      badType("??? >= float");
    }
    break;

  default:
    badType("??? >= ???");
  }
}



void equal(void)
// Returns 1 on stack if LHS == RHS for numeric types. If not executing, just punt.
{
  uint8_t __xdata *rhs;
  uint8_t __xdata *lhs;
  
  // If not executing, just punt.
  if (!doit) return;

  // RHS value.
  DREG = popST();
  switch (tag()) {

  // ??? >= Integer.
  case DESC_INT:
    getIACC();

    DREG = popST();
    switch (tag()) {
    case DESC_INT:
      getIACCB();
      if (IACCB.i == IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;

    case DESC_FLOAT:
      getFACC();
      if (FACC.f ==  (float) IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;

    case DESC_STRING:
      pushST(iZERO);
      return;
      
    default:
      badType("??? == Int ");
    }
    break;

  case DESC_FLOAT:
    getFACC();
    DREG = popST();
    switch (tag()) {
    case DESC_INT:
      getIACC();
      if (FACC.f == (float) IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;
      
    case DESC_FLOAT:
      getFACCB();
      if (FACCB.f == FACC.f) pushST(iONE);
      else pushST(iZERO);
      break;

    case DESC_STRING:
      pushST(iZERO);
      return;
      
    default:
      badType("??? == float");
    }
    break;

  // Strings are simple character by character. DREG points to the rhs string. String compare to
  // anything else is always false.
  case DESC_STRING:
    rhs = (uint8_t __xdata *) &D[DREG + 3];
    DREG = popST();
    switch (tag()) {
    case DESC_STRING:
      lhs = (uint8_t __xdata *) &D[DREG + 3];
      for (;;)
      {
        if (*lhs == *rhs)
        {
          if (*lhs == 0)
          {
            pushST(iONE);
            return;
          }
          ++lhs;
          ++rhs;
        }
        else
        {
          pushST(iZERO);
          return;
        }
      }
      break;

    default:
      pushST(iZERO);
      return;
    }
    break;
  default:
    badType("??? == ???");
  }
}




void neq(void)
// Returns 1 on stack if LHS != RHS for numeric types and strings. If not executing, just punt.
{
  uint8_t __xdata *rhs;
  uint8_t __xdata *lhs;
  
  // If not executing, just punt.
  if (!doit) return;

  // RHS value.
  DREG = popST();
  switch (tag()) {

  // ??? >= Integer.
  case DESC_INT:
    getIACC();

    DREG = popST();
    switch (tag()) {
    case DESC_INT:
      getIACCB();
      if (IACCB.i != IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;

    case DESC_FLOAT:
      getFACC();
      if (FACC.f !=  (float) IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;

    case DESC_STRING:
      pushST(iONE);
      return;
      
    default:
      badType("??? != Int ");
    }
    break;

  case DESC_FLOAT:
    getFACC();
    DREG = popST();
    switch (tag()) {
    case DESC_INT:
      getIACC();
      if (FACC.f != (float) IACC.i) pushST(iONE);
      else pushST(iZERO);
      break;
      
    case DESC_FLOAT:
      getFACCB();
      if (FACCB.f != FACC.f) pushST(iONE);
      else pushST(iZERO);
      break;

    case DESC_STRING:
      pushST(iONE);
      return;
      
    default:
      badType("??? != float");
    }
    break;

  // Strings are simple character by character. DREG points to the rhs string. String compare to
  // anything else is always true.
  case DESC_STRING:
    rhs = (uint8_t __xdata *) &D[DREG + 3];
    DREG = popST();
    switch (tag()) {
    case DESC_STRING:
      lhs = (uint8_t __xdata *) &D[DREG + 3];
      for (;;)
      {
        if (*lhs == *rhs)
        {
          if (*lhs == 0)
          {
            pushST(iZERO);
            return;
          }
          ++lhs;
          ++rhs;
        }
        else
        {
          pushST(iONE);
          return;
        }
      }
      break;

    default:
      pushST(iONE);
      return;
    }
    break;
       
  default:
    badType("??? != ???");
  }
}




// EOF
