Logo Search packages:      
Sourcecode: fceux version File versions  Download package

conddebug.cpp

/* FCEUXD SP - NES/Famicom Emulator
 *
 * Copyright notice for this file:
 *  Copyright (C) 2005 Sebastian Porst
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
* The parser was heavily inspired by the following two websites:

* http://www.cs.utsa.edu/~wagner/CS5363/rdparse.html
* http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm
*
* Grammar of the parser:
* 
* P         -> Connect
* Connect   -> Compare {('||' | '&&') Compare}
* Compare   -> Sum {('==' | '!=' | '<=' | '>=' | '<' | '>') Sum}
* Sum       -> Product {('+' | '-') Product}
* Product   -> Primitive {('*' | '/') Primitive}
* Primitive -> Number | Address | Register | Flag | '(' Connect ')'
* Number    -> '#' [1-9A-F]*
* Address   -> '$' [1-9A-F]* | '$' '[' Connect ']'
* Register  -> 'A' | 'X' | 'Y' | 'R'
* Flag      -> 'N' | 'C' | 'Z' | 'I' | 'B' | 'V'
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>

#include "conddebug.h"

// Next non-whitespace character in string
char next;

int ishex(char c)
{
      return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}

void scan(const char** str)
{
      do
      {
            next = **str;
            (*str)++;
      } while (isspace(next));
}

// Frees a condition and all of it's sub conditions
void freeTree(Condition* c)
{
      if (c->lhs) freeTree(c->lhs);
      if (c->rhs) freeTree(c->rhs);

      free(c);
}

// Generic function to handle all infix operators but the last one in the precedence hierarchy. : '(' E ')'
Condition* InfixOperator(const char** str, Condition(*nextPart(const char**)), int(*operators)(const char**))
{
      Condition* t = nextPart(str);
      Condition* t1;
      Condition* mid;
      int op;

      while ((op = operators(str)))
      {
            scan(str);

            t1 = nextPart(str);

            if (t1 == 0)
            {
                  freeTree(t);
                  return 0;
            }

            mid = (Condition*)malloc(sizeof(Condition));
            memset(mid, 0, sizeof(Condition));

            mid->lhs = t;
            mid->rhs = t1;
            mid->op = op;

            t = mid;
      }

      return t;
}

// Generic handler for two-character operators
int TwoCharOperator(const char** str, char c1, char c2, int op)
{
      if (next == c1 && **str == c2)
      {
            scan(str);
            return op;
      }
      else
      {
            return 0;
      }
}

// Determines if a character is a flag
int isFlag(char c)
{
      return c == 'N' || c == 'I' || c == 'C' || c == 'V' || c == 'Z' || c == 'B' || c == 'U' || c == 'D';
}

// Determines if a character is a register
int isRegister(char c)
{
      return c == 'A' || c == 'X' || c == 'Y' || c == 'P';
}

// Reads a hexadecimal number from str
int getNumber(unsigned int* number, const char** str)
{
//    char buffer[5];

      if (sscanf(*str, "%X", number) == EOF || *number > 0xFFFF)
      {
            return 0;
      }
      
// Older, inferior version which doesn't work with leading zeros
//    sprintf(buffer, "%X", *number);
//    *str += strlen(buffer);
      while (ishex(**str)) (*str)++;
      scan(str);

      return 1;
}

Condition* Connect(const char** str);

// Handles the following part of the grammar: '(' E ')'
Condition* Parentheses(const char** str, Condition* c, char openPar, char closePar)
{
      if (next == openPar)
      {
            scan(str);

            c->lhs = Connect(str);

            if (!c) return 0;

            if (next == closePar)
            {
                  scan(str);
                  return c;
            }
            else
            {
                  return 0;
            }
      }

      return 0;
}

/*
* Check for primitives
* Flags, Registers, Numbers, Addresses and parentheses
*/
Condition* Primitive(const char** str, Condition* c)
{
      if (isFlag(next)) /* Flags */
      {
            if (c->type1 == TYPE_NO)
            {
                  c->type1 = TYPE_FLAG;
                  c->value1 = next;
            }
            else
            {
                  c->type2 = TYPE_FLAG;
                  c->value2 = next;
            }

            scan(str);

            return c;
      }
      else if (isRegister(next)) /* Registers */
      {
            if (c->type1 == TYPE_NO)
            {
                  c->type1 = TYPE_REG;
                  c->value1 = next;
            }
            else
            {
                  c->type2 = TYPE_REG;
                  c->value2 = next;
            }

            scan(str);

            return c;
      }
      else if (next == '#') /* Numbers */
      {
            unsigned int number = 0;
            if (!getNumber(&number, str))
            {
                  return 0;
            }

            if (c->type1 == TYPE_NO)
            {
                  c->type1 = TYPE_NUM;
                  c->value1 = number;
            }
            else
            {
                  c->type2 = TYPE_NUM;
                  c->value2 = number;
            }

            return c;
      }
      else if (next == '$') /* Addresses */
      {
            if ((**str >= '0' && **str <= '9') || (**str >= 'A' && **str <= 'F')) /* Constant addresses */
            {
                  unsigned int number = 0;
                  if (!getNumber(&number, str))
                  {
                        return 0;
                  }

                  if (c->type1 == TYPE_NO)
                  {
                        c->type1 = TYPE_ADDR;
                        c->value1 = number;
                  }
                  else
                  {
                        c->type2 = TYPE_ADDR;
                        c->value2 = number;
                  }

                  return c;
            }
            else if (**str == '[') /* Dynamic addresses */
            {
                  scan(str);
                  Parentheses(str, c, '[', ']');

                  if (c->type1 == TYPE_NO)
                  {
                        c->type1 = TYPE_ADDR;
                  }
                  else
                  {
                        c->type2 = TYPE_ADDR;
                  }

                  return c;
            }
            else
            {
                  return 0;
            }
      }
      else if (next == '(')
      {
            return Parentheses(str, c, '(', ')');
      }

      return 0;
}

/* Handle * and / operators */
Condition* Term(const char** str)
{
      Condition* t = (Condition*)malloc(sizeof(Condition));
      Condition* t1;
      Condition* mid;

      memset(t, 0, sizeof(Condition));

      if (!Primitive(str, t))
      {
            freeTree(t);
            return 0;
      }

      while (next == '*' || next == '/')
      {
            int op = next == '*' ? OP_MULT : OP_DIV;

            scan(str);

            t1 = (Condition*)malloc(sizeof(Condition));
            memset(t1, 0, sizeof(Condition));

            if (!Primitive(str, t1))
            {
                  freeTree(t);
                  freeTree(t1);
                  return 0;
            }

            mid = (Condition*)malloc(sizeof(Condition));
            memset(mid, 0, sizeof(Condition));

            mid->lhs = t;
            mid->rhs = t1;
            mid->op = op;

            t = mid;
      }

      return t;
}

/* Check for + and - operators */
int SumOperators(const char** str)
{
      switch (next)
      {
            case '+': return OP_PLUS;
            case '-': return OP_MINUS;
            default: return OP_NO;
      }
}

/* Handle + and - operators */
Condition* Sum(const char** str)
{
      return InfixOperator(str, Term, SumOperators);
}

/* Check for <=, =>, ==, !=, > and < operators */
int CompareOperators(const char** str)
{
      int val = TwoCharOperator(str, '=', '=', OP_EQ);
      if (val) return val;

      val = TwoCharOperator(str, '!', '=', OP_NE);
      if (val) return val;

      val = TwoCharOperator(str, '>', '=', OP_GE);
      if (val) return val;

      val = TwoCharOperator(str, '<', '=', OP_LE);
      if (val) return val;

      val = next == '>' ? OP_G : 0;
      if (val) return val;

      val = next == '<' ? OP_L : 0;
      if (val) return val;

      return OP_NO;
}

/* Handle <=, =>, ==, !=, > and < operators */
Condition* Compare(const char** str)
{
      return InfixOperator(str, Sum, CompareOperators);
}

/* Check for || or && operators */
int ConnectOperators(const char** str)
{
      int val = TwoCharOperator(str, '|', '|', OP_OR);
      if(val) return val;

      val = TwoCharOperator(str, '&', '&', OP_AND);
      if(val) return val;

      return OP_NO;
}

/* Handle || and && operators */
Condition* Connect(const char** str)
{
      return InfixOperator(str, Compare, ConnectOperators);
}

/* Root of the parser generator */
Condition* generateCondition(const char* str)
{
      Condition* c;

      scan(&str);
      c = Connect(&str);

      if (!c || next != 0) return 0;
      else return c;
}

Generated by  Doxygen 1.6.0   Back to index