/******************************************************************** * * * HCS_C XPRESS Compiler * * Version 1.06 Added video support * Version 1.05 Added bit set command, made bit command consistent * Version 1.00 X10 support improved, old commands turned off * Version 0.04a HCSNET device command * Version 0.04 HCSNET support, rabbit ethernet support * Version 0.03a Bug fix * Version 0.03 Added HCSNET operands and VOICE, VOICE_RECORD * Version 0.02 New versioning scheme to match the firmware * Version 5.10 * * * June 15, 2000 * * Copyright (C) 2001 Circuit Cellar Incorporated * 4 Park St. * Vernon, CT 06066 * * steve@circuitcellar.com; * * 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. * * * Copyright (c) 2005, ZetaEngineering * Copyright (c) 1999-2000, Creative Control Concepts * Copyright (c) 1992-1998, Circuit Cellar Inc. * * Version 0.02 R -- May 16,2005 -- Added case, subroutine call * -- direct addressing * -- changing command names for consistency * Version 5.10 R -- Mar 18,2005 -- Making changes for HCS_C * Version 5.03 R -- Aug 27,2002 -- added phone number command * Version 5.02 R -- July 27,2002 -- added logical operators, hex display * Version 5.01 R -- June 24,2002 -- added new operators, inline FPGA regs * Version 1.04 R -- May 24,2002 -- added OUTSTR support for display * Version 1.01 R -- May 24,2002 -- removed HVAC support, added FPGA support rdm * * Version 5.01 -- July 21, 2002 -- Add support for Stat-Link HVAC Interface * Version 4.10 -- June 15, 2000 -- Add support for Stat-Link HVAC Interface * Version 4.01 -- May 1, 2000 -- Add support for CID Name via %N string flag * -- Add support for CID Num via %M string flag * -- Add support for %S fixed size value flag * Version 4.00 -- January 5, 2000 -- Version Increment * Version 3.63 -- December 25, 1999 -- Version Increment * Version 3.62 -- August 25, 1999 * -- Fix 8th Module bug so 8 of each can be used * -- Added more detailed error messages * Version 3.60 -- March 10, 1998 * -- Version change to match update * * Version 3.50 -- April 7, 1997 * -- Final Answer MAN support * * Version 3.05 -- November 3, 1996 * -- Answer MAN support * * Version 3.01 -- May 8, 1995 * -- Version change to match update * * Version 3.00 -- March 16, 1995 * -- Release version * * Version 2.70 -- Version change to match rest of release * Version 2.69 -- Added power fail stuff * Version 2.68 -- Added network module display bitmaps, network string out * Version 2.67 -- Added embedded version, LogSize, and NetByte * Version 2.66 -- Added analog I/O and netbit display bitmaps * Version 2.65 -- Version number change to match rest of beta release * Version 2.64 -- Added more modem and Caller ID stuff * Version 2.63 -- Added Caller ID stuff * Version 2.62 -- Added CONSOLE stuff * Version 2.61 -- Beta release to match rest of suite * -- Upped IF/END array size * * Version 2.55 -- May 18, 1994 * -- Cleaned up CS support for initial beta * Version 2.50 -- First pass at CS support * * Version 2.10 -- December 20, 1993 * -- Release version * * Version 2.00 -- March 3, 1993 * -- Release version * * Version 1.20 -- November 12, 1992 * -- Added MCIR support * -- Added TV-Link support * -- Fix GIF bug * -- Fix tab bug * -- Increase labels to 512 * * Version 1.00 -- March 1992 * -- Release version * * *********************************************************************/ #include #include #include #include #include #include #include #include #define true 1 #define false 0 int verbose_debug = 0; // set to 1 to see debugging output int eqn_debug = 0; // set to 1 to see debugging of equation syntax #define MAX_CHANGEVARS 256 #define NUM_LOC_VARS 256 #define MAX_LINK_LIMIT 100 #define MAX_CHAINLINK_LIMIT 256 #define NUM_HCSNET_DEVICES 32 #define MAX_HCSNET_ADDR 32 // HCSNET IDs #define ID_HCSNET_CMB 1 #define ID_HCSNET_TERM 2 #define ID_HCSNET_IMG 3 #define OPERATOR_TYPE_NORMAL 0 #define OPERATOR_TYPE_NONE 1 #define DONE_TYPE_NORMAL 0 #define DONE_TYPE_THEN 1 #define DONE_TYPE_END 2 #define DONE_TYPE_ELSE 3 #define DONE_TYPE_IS 4 #define DONE_TYPE_EOF 5 #define TOKEN_CFG_OFFSET 0x2f //start of actual token area after cfg stuff #define TOKEN_CFG_TEST 0 //start of actual token area after cfg stuff #define maxtmr 256 #define maxvar 256 #define maxinp 207 #define maxout 207 #define maxadc 135 #define maxdac 31 #define MAX_IFS 1024 #define MAX_IFSTACK 32 #define bell 0x07 #define CHAR_CR 0x0d #define esc 0x1b FILE * fp, *outfile; char *keyp, keyw[80]; /* global token holder */ int version = 106; /* Low two digits must be minor version number */ int linenum = 0; int errors = 0; int numpl = 0; int numir = 0; int numlcd = 0 ; int numdio = 0; int numaman = 0; int numdiop = 0; int sc = 0; int adcres = 8; int adcchan = 8; int adcgain[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int digin[4] = { 0, 0, 0, 0 }; int digout[4] = { 0, 0, 0, 0}; int anain[3] = { 0, 0, 0 }; int anaout[1] = { 0}; int netbits[5] = { 0, 0, 0, 0, 0 }; int netmod[1] = { 0}; int proc_eqn = false; int eqn_end = false; int lparens = 0; int count_parens = 0; int sequence = false; int changevar_count = 0; unsigned int iftable[MAX_IFS][2]; unsigned int curr_backfill; unsigned int ifctr = 0; unsigned int bytectr = 0; unsigned int ifcount = 0; unsigned int endcount = 0; unsigned int house_used = 0, checksum = 0; char label[512][32], def[512][32]; short hcsnet_device_array[NUM_HCSNET_DEVICES]; int hcsnet_device_ptr; char *sptr, substring[128]; int link_count; // FUNCTION PROTOTYPING extern int read_word(); extern int read_prefix(); int atoh(); void if_error(); void process_then(); void process_equation(); void get_non_comment_keyword(); // functions int open_files (char *input, char *output) { if ((fp = fopen (input, "r")) == NULL) { printf ("\nInput file %s not found", input); errors++; return 1; } if ((outfile = fopen (output, "wb+")) == NULL) { printf ("\nOutput file %s could not be created", output); errors++; return 1; } return 0; } void close_files (void) { fclose (outfile); fclose (fp); } int new_ifctr() { // First, create a stack entry for this IF. This permits nested if // statements. The stack holds the iftable pointer of the current // holding address. When restored, the iftable entry will be // filled with the destination to be backannotated. // The first ifctr location at zero is thrown away (stack push) ifctr++; if (ifctr >= MAX_IFS) { if_error (); ifctr--; } return(ifctr); } //out_file (char ch) // @CHANGE void out_file (int ch) { fputc ((char)ch, outfile); // @CHANGE checksum += (int) ch; bytectr++; } void out_file_no_check (int ch) // @CHANGE { fputc ((char)ch, outfile); //@CHANGE } char get_ch (void) { if (*sptr) return *sptr++; else return fgetc(fp); } void unget_ch (char ch) { if (*sptr) *sptr--; else ungetc (ch, fp); } // replace is a routine that does the DEFINE substitutions // before the read_word routine is called. void replace (char *st) { int i; for (i = 0; *def[i]; i++) { if (strcmp (st, label[i])) continue; strcpy (substring, def[i]); sptr = substring; read_word (st); break; } } //////////////////////////////////////////////////////////////////////// // Read word //////////////////////////////////////////////////////////////////////// // This gets the next word in the file and puts it in // the parameter string. HCS2 had some kind of sensitivity to , this // is removed in HCS_C Xpress. Unfortunately, this routine is rather // obtuse, so I put in some comments to try to make sense of it.. // int read_word (char *input_string) { char ch, *local; int done = false; int leading_whitespace = true; // keep a local copy of the string pointer. local = input_string; // while (!done && ((ch = toupper (get_ch ())) != EOF)) // Now scan the input stream for a word (until end of file) // and convert the word to upper case (so the case statements // dont have to try to check both lower case and upper case // words). leading_whitespace is indicator to skip front // end whitespace. while (!done && ((ch = get_ch ()) != EOF)) // BUG!! msvc toupper calls get_ch twice! { ch = toupper(ch); // library routine that converts to uppercase so that // events.bin can be case insensitive. switch (ch) { case '!': // comment, just ignore through rest of line, then try again while (get_ch () != '\n') // to get a real word. { } linenum++; if (verbose_debug == 1) printf("Line count inc to %d\n",linenum); break; case '\n': linenum++; if (verbose_debug == 1) printf("Line count inc to %d\n",linenum); case '\t': case ',': case ' ': if (leading_whitespace) break; else { done = true; break; } case 0: case ';': if (proc_eqn) { if (leading_whitespace) { *input_string++ = ch; if (ch == '\n') { unget_ch (ch); linenum--; } eqn_end = true; done = true; break; } else { unget_ch (ch); if (ch == '\n') linenum--; eqn_end = true; done = true; break; } } else { if (leading_whitespace) break; else { unget_ch (ch); if (ch == '\n') linenum--; done = true; break; } } case '+': case '~': case '-': case '*': case '/': if (proc_eqn) { if (leading_whitespace) { *input_string++ = ch; done = true; break; } else { unget_ch (ch); done = true; break; } } else { *input_string++ = ch; leading_whitespace = false; // turn off leading_whitespace if not process eqn and get operator break; } case ':': case '"': case '(': case ')': if (leading_whitespace) { *input_string++ = ch; done = true; break; } else { unget_ch (ch); done = true; break; } case '>': case '<': case '=': if (leading_whitespace) { *input_string++ = ch; // ch = toupper (get_ch ()); //@msvc toupper calls get_ch twice! ch = get_ch(); ch = toupper(ch); if ((ch == '>') || (ch == '<') || (ch == '=')) { *input_string++ = ch; done = true; break; } else { unget_ch (ch); done = true; break; } } else { unget_ch (ch); done = true; break; } default: if (iscntrl (ch)) break; *input_string++ = ch; leading_whitespace = false; } } *input_string = '\000'; replace (local); // WARNING THIS WILL SKIP AN APPENDED WORD--must have space return !feof (fp); } //////////////////////////////////////////////////////////////////////// // Read prefix //////////////////////////////////////////////////////////////////////// // This gets the next word in the file and puts it in keyp. It assumes // there may (or may not) be a suffix number portion, so it only reads // up to when a number or space is encountered. It then returns the // value of the number (generally an ID address). // int read_prefix (char *input_string) { char ch, *local; char done; int leading_whitespace; int retval; // keep a local copy of the string pointer. local = input_string; retval = 0; done = false; leading_whitespace = 0; while (!done && ((ch = get_ch ()) != EOF)) // BUG!! msvc toupper calls get_ch twice! { ch = toupper(ch); // library routine that converts to uppercase so that // events.bin can be case insensitive. switch (ch) { case '!': // comment, just ignore through rest of line, then try again while (get_ch () != '\n') // to get a real word. { } linenum++; if (verbose_debug == 1) printf("Line count inc to %d\n",linenum); break; case '\n': linenum++; if (verbose_debug == 1) printf("Line count inc to %d\n",linenum); case '\t': case ',': case ' ': if (leading_whitespace) break; else { done = true; break; } case ')': unget_ch(ch); done = true; break; default: if ((ch < '0') || (ch > '9')) *input_string++ = ch; else { retval = (retval * 10) + (ch & 0xf); break; } leading_whitespace = false; } } *input_string = '\000'; //replace (local); // WARNING THIS WILL SKIP AN APPENDED WORD--must have space return (retval); } int read_line (char *input_string) { char ch; int done = false; // while (!done && ((ch = toupper (fgetc (fp))) != EOF)) //@CHANGE toupper calls get_ch twice! while (!done && ((ch = fgetc (fp)) != EOF)) { ch = toupper(ch); switch (ch) { case '!': unget_ch (ch); done = true; break; case '\n': linenum++; done = true; break; default: if (iscntrl (ch)) break; *input_string++ = ch; } } *input_string = '\000'; return !feof (fp); } int read_string (char *input_string) { char ch; int done = false; while (!done && ((ch = fgetc (fp)) != EOF)) { switch (ch) { case '"': done = true; break; case '\n': linenum++; done = true; break; default: if (iscntrl (ch)) break; *input_string++ = ch; } } *input_string = '\000'; return !feof (fp); } /* 990825 MB - New Error Messages with more detailed info */ void nsyn_error (char *err, char *msg) { printf ("\n* Syntax error in line %i: \"%s\" ", linenum, err); printf ("\n - %s ", msg); errors++; } void nval_error (char *err, char *msg) { printf ("\n* Illegal value in line %i: \"%s\" (%x,%x,%x,%x)", linenum, err, err[0],err[1],err[2],err[3]); printf ("\n - %s \n",msg); errors++; } void syn_error (char *err) { printf ("\n* Syntax error in line %i: \"%s\" ", linenum, err); errors++; } void val_error (char *err) { printf ("\n* Illegal value in line %i: \"%s\" ", linenum, err); errors++; } void if_error (void) { printf ("\n* Too many IFs at line %i", linenum); errors++; } void paren_error (void) { printf ("\n* Parentheses mismatch in line %i.", linenum); errors++; } int bcd (int num) { return (((num / 10) * 16) + (num % 10)); } int atoh(char *ascii_num) { int sum; sum = 0; while (((*ascii_num >= '0') && (*ascii_num <= '9')) || ((*ascii_num >= 'A') && (*ascii_num <= 'F'))) { // If the digit is an ascii letter, treat as hex digit A-F if (*ascii_num > '9') sum = (16 * sum) + ((*ascii_num & 0xf) + 9); else sum = (16 * sum) + (*ascii_num - '0'); ascii_num++; } return(sum); } int getnumber(char *ascii_num) { if ((*ascii_num == '0') && (*(ascii_num + 1) == 'X')) return(atoh(ascii_num + 2)); else return(atoi(ascii_num)); } ////////////////////////////////////////////////////////////////// // Retrieve operators. This assumes that keyp has a valid word read into // it. The res[] array holds the count of tokens to store and the actual // token data. Do not put unary operators here since they are treated // like operands. If a symbol is both unary and binary, put the binary // handling here, and the unary handling in the operand section. ////////////////////////////////////////////////////////////////// int process_operator (int *res) { get_non_comment_keyword(); // get next valid keyword if (eqn_debug == 1) printf("begun process_operator %s\n",keyp); // These are operators, not operands. OK, whatever. Looks like // just a generic token generation is done without caring whether operands // or operators. Add in the compare tokens so function comparison can be // done in math expressions. if (!strcmp (keyp, "+")) { res[0] = 1; res[1] = 0x75; } else if (!strcmp (keyp, "-")) { res[0] = 1; res[1] = 0x76; } else if (!strcmp (keyp, "*")) { res[0] = 1; res[1] = 0x77; } else if (!strcmp (keyp, "/")) { res[0] = 1; res[1] = 0x78; } // @FPGA added logic to support byte operations // Like C, the &, |, and ~ functions are bit-wise // operations, while the keywords AND, OR,NOT are // logical condition operators. They return 0 // if false and 1 if true. else if (!strcmp (keyp, "&")) { res[0] = 1; res[1] = 0x79; } else if (!strcmp (keyp, "|")) { res[0] = 1; res[1] = 0x7a; } else if (!strcmp (keyp, "BIT")) { res[0] = 1; res[1] = 0x7c; } else if (!strcmp (keyp, "AND")) { res[0] = 1; res[1] = 0x7d; } else if (!strcmp (keyp, "OR")) { res[0] = 1; res[1] = 0x7e; } else if (!strcmp (keyp, "NOT")) { res[0] = 1; res[1] = 0x7f; } // functional compare operators. else if (!strcmp (keyp, "=")) { res[0] = 1; res[1] = 0x01; } else if (!strcmp (keyp, "<>")) { res[0] = 1; res[1] = 0x02; } else if (!strcmp (keyp, "><")) { res[0] = 1; res[1] = 0x02; } else if (!strcmp (keyp, ">")) { res[0] = 1; res[1] = 0x03; } else if (!strcmp (keyp, "<")) { res[0] = 1; res[1] = 0x04; } else if (!strcmp (keyp, ">=")) { res[0] = 1; res[1] = 0x05; } else if (!strcmp (keyp, "=>")) { res[0] = 1; res[1] = 0x05; } else if (!strcmp (keyp, "<=")) { res[0] = 1; res[1] = 0x06; } else if (!strcmp (keyp, "=<")) { res[0] = 1; res[1] = 0x06; } else if (*keyp == 0) { if (verbose_debug == 1) printf("At end of file\n"); return(0); // nothing more in file, so return 0 } else // no operator found { // if (verbose_debug == 1) // printf("ending process_operator type 0, new keyword is %s\n",keyp); return(0); // no operand found, so return 0 } // if (verbose_debug == 1) // printf("ending process_operator type 1, new keyword is %s \n",keyp); return(1); // gets here if found an operator, return 1 } // Skip comments and CRs, refetch the key word. CAUTION: It assumes the keyword // already in the keyp location IS TO BE THROWN AWAY. void get_non_comment_keyword() { keyp = keyw; read_word (keyp); while ((!strcmp (keyp, "!")) || (!strcmp (keyp, "\n"))) { // if (verbose_debug == 1) //printf("Found comment\n"); if (!strcmp (keyp, "!")) { // Since is comment, skip all to after CR while (strcmp (keyp, "\n")) //If not cr, just keep going { keyp = keyw; if (!read_word (keyp)) break; } linenum += 1; } else // Has to be a CR, skip it { keyp = keyw; if (!read_word (keyp)) break; } } // will leave at non comment keyword } ////////////////////////////////////////////////////////////////// // Expression operands // These are used in then (or else) clauses, and now in if expressions. // (this is different than HCS2 which had special IF compare evaluation) // This routine will stop as soon as an unrecognized keyword is read. // This routine assumes that the keyw is invalid and needs a new // value read into keyp. // It returns 0 if no operand/operator is found, 1 if is an operand, // and 2 if an operator. // NOTE!!!! Currently a string operand may set up to 4 tokens, increase // the local token array size if an operand requires more. ////////////////////////////////////////////////////////////////// int process_operand () { int num, bitnum, devnum; int local_tokens[5]; // will leave at non comment keyword get_non_comment_keyword(); if (eqn_debug == 1) printf("begun process_operand %s\n",keyp); // parens are a special case, treat left paren as an enclosed operand if (!strcmp (keyp, "(")) { count_parens++; out_file(TOKEN_LEFTPAREN); process_equation(); // Now must find the right paren if (!strcmp (keyp, ")")) { count_parens--; local_tokens[0] = 1; local_tokens[1] = TOKEN_RIGHTPAREN; } else nsyn_error (keyp, "Expecting \")\""); } else if (!strcmp (keyp, "CHANGED")) { read_word (keyp); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); // NOTE: this is a nested equation call. It expects to shut down when the // extra right hand paren (associated with this keyword) occurs. This is the // only way that the equation will stop at the right place if embedded within // another equation. process_equation(); num = changevar_count; changevar_count += 1; if (changevar_count >= MAX_CHANGEVARS) nsyn_error (keyp,"Cannot have more than 256 CHANGE checks in a program\n"); local_tokens[0] = 2; local_tokens[1] = TOKEN_OPRND_CHANGED; local_tokens[2] = num; } else if (!strcmp (keyp, "CHANGE_VALUE")) { read_word (keyp); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); // NOTE: this is a nested equation call. It expects to shut down when the // extra right hand paren (associated with this keyword) occurs. This is the // only way that the equation will stop at the right place if embedded within // another equation. process_equation(); num = changevar_count; changevar_count += 1; if (changevar_count >= MAX_CHANGEVARS) nsyn_error (keyp,"Cannot have more than 256 CHANGE checks in a program\n"); local_tokens[0] = 2; local_tokens[1] = TOKEN_OPRND_CHANGE_VALUE; local_tokens[2] = num; } else if (!strcmp (keyp, "VARIABLE")) { get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Variable Operand Expecting \"(\""); read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > maxvar)) nval_error (keyp, "Invliad Variable Operand Reference/ID"); read_word (keyp); if (!strcmp (keyp, ")")) { // If done, not a bit var local_tokens[0] = 2; local_tokens[1] = 0x30; local_tokens[2] = num; } else { // If comma, then set the bit in the var bitnum = getnumber (keyp); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Variable Bit Operand Expecting \")\""); local_tokens[0] = 3; local_tokens[1] = 0x31; local_tokens[2] = num; local_tokens[3] = bitnum; } if (verbose_debug == 1) printf("Done with variable set, keyword %s (%x)\n",keyp,keyp[0]); // read_word (keyp); // if (strcmp (keyp, "(")) // nsyn_error (keyp, "Expecting \"(\""); // read_word (keyp); // num = getnumber (keyp); // if ((num < 0) || (num > maxvar)) // val_error (keyp); // read_word (keyp); // if (strcmp (keyp, ")")) // nsyn_error (keyp, "Expecting \")\""); // local_tokens[0] = 2; // local_tokens[1] = 0x30; // local_tokens[2] = num; } else if (!strcmp (keyp, "ADC")) { read_word (keyp); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > maxadc)) nval_error (keyp, "Invalid ADC ID"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); local_tokens[0] = 2; local_tokens[1] = 0x31; local_tokens[2] = num; } else if (!strcmp (keyp, "TIMER")) { read_word (keyp); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > maxtmr)) nval_error (keyp, "Invalid Timer ID"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); local_tokens[0] = 2; local_tokens[1] = 0x32; local_tokens[2] = num; } // 0.04 modification: input mode will require a string to specify device. // Right now, compiles just () else if (!strcmp (keyp, "HCSNET")) { num = 0; devnum = 0; read_word (keyp); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); devnum = read_prefix (keyp); // Check for CMB operands. Right now, only support one device if (!strcmp (keyp, "CMB")) { if ((devnum < 0) || (devnum > MAX_HCSNET_ADDR)) nval_error (keyp, "Invalid CMB Device number"); num = read_prefix (keyp); if (verbose_debug == 1) printf("Processing CMB operand %c, num %d\n",*keyp,num); if (!strcmp (keyp, "?")) { printf("Got question\n"); num = 23; } else if (!strcmp (keyp, "A")) // Input values { if ((num > 0) && (num < 5)) num = 9 + num - 1; // use 2-5 (A1,A2,A3,A4) else nval_error (keyp, "Invalid CMB input register number"); } else if (!strcmp (keyp, "X")) // Input values { if ((num > 0) && (num < 5)) num = 17 + num - 1; // use 6-9 (A1,A2,A3,A4) else nval_error (keyp, "Invalid CMB input register number"); } else if (!strcmp (keyp, "N")) // Input values { if ((num > 0) && (num < 5)) num = 13 + num - 1; // use 6-9 (A1,A2,A3,A4) else nval_error (keyp, "Invalid CMB input register number"); } else if (!strcmp (keyp, "O")) num = 0; else if (!strcmp (keyp, "P")) num = 1; else if (!strcmp (keyp, "R")) num = 2; else if (!strcmp (keyp, "V")) num = 3; else if (!strcmp (keyp, "K")) num = 4; else if (!strcmp (keyp, "T")) num = 5; else if (!strcmp (keyp, "D")) num = 6; else if (!strcmp (keyp, "I")) num = 7; else if (!strcmp (keyp, "J")) num = 8; else if (!strcmp (keyp, "S")) num = 21; else nsyn_error (keyp, "Invalid CMB Device command"); } read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); local_tokens[0] = 2; local_tokens[1] = TOKEN_OPRND_HCSNET; local_tokens[2] = num; } else if (!strcmp (keyp, "INPUT")) { read_word (keyp); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > maxtmr)) nval_error (keyp, "Invalid Timer ID"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); local_tokens[0] = 2; local_tokens[1] = 0x17; local_tokens[2] = num; } // NOT YET IMPLEMENTED //else if (!strcmp (keyp, "IRCODE")) // { // read_word (keyp); // if (strcmp (keyp, "(")) // nsyn_error (keyp, "Expecting \"(\""); // read_word (keyp); // num = getnumber (keyp); // if ((num < 0) || (num > 511)) // nval_error (keyp, "Valid IR Codes are 0-511"); // read_word (keyp); // if (strcmp (keyp, ")")) // nsyn_error (keyp, "Expecting \")\""); // local_tokens[0] = 3; // local_tokens[1] = 0x33; // local_tokens[2] = num % 256; // local_tokens[3] = num / 256; // } else if (!strcmp (keyp, "TRUE")) { local_tokens[0] = 1; local_tokens[1] = 0x36; } else if (!strcmp (keyp, "ON")) { local_tokens[0] = 1; local_tokens[1] = 0x36; } else if (!strcmp (keyp, "FALSE")) { local_tokens[0] = 1; local_tokens[1] = 0x37; } else if (!strcmp (keyp, "OFF")) { local_tokens[0] = 1; local_tokens[1] = 0x37; } else if (!strcmp (keyp, "RANDOM")) { read_word (keyp); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > 255)) nval_error (keyp, "RANDOM value should be 0-255"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); local_tokens[0] = 2; local_tokens[1] = 0x38; local_tokens[2] = num; } else if (!strcmp (keyp, "VOICE")) { local_tokens[0] = 2; local_tokens[1] = TOKEN_OPRND_FPGA_IN; local_tokens[0] = 1; } else if (!strcmp (keyp, "MONTH")) { local_tokens[0] = 1; local_tokens[1] = 0x39; } else if (!strcmp (keyp, "DAY")) { local_tokens[0] = 1; local_tokens[1] = 0x3A; } else if (!strcmp (keyp, "YEAR")) { local_tokens[0] = 1; local_tokens[1] = 0x3B; } else if (!strcmp (keyp, "DOW")) { local_tokens[0] = 1; local_tokens[1] = 0x3C; } else if (!strcmp (keyp, "HOUR")) { local_tokens[0] = 1; local_tokens[1] = 0x3D; } else if (!strcmp (keyp, "MINUTE")) { local_tokens[0] = 1; local_tokens[1] = 0x3E; } else if (!strcmp (keyp, "SECOND")) { local_tokens[0] = 1; local_tokens[1] = 0x3F; } // NOT SUPPORTED //else if (!strcmp (keyp, "DTMFDIGIT") & sequence) // { // read_word (keyp); // if (strcmp (keyp, "(")) // nsyn_error (keyp, "Expecting \"(\""); // read_word (keyp); // num = getnumber (keyp); // if ((num < 0) || (num > 255)) // nval_error (keyp, "Expecting value between 0 and 255"); // read_word (keyp); // if (strcmp (keyp, ")")) // nsyn_error (keyp, "Expecting \")\""); // local_tokens[0] = 2; // local_tokens[1] = 0x40; // local_tokens[2] = num; // } // NOT SUPPORTED // else if (!strcmp (keyp, "DTMFNUMBER") & sequence) // { // read_word (keyp); // if (strcmp (keyp, "(")) // nsyn_error (keyp, "Expecting \"(\""); // read_word (keyp); // num = getnumber (keyp); // if ((num < 0) || (num > 255)) // nval_error (keyp, "Expecting value between 0 and 255"); // read_word (keyp); // if (strcmp (keyp, ")")) // nsyn_error (keyp, "Expecting \")\""); // local_tokens[0] = 2; // local_tokens[1] = 0x41; // local_tokens[2] = num; // } // NOT SUPPORTED // else if (!strcmp (keyp, "RINGS")) // { // local_tokens[0] = 1; // local_tokens[1] = 0x42; // } // NOT SUPPORTED // else if (!strcmp (keyp, "DIALTONE") & sequence) // { // local_tokens[0] = 1; // local_tokens[1] = 0x43; // } // NOT SUPPORTED // else if (!strcmp (keyp, "CALLPROGRESS") & sequence) // { // read_word (keyp); // if (strcmp (keyp, "(")) // nsyn_error (keyp, "Expecting \"(\""); // read_word (keyp); // num = getnumber (keyp); // if ((num < 0) || (num > 255)) // nval_error (keyp, "Expecting CALLPROGRESS value between 0 and 255"); // read_word (keyp); // if (strcmp (keyp, ")")) // nsyn_error (keyp, "Expecting \")\""); // local_tokens[0] = 2; // local_tokens[1] = 0x44; // local_tokens[2] = num; // } // NOT YET IMPLEMENTED // else if (!strcmp (keyp, "CIDNEW")) // { // local_tokens[0] = 1; // local_tokens[1] = 0x45; // } // NOT YET IMPLEMENTED // else if (!strcmp (keyp, "CIDMONTH")) // { // local_tokens[0] = 1; // local_tokens[1] = 0x46; // } // NOT YET IMPLEMENTED // else if (!strcmp (keyp, "CIDDAY")) // { // local_tokens[0] = 1; // local_tokens[1] = 0x47; // } // NOT YET IMPLEMENTED // else if (!strcmp (keyp, "CIDHOUR")) // { // local_tokens[0] = 1; // local_tokens[1] = 0x48; // } // NOT YET IMPLEMENTED // else if (!strcmp (keyp, "CIDMINUTE")) // { // local_tokens[0] = 1; // local_tokens[1] = 0x49; // } // NOT YET IMPLEMENTED // else if (!strcmp (keyp, "CIDAREA")) // { // local_tokens[0] = 1; // local_tokens[1] = 0x4A; // } // NOT YET IMPLEMENTED // else if (!strcmp (keyp, "CIDEXCH")) // { // local_tokens[0] = 1; // local_tokens[1] = 0x4B; // } // NOT YET IMPLEMENTED // else if (!strcmp (keyp, "CIDNUMBER")) // { // local_tokens[0] = 1; // local_tokens[1] = 0x4C; // } // else if (!strcmp (keyp, "LOGSIZE")) // { // local_tokens[0] = 1; // local_tokens[1] = 0x4D; // } // NOT YET IMPLEMENTED // else if (!strcmp (keyp, "NETBYTE")) // { // read_word (keyp); // if (strcmp (keyp, "(")) // nsyn_error (keyp, "Expecting \"(\""); // read_word (keyp); // num = getnumber (keyp); // if ((num < 0) || (num > 40)) // nval_error (keyp, "Expecting NETBYTE value between 0 and 40"); // read_word (keyp); // if (strcmp (keyp, ")")) // nsyn_error (keyp, "Expecting \")\""); // local_tokens[0] = 2; // local_tokens[1] = 0x4E; // local_tokens[2] = num; // } // NOT YET IMPLEMENTED // else if (!strcmp (keyp, "ACPOWER")) // { // local_tokens[0] = 1; // local_tokens[1] = 0x4F; // } // NOT YET IMPLEMENTED // else if (!strcmp (keyp, "ACFAILED")) // { // local_tokens[0] = 1; // local_tokens[1] = 0x50; // } // NOT SUPPORTED // else if (!strcmp (keyp, "FREQUENCY")) // { // read_word (keyp); // if (strcmp (keyp, "(")) // nsyn_error (keyp, "Expecting \"(\""); // read_word (keyp); // num = getnumber (keyp); // if ((num < 0) || (num > 7)) // nval_error (keyp, "Expecting FREQUENCY value between 0 and 7"); // read_word (keyp); // if (strcmp (keyp, ")")) // nsyn_error (keyp, "Expecting \")\""); // local_tokens[0] = 2; // local_tokens[1] = 0x51; // local_tokens[2] = num; // } // NOT SUPPORTED // else if (!strcmp (keyp, "TOTAL")) // { // read_word (keyp); // if (strcmp (keyp, "(")) // nsyn_error (keyp, "Expecting \"(\""); // read_word (keyp); // num = getnumber (keyp); // if ((num < 0) || (num > 7)) // nval_error (keyp, "Expecting TOTAL value between 0 and 7"); // read_word (keyp); // if (strcmp (keyp, ")")) // nsyn_error (keyp, "Expecting \")\""); // local_tokens[0] = 2; // local_tokens[1] = 0x52; // local_tokens[2] = num; // } else if (!strcmp (keyp, "KEYDIGIT")) { read_word (keyp); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > 7)) nval_error (keyp, "Expecting KEYDIGIT value between 0 and 7"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); local_tokens[0] = 2; local_tokens[1] = 0x53; local_tokens[2] = num; } else if (!strcmp (keyp, "KEYNUMBER")) { read_word (keyp); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > 7)) nval_error (keyp, "Expecting KEYNUMBER value between 0 and 7"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); local_tokens[0] = 2; local_tokens[1] = 0x54; local_tokens[2] = num; } else if (!strcmp (keyp, "HCS_FPGA")) { read_word (keyp); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); if ((*keyp >= '0') && (*keyp <= '9')) { num = getnumber (keyp); // FPGA addresses are word offsets if ((num < 0) || (num > 255)) val_error (keyp); read_word (keyp); } else { if (!strcmp (keyp, "FPGA_VOICE")) num = 1; else if (!strcmp (keyp, "FPGA_SPI")) num = 2; else if (!strcmp (keyp, "FPGA_RS232_0")) num = 3; else if (!strcmp (keyp, "FPGA_RS485_0")) num = 5; else if (!strcmp (keyp, "FPGA_HCSNET")) num = 5; else if (!strcmp (keyp, "FPGA_RS485_1")) num = 6; else if (!strcmp (keyp, "FPGA_FASTNET")) num = 6; else if (!strcmp (keyp, "FPGA_RS485_2")) num = 7; else if (!strcmp (keyp, "FPGA_KEYBD")) num = 8; else if (!strcmp (keyp, "FPGA_X10")) num = 10; else nsyn_error (keyp, "Invalid HCS_FPGA keyword \"=\""); } if ((num < 0) || (num > 255)) nval_error (keyp, "Expecting HCS_FPGA register select between 0 and 255"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); local_tokens[0] = 2; local_tokens[1] = 0x55; local_tokens[2] = num; } else if (!strcmp (keyp, "X10")) { local_tokens[0] = 2; local_tokens[1] = 0x55; local_tokens[2] = 0x0a; } else if (!strcmp (keyp, "KEYBD")) { local_tokens[0] = 1; local_tokens[1] = 0x56; } // NOT YET IMPLEMENTED // else if (!strcmp (keyp, "CID")) // { // read_word (keyp); // if (strcmp (keyp, "(")) //syn_error (keyp, "Expecting \"(\""); // read_word (keyp); // num = getnumber (keyp); // if ((num < 0) || (num > 255)) //val_error (keyp, "Expecting CID buffer value between 0 and 255"); // read_word (keyp); // if (strcmp (keyp, ")")) //syn_error (keyp, "Expecting \")\""); // local_tokens[0] = 1; // local_tokens[1] = 0x57; // local_tokens[2] = num; // } else if (!strcmp (keyp, "RESET")) { local_tokens[0] = 1; local_tokens[1] = 0x16; } // Unary operators are treated like an operand with a recursive call // to process_operand else if (!strcmp (keyp, "ABS")) { out_file(TOKEN_OP_ABS); process_operand(); // recursive call will fetch unary operand local_tokens[0] = 0; // nothing to load after unary operator } else if (!strcmp (keyp, "-")) { out_file(TOKEN_OP_NEG); process_operand(); // recursive call will fetch unary operand local_tokens[0] = 0; // nothing to load after unary operator } else if (!strcmp (keyp, "~")) { out_file(TOKEN_OP_LNOT); process_operand(); // recursive call will fetch unary operand local_tokens[0] = 0; // nothing to load after unary operator } // @FPGA added ability to enter number in hex format // else if ((*keyp == '0') && (*(keyp + 1) == 'X')) // { // keyp = keyp + 2; //skip over 0x // num = atoh (keyp); // if ((num >= 0) && (num <= 255)) // { // local_tokens[0] = 2; // local_tokens[1] = 0x34; // local_tokens[2] = num; // } // else // { // local_tokens[0] = 3; // local_tokens[1] = 0x35; // local_tokens[2] = num % 256; // local_tokens[3] = num / 256; // } // } else if (isdigit (keyp[0])) { num = getnumber (keyp); if ((num & 0xff00) == 0) { local_tokens[0] = 2; local_tokens[1] = 0x34; local_tokens[2] = num; } else { local_tokens[0] = 3; local_tokens[1] = 0x35; local_tokens[2] = num & 0xff; local_tokens[3] = (num & 0xff00) >> 8; } } else if (*keyp == 0) { if (verbose_debug == 1) printf("At end of file\n"); return(0); // nothing more in file, so return 0 } else // no operand found { printf("ending process_operand type 0, new keyword is %s \n",keyp); return(0); // no operand found, so return 0 } if (eqn_debug == 1) printf("ending process_operand type 1, new keyword is %s \n",keyp); // store the token and parameters into the events.bin output file. for (num = 1; num <= local_tokens[0]; num++) out_file(local_tokens[num]); return(1); // gets here if found an operand, return 1 } ////////////////////////////////////////////////////////// // Process_equation is used as the then (or else) clause, // and now also for the IF compare expression. This is new // from HCS2, which had a special IF compare expression evaluator. // I got rid of that (process_if) to simplify maintenance and to give // the full power of a math expression in the IF compare clause. // count_parens is a badly done means of tracking mismatched // parens that keeps this from being recursive (global var). // I get around this sort of with the local_parens tracking. // This routine will stop if there is an extra left hand paren. // // There is a problem in that this does not know when to stop // processing. If it reads a terminate token (ELSE/IS/END) it // can stop, but if there are consecutive actions, it does not // have a way to distinguish. For example, the following // // timer(0) = variable(0) // variable(0) = timer(0) // is legal but doesnt have a specific syntactical terminator // between them so equation will not stop. // // So--the fix is to make process_equation check for consecutive // operands. It will terminate either if a keyword is unrecognized // (typically a terminator keyword such as ELSE/IS/END) or if there // are two consecutive operatands. // // process_equation assumes that an old keyword is present in keyp. // and that a new one must be fetched. // process_equation will end with the terminating keyword in the // keyp location. ////////////////////////////////////////////////////////// void process_equation () { // maximum of four bytes in any given operand. Can increase this if need // more. int i; int token_list[5]; int local_paren_count; int loc_eqn_end; if (eqn_debug == 1) printf("Starting equation processing at %x\n",bytectr+TOKEN_CFG_OFFSET); proc_eqn = true; loc_eqn_end = false; local_paren_count = count_parens; // initialize to current paren count global // now process operands followed by operators. The left paren // is treated like an operand and recursively calls process_equation. while (loc_eqn_end == false) { // process operators and operands. If an operand is not followed by // an operator, or an operator is not followed by an operand, terminate. // This can legally happen if two actions in sequence occur--but must // stop the equation processing. In addition, if the operator is a // unary operator (eg, "-" or "~") then it may follow another operator. if (process_operand() != 1) // retrieve new operand from file { // and save info into token_array loc_eqn_end = true; // stop processing this equation nsyn_error (keyp, "Expecting an operand"); } if (process_operator(token_list) == 1) // retrieve new operator from file { // store the token and parameters into the events.bin output file. // Dont load a right paren if this is a nested equation for (i = 1; i <= token_list[0]; i++) out_file (token_list[i]); // Now can retrieve another keyword (must be operand) } // If the operand is not followed by an operator, then all done, // exit. else { if (verbose_debug == 1) printf("End equation, not followed by operator at %x with keyword %s\n",bytectr+TOKEN_CFG_OFFSET,keyp); loc_eqn_end = true; // stop processing this equation } // and leave terminating word in keyp. } if (count_parens != local_paren_count) // should have balanced parens at this point paren_error (); if (eqn_debug == 1) printf("Ending equation processing at %x, with keyword %s\n",bytectr+TOKEN_CFG_OFFSET,keyp); return; } //////////////////////////////////////////////////////////////////////////////////// // ACTIONS. // All actions must exit with a token word in the keyw register. //////////////////////////////////////////////////////////////////////////////////// void dothentmr (void) { int timer; get_non_comment_keyword(); // skip past TIMER keyword if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); timer = getnumber (keyp); if ((timer < 0) || (timer > maxtmr)) nval_error (keyp, "Invalid Timer ID"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); read_word (keyp); if (!strcmp (keyp, "OFF")) { out_file (TOKEN_ACTION_TIMER); out_file (timer); out_file (0x00); // specify timer mode } else if (!strcmp (keyp, "ON")) { out_file (TOKEN_ACTION_TIMER); out_file (timer); out_file (0x01); // specify timer mode } else if (!strcmp (keyp, "CLEAR")) { out_file (TOKEN_ACTION_TIMER); out_file (timer); out_file (0x02); // specify timer mode } else if (!strcmp (keyp, "PAUSE")) { out_file (TOKEN_ACTION_TIMER); out_file (timer); out_file (0x03); // specify timer mode } else if (!strcmp (keyp, "CONTINUE")) { out_file (TOKEN_ACTION_TIMER); out_file (timer); out_file (0x04); // specify timer mode } else if (!strcmp (keyp, "MINUTES")) { out_file (TOKEN_ACTION_TIMER); out_file (timer); out_file (0x05); // specify timer mode } else if (!strcmp (keyp, "SECONDS")) { out_file (TOKEN_ACTION_TIMER); out_file (timer); out_file (0x06); // specify timer mode } else nsyn_error (keyp, "Expecting ON, OFF, CLEAR, PAUSE, CONTINUE, SECONDS, or MINUTES"); get_non_comment_keyword(); // get a new keyword for next action } void dothenout (void) { int outnum; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); outnum = getnumber (keyp); if ((outnum < 0) || (outnum > maxout)) nval_error (keyp, "Invalid Output ID"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (TOKEN_ACTION_OUT); out_file (outnum); process_equation(); out_file (0xfc); //if (!strcmp (keyp, "ON")) // just use equation now // out_file (0x82); // //else if (!strcmp (keyp, "TRUE")) // out_file (0x82); // //else if (!strcmp (keyp, "OFF")) // out_file (0x83); // //else if (!strcmp (keyp, "FALSE")) // out_file (0x83); // //else // nsyn_error (keyp, "Expecting ON, TRUE, OFF, or FALSE"); //get_non_comment_keyword(); // Now prefetch next keyword for next // // action } // NOT YET SUPPORTED //void dothennet (void) //{ // int netnum; // get_non_comment_keyword(); // if (strcmp (keyp, "(")) // nsyn_error (keyp, "Expecting \"(\""); // read_word (keyp); // netnum = getnumber (keyp); // if ((netnum < 0) || (netnum > 511)) // nval_error (keyp, "Invalid NetBit ID"); // read_word (keyp); // if (strcmp (keyp, ")")) // nsyn_error (keyp, "Expecting \")\""); // read_word (keyp); // if (strcmp (keyp, "=")) // nsyn_error (keyp, "Expecting \"=\""); // read_word (keyp); // if (!strcmp (keyp, "ON")) // out_file (0x86); // // else if (!strcmp (keyp, "TRUE")) // out_file (0x86); // // else if (!strcmp (keyp, "OFF")) // out_file (0x87); // // else if (!strcmp (keyp, "FALSE")) // out_file (0x87); // // else // nsyn_error (keyp, "Expecting ON, TRUE, OFF, or FALSE"); // out_file (netnum % 256); // out_file (netnum / 256); //} void doinc (void) { int varnum; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); if (strcmp (keyp, "VARIABLE")) nsyn_error (keyp, "Expecting Variable Label/Reference"); read_word (keyp); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); varnum = getnumber (keyp); if ((varnum < 0) || (varnum > maxvar)) nval_error (keyp, "Invalid Variable Reference"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); out_file (0x80); out_file (varnum); } void dodec (void) { int varnum; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); if (strcmp (keyp, "VARIABLE")) nsyn_error (keyp, "Expecting Variable Label/Reference"); read_word (keyp); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); varnum = getnumber (keyp); if ((varnum < 0) || (varnum > maxvar)) nval_error (keyp, "Invalid Variable Reference"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); out_file (0x81); out_file (varnum); } void domsg (void) { char *strp, str[80]; int i, numvar = 0, done = false; int varnum[10]; int loc_num; int fpga_port_num; int fpga_reg_num_occurred; for (i = 0; i < 10; i++) varnum[i] = 0; get_non_comment_keyword(); // skip past previous keyword to quote if (strcmp (keyp, "\"")) nsyn_error (keyp, "Expecting \""); strp = str; read_string (strp); // retrieve the string from the file if (verbose_debug == 1) printf("Got message string %s (%x)\n",strp,strp[0]); if (strcmp (keyp, "\"")) nsyn_error (keyp, "Hmm, Expecting \""); // This section processes escape sequences in the quoted string, // must save away the digits following the %x code. // This skips over the escape but increments the variable list // Each while loop traverses the keyp string to find the quoted // string. If it is found, the number of variables is incremented // and the fileptr to the message string skips 2 chars (each quoted // string consists of 2 chars, %x. This multidimensional search // has to be the worst possible way to do this, but I suppose it // doesn't matter in a small compiler like Xpress // Note that you only want to increment the numvar count // if a corresponding variable name is in the subsequent variable // list of the message. */ // Escape sequence codes: // // %Nn (insert special character) // %Pn (decimal format no leading zeroes) // %Qn (decimal format leading zeroes get space) // %Rn (decimal format leading zeroes) // %A (time display HH:MM) // %B (time display HH:MM:SS) // %C (time display day of week) // %D (time display MM/DD/YYYY) // done = false; keyp = strp; while (!done) { keyp = strstr (keyp, "%P"); if (keyp) { numvar++; keyp += 2; } else done = true; } done = false; keyp = strp; while (!done) { keyp = strstr (keyp, "%p"); if (keyp) { numvar++; keyp += 2; } else done = true; } done = false; keyp = strp; while (!done) { keyp = strstr (keyp, "%Q"); if (keyp) { numvar++; keyp += 2; } else done = true; } done = false; keyp = strp; while (!done) { keyp = strstr (keyp, "%q"); if (keyp) { numvar++; keyp += 2; } else done = true; } done = false; keyp = strp; while (!done) { keyp = strstr (keyp, "%R"); if (keyp) { numvar++; keyp += 2; } else done = true; } done = false; keyp = strp; while (!done) { keyp = strstr (keyp, "%r"); if (keyp) { numvar++; keyp += 2; } else done = true; } done = false; keyp = strp; while (!done) { keyp = strstr (keyp, "%S"); if (keyp) { numvar++; keyp += 2; } else done = true; } done = false; keyp = strp; while (!done) { keyp = strstr (keyp, "%s"); if (keyp) { numvar++; keyp += 2; } else done = true; } done = false; keyp = strp; while (!done) { keyp = strstr (keyp, "%X"); if (keyp) { numvar++; keyp += 2; } else done = true; } done = false; keyp = strp; while (!done) { keyp = strstr (keyp, "%x"); if (keyp) { numvar++; keyp += 2; } else done = true; } // OK, now traverse the variable list, checking that the // right number of variables are in the list. For each // word, check if it is a variable(xx) format, if so, save // the varnum, which will be used to output a variable // number token. Note that // if a variable is listed that doesn't have a declaration // in the string, then a bad error is given (the compiler // tries to interpret the variable as the start of a new // statement). There's not an easy way to fix this since // no variable is expected. */ // // New: If a paren is present instead of "VARIABLE", it // places an expression in the location to be printed. get_non_comment_keyword(); // skip endquote for (i = 0; i < numvar; i++) { if (strcmp (keyp, "VARIABLE")) nsyn_error (keyp, "Expecting Variable Reference"); else { get_non_comment_keyword(); // skip endquote if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); get_non_comment_keyword(); // skip endquote varnum[i] = getnumber (keyp); if ((varnum[i] < 0) || (varnum[i] > NUM_LOC_VARS)) nval_error (keyp, "Invalid Varible Reference/ID"); get_non_comment_keyword(); // skip endquote if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); get_non_comment_keyword(); // skip endquote } } i = 0; while (*strp) { if (*strp == '%') { strp++; switch (toupper (*strp)) { case 'A': out_file (0x80); break; case 'B': out_file (0x81); break; case 'C': out_file (0x82); break; case 'D': out_file (0x83); break; case 'E': out_file (0x84); break; // @FPGA here is the FPGA domsg capability. It will read the // FPGA decimal number following the %F, and convert it to // the 87,xx token sequence that will be interpreted by the // HCS rom. case 'F': out_file (0x87); strp++; fpga_port_num = 0; fpga_reg_num_occurred = 0; while((*strp >= '0') && (*strp <= '9')) { fpga_reg_num_occurred = 1; fpga_port_num = (fpga_port_num * 10) + ((*strp) - '0'); strp++; } if ((fpga_port_num > 255) || (fpga_reg_num_occurred == 0)) { nsyn_error (keyp, "Invalid FPGA register"); out_file (0); // Gotta have something, default to no reg } else { out_file (fpga_port_num); // token 87 expects a read port# strp = strp - 1; //While loop will do one too many } // since there's a strp++ at the end break; // %M will dump data from CID Number if CID Modem is in use case 'M': out_file (TOKEN_FORMAT_COLORTYPE); strp++; fpga_port_num = 0; fpga_reg_num_occurred = 0; while((*strp >= '0') && (*strp <= '9')) { fpga_reg_num_occurred = 1; fpga_port_num = (fpga_port_num * 10) + ((*strp) - '0'); strp++; } if ((fpga_port_num > 16) || (fpga_reg_num_occurred == 0)) { nsyn_error (keyp, "Invalid or missing character type spec"); out_file (0); // Gotta have something, default to no reg } else { out_file (fpga_port_num); // token 85 expects a color or type strp = strp - 1; //While loop will do one too many } // since there's a strp++ at the end break; // %N will output a special character from a two char hex number // This used to be a CID number case 'N': strp++; loc_num = atoh(strp); out_file (loc_num); strp++; break; case 'P': out_file (0x90); strp++; if ((*strp < '1') || (*strp > '5')) nsyn_error (strp, "Expecting number of decimal digits 1-5"); out_file (*strp - '0'); out_file (varnum[i++]); break; case 'Q': out_file (0x91); strp++; if ((*strp < '1') || (*strp > '5')) nsyn_error (strp, "Expecting number of decimal digits 1-5"); out_file (*strp - '0'); out_file (varnum[i++]); break; case 'R': out_file (0x92); strp++; if ((*strp < '1') || (*strp > '5')) nsyn_error (strp, "Expecting number of decimal digits 1-5"); out_file (*strp - '0'); out_file (varnum[i++]); break; case 'S': out_file (0x93); strp++; out_file (*strp - '0'); out_file (varnum[i++]); break; case 'X': out_file (0x9D); strp++; loc_num = *strp - '0'; if ((loc_num < 1) || (loc_num > 5)) nsyn_error (strp, "Expecting number of hex digits 1-4"); out_file (loc_num); out_file (varnum[i++]); break; case '%': out_file ('%'); break; default: out_file ('%'); out_file (*strp); } } else out_file (*strp); strp++; } out_file (CHAR_CR); if (verbose_debug == 1) printf("Ending message, new keyword %s (%x)\n",keyp,keyp[0]); } void do_hcsnet_msg () { // int hcsnet_num; get_non_comment_keyword(); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0x88); //out_file (hcsnet_num); domsg (); } void do_video_msg () { get_non_comment_keyword(); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0x98); domsg (); } void dodiomsg (void) { int dionum; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); dionum = getnumber (keyp); if ((dionum < 0) || (dionum > 7)) nval_error (keyp, "Invalid DIO Module ID (0-7)"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0x89); out_file (dionum); domsg (); } void dotvmsg (void) { int tvnum; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); tvnum = getnumber (keyp); if ((tvnum < 0) || (tvnum > 7)) nval_error (keyp, "Invalid TV Module ID (0-7)"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0x8A); out_file (tvnum); domsg (); } void doconmsg (void) { get_non_comment_keyword(); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0x97); domsg (); } void donetmsg (void) { get_non_comment_keyword(); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0x99); domsg (); } void dominit (void) { char *strp, str[80]; get_non_comment_keyword(); strp = str; if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0x98); read_word (keyp); if (strcmp (keyp, "\"")) nsyn_error (keyp, "Expecting \""); read_string (strp); while (*strp) { out_file (*strp); strp++; } out_file (0x00); } void doalloff (void) { char modlet; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); modlet = *keyp; if ((modlet < 'A') || (modlet > 'P')) nval_error (keyp, "Invalid X10 House Code. Should be A-P"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); out_file (modlet); out_file (0x00); out_file (0x01); out_file (0x00); } void doallon (void) { char modlet; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); modlet = *keyp; if ((modlet < 'A') || (modlet > 'P')) nval_error (keyp, "Invalid X10 House Code. Should be A-P"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); out_file (modlet); out_file (0x00); out_file (0x02); out_file (0x00); } void dothenx10 (void) { char modlet; int modnum, modfunc, level = 0; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); modlet = *keyp++; modnum = getnumber (keyp); if ((modlet < 'A') || (modlet > 'P')) nval_error (keyp, "Invalid X10 House Code. Should be A-P"); if ((modnum < 1) || (modnum > 16)) nval_error (keyp, "Invalid X10 Unit Code. Should be 1-16"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); read_word (keyp); if (!strcmp (keyp, "ON")) modfunc = 0x03; else if (!strcmp (keyp, "ONA")) modfunc = 0x83; else if (!strcmp (keyp, "OFF")) modfunc = 0x04; else if (!strcmp (keyp, "DIM")) { modfunc = 0x05; read_word (keyp); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); level = getnumber (keyp); if ((level < 1) || (level > 30)) nval_error (keyp, "Invalid DIM value. Expecting 0-30."); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); } else if (!strcmp (keyp, "BRIGHT")) { modfunc = 0x06; read_word (keyp); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); level = getnumber (keyp); if ((level < 1) || (level > 30)) nval_error (keyp, "Invalid BRIGHT value. Expecting 0-30."); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); } else nsyn_error (keyp, "Invalid X10 Action. Expecting ON, ONA, OFF, DIM, or BRIGHT"); out_file (modlet); out_file (modnum); out_file (modfunc); out_file (level); get_non_comment_keyword(); // Now prefetch next keyword for next // action } void dothenvar (void) { int num,bitnum; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Variable Expecting \"(\""); read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > maxvar)) nval_error (keyp, "Invliad Variable Reference/ID"); read_word (keyp); if (!strcmp (keyp, ")")) { // If done, not a bit var read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Variable Expecting \"=\""); out_file (TOKEN_ACTION_VAR); out_file (num); process_equation (); out_file (0xfc); } else { // If comma, then set the bit in the var bitnum = getnumber (keyp); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Variable Bit Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Variable Bit Expecting \"=\""); out_file (TOKEN_ACTION_VARBIT); out_file (num); out_file (bitnum); process_equation (); out_file (0xfc); } if (verbose_debug == 1) printf("Done with variable set, keyword %s (%x)\n",keyp,keyp[0]); } void dodac (void) { int num; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > maxdac)) nval_error (keyp, "Invalid DAC reference"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0xa1); out_file (num); process_equation (); out_file (0xfc); } void dorefresh (void) { get_non_comment_keyword(); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0xa2); process_equation (); out_file (0xfc); } void docleartotal (void) { int varnum; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); varnum = getnumber (keyp); if ((varnum < 0) || (varnum > 7)) nval_error (keyp, "Expecting Module ID 0-7"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); out_file (0x9A); out_file (varnum); } // HCS_C Xpress 0.04 expands the LOG keyword to permit several modes of // operation. int dolog (void) { int num; int retval; int logmode; short done; logmode = 0; // default no action get_non_comment_keyword(); if (!strcmp (keyp, "(")) { read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > 254)) nval_error (keyp, "Log Event ID should be 0-254"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0xa4); out_file (num); process_equation (); out_file (0xfc); return(retval); } else if (!strcmp (keyp, "MODE")) { done = 0; while (done == 0) { keyp = keyw; read_word (keyp); if (!strcmp (keyp, "LOG_ON")) // reset to HCS2 mode, reset pointer logmode |= LOG_MODE_ON; if (!strcmp (keyp, "LOG_OFF")) // reset to HCS2 mode, reset pointer logmode |= LOG_MODE_OFF; if (!strcmp (keyp, "LOG_RESET")) // reset to HCS2 mode, reset pointer logmode |= LOG_MODE_RESET; if (!strcmp (keyp, "LOG_NORMAL")) // reset to HCS2 mode, reset pointer logmode |= LOG_MODE_NORMAL; if (!strcmp (keyp, "LOG_VALUE")) // reset to HCS2 mode, reset pointer logmode |= LOG_MODE_VALUE; if (!strcmp (keyp, "LOG_TIME")) // reset to HCS2 mode, reset pointer logmode |= LOG_MODE_TIME; if (!strcmp (keyp, "LOG_ACTIONS")) // reset to HCS2 mode, reset pointer logmode |= LOG_MODE_ACTIONS; if (!strcmp (keyp, "LOG_HCSNET")) // reset to HCS2 mode, reset pointer logmode |= LOG_MODE_ACTIONS; if (!strcmp (keyp, "LOG_X10")) // reset to HCS2 mode, reset pointer logmode |= LOG_MODE_ACTIONS; else { done = 1; if (logmode == 0) nsyn_error (keyp, "Expecting log mode NORMAL/OFF/SHORT/ACTIONS/HCSNET/X10"); } get_non_comment_keyword(); // Now prefetch next keyword for next // action } out_file (TOKEN_ACTION_LOG_MODE); out_file (logmode); } else nsyn_error (keyp, "Expecting \"(\""); } void domcirmsg (void) { int num; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > 7)) nval_error (keyp, "Expecting MCIR ID 0-7"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0xa3); out_file (num); process_equation (); out_file (0xfc); } void domrings (void) { get_non_comment_keyword(); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0xa5); process_equation (); out_file (0xfc); } void docid (void) { get_non_comment_keyword(); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0xa6); process_equation (); out_file (0xfc); } void dothennbyt (void) { int num; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > 7)) val_error (keyp); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0xa7); out_file (num); process_equation (); out_file (0xfc); } // hcsfpga command writes to the FPGA registers. This expects // a decimal register select void dothenhcsfpga (void) { int num; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); if ((*keyp >= '0') && (*keyp <= '9')) { num = getnumber (keyp); // FPGA addresses are word offsets if ((num < 0) || (num > 255)) nval_error (keyp, "Expecting HCS_FPGA register select between 0 and 255"); read_word (keyp); } else { if (!strcmp (keyp, "FPGA_MODE")) num = 0; else if (!strcmp (keyp, "FPGA_VOICE")) num = 1; else if (!strcmp (keyp, "FPGA_SPI")) num = 2; else if (!strcmp (keyp, "FPGA_RS232_0")) num = 3; else if (!strcmp (keyp, "FPGA_RS485_0")) num = 5; else if (!strcmp (keyp, "FPGA_HCSNET")) num = 5; else if (!strcmp (keyp, "FPGA_RS485_1")) num = 6; else if (!strcmp (keyp, "FPGA_FASTNET")) num = 6; else if (!strcmp (keyp, "FPGA_RS485_2")) num = 7; else if (!strcmp (keyp, "FPGA_LCD")) num = 8; else if (!strcmp (keyp, "FPGA_X10")) num = 10; else if (!strcmp (keyp, "FPGA_RELAY")) num = 12; else nsyn_error (keyp, "Invalid HCS_FPGA keyword \"=\""); } if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0x9b); out_file (num); process_equation (); out_file (0xfc); // get_non_comment_keyword(); // Now prefetch next keyword for next // action } // Do_voice command initiates a voice playback. Multiple commands will // override prior commands. void do_voice (void) { int num; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); num = getnumber (keyp); // FPGA addresses are word offsets if ((num < 0) || (num > 1023)) val_error (keyp); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); read_word (keyp); if (!strcmp (keyp, "OFF")) out_file (TOKEN_ACTION_VOICE_STOP); else if (!strcmp (keyp, "ON")) { out_file (TOKEN_ACTION_VOICE); out_file (num & 0xff); out_file ((num >> 8) & 0xff); } else if (!strcmp (keyp, "RECORD")) { out_file (TOKEN_ACTION_VOICE_RECORD); out_file (num & 0xff); out_file ((num >> 8) & 0xff); } else nsyn_error (keyp, "Expecting voice command OFF/ON/RECORD"); get_non_comment_keyword(); // Now prefetch next keyword for next // action } void dothenhcsstr (void) { get_non_comment_keyword(); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0x9c); domsg(); } void dothenphone (void) { int num; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > 50)) val_error (keyp); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0x9d); out_file (num); domsg(); } void dowait (void) { int time; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); time = getnumber (keyp); if ((time < 1) || (time > 255)) nval_error (keyp, "WAIT value must be between 0 and 255 (0 and 25.5 seconds)"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); out_file (0x8f); out_file (time); } void dodialstr (void) { char *strp, strw[80]; get_non_comment_keyword(); strp = strw; if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); if (strcmp (keyp, "\"")) nsyn_error (keyp, "Expecting \""); out_file (0x92); read_string (strp); while (*strp) { out_file (*strp); strp++; } out_file (0); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); } void dobitdir (void) { int num; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > 7)) nval_error (keyp, "Expecting Module ID of 0-7"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0xaa); out_file (num); process_equation (); out_file (0xfc); } void dokeypto (void) { int num; get_non_comment_keyword(); if (strcmp (keyp, "(")) nsyn_error (keyp, "Expecting \"(\""); read_word (keyp); num = getnumber (keyp); if ((num < 0) || (num > 7)) nval_error (keyp, "Expecting Module ID of 0-7"); read_word (keyp); if (strcmp (keyp, ")")) nsyn_error (keyp, "Expecting \")\""); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); out_file (0xab); out_file (num); process_equation (); out_file (0xfc); } void do_if_token() { int num; int first_ifctr,second_ifctr; int if_state; int if_end_backfill; ifcount++; // keep track of number of "IF" keywords (to match with ends) // output the if token to start off out_file (TOKEN_CTL_IF); // compute the compare expression. process_equation(); // The iftable holds addresses that have to be back-filled. Two // locations need to be backfilled, one after the compare expression // that points to the start of the else actions, and one after the // first (true) actions that points to after the else actions. // This is complicated to set up. The iftable entries are stored // with a code to assist in IS address backannotation. Backannotation // starts at the last loaded address (stored in the iftable[x][0] // indexed location). It first reads this location, if < 2, this is not // a valid chain address, just load this backannotation // with the iftable[x][1] destination address, then stop chaining if // any. But if the value is >1, this is a chained backannotate (only // used for the IS goto-END destination backannotation--so hold the // value, re-store the location with the actual back annotate value // (iftable[n][1], and repeat using the value as the new iftable[n][0] // index. The end of the chain must have a 1 (or a 0) in it, and // since chaining goes backwards, will exist at the first IS goto-END // location. // The iftable holds addresses that have to be back-filled. // iftable[n][0] indicates the address to be backfilled // iftable[n][1] indicates what address to backfill with // ifctr keeps track of which if (for nested ifs) is being serviced. // ifcount keeps track of number of ifs, is checked to match number of // ends otherwise syntax error. // NOTE: the process_equation just executed (for the if-statement // compare expression) will leave the terminating token word in the // keyp location. // NORMAL IF THEN ELSE END if (!strcmp (keyp, "THEN")) { if (verbose_debug == 1) printf("THEN found\n"); // get new IF table entry. Also push an entry onto the if_stack--this // will hold the pointer to the iftable that is to be backannotated. first_ifctr = new_ifctr(); // set up new entry for if statement in iftable out_file (TOKEN_CTL_THEN); // now pointing after compare-exp iftable[first_ifctr][0] = bytectr + TOKEN_CFG_OFFSET; // indicate backfill. out_file (0x00); // backfill place holders. out_file (0x00); process_then(); // This may create new iftable entries. Will always // end with a terminating keyword or duplicate operand. if (verbose_debug == 1) printf("done with THEN, keyword %s (%x)\n",keyp,keyp[0]); // NOTE: the process_then just executed (for the if-statement // then action list) will leave the terminating token word in the // keyp location. if (!strcmp (keyp, "ELSE")) { if (verbose_debug == 1) printf("ELSE found\n"); out_file (TOKEN_CTL_ELSE); // else token ends previous THEN section // The else token IS will be preceded by a ELSE goto-END. Signify the end // of the back annotate chain by putting in a 0 here. second_ifctr = new_ifctr(); // Now save away location of ELSE-end backfill address iftable[second_ifctr][0] = bytectr + TOKEN_CFG_OFFSET; // store backannotate location out_file (0x00); // end of backfill chain, this is the out_file (0x00); // ELSE end-goto index. iftable[first_ifctr][1] = bytectr + TOKEN_CFG_OFFSET; // destin after else_token/addr if (verbose_debug == 1) printf("IF-THEN backannotating to just before else stuff, %x: %x\n",first_ifctr,bytectr); process_then(); // This may create new iftable entries. Will always // end with a terminating keyword or duplicate operand. // NOTE: the process_then just executed (for the if-statement // then action list) will leave the terminating token word in the // keyp location. if (verbose_debug == 1) printf("IF-THEN backannotating to just after else stuff, %x: %x\n",second_ifctr,bytectr); iftable[second_ifctr][1] = bytectr + TOKEN_CFG_OFFSET; // Here is the backannotate } else // Normal IF with no ELSE, must backannotate { iftable[first_ifctr][1] = bytectr + TOKEN_CFG_OFFSET; // set dest loc aft else_token/addr } // Whether or not ELSE occurred, cleanup is the same, END causes the current // iftable to be backannotated. This is i, which may be at the if clause (no // else) or the else clause). Either way, store the current token address into // the current iftable destination address. // An END must occur at this point. Dump the if_stack_ptr when done. if (!strcmp (keyp, "END")) { if (verbose_debug == 1) printf("END found at line %d, bytectr %x\n",linenum,bytectr+TOKEN_CFG_OFFSET); endcount++; // keep track to match number of ifs out_file (TOKEN_IF_END); } else { if (verbose_debug == 1) printf("Keyword is %s (%x)\n",keyp,keyp[0]); nsyn_error (keyp, "Must have ELSE or END in this IF statement"); } } //////////////// CASE STATEMENT IF IS THEN ELSE END /////////////////////////// else if (!strcmp (keyp, "IS")) { if (verbose_debug == 1) printf("IS found\n"); // first, indicate that an IS clause is coming. set the current state to // the first IS occurance. if_state = 0; // First couple of IS tokens handled differently while (!strcmp (keyp, "IS")) { // Now process this IS depending on what if-is state is active: // 0 is first IS, start up the case statement // 1 is the second IS, this one sets up the chained link, which is // needed so that all cases jump to the same end address when done. // 2 is subsequent IS, with chaining values being set in the end-GOTO // address. This chained value will get overwritten by the actual // destination address, but permits only one iftable entry for all // case statement gotos. switch (if_state) { ////////////////////////////// first IS ///////////////////////////////// case 0: out_file (TOKEN_CTL_FIRST_IS); // now pointing after compare-exp if (verbose_debug == 1) printf("first IS statement\n"); if_state = 1; // no longer is first // first IS will not be preceded by a IS goto-END, the FIRST_IS token // is immediately followed with a compare value token. read_word (keyp); num = getnumber (keyp); // out_file(num % 256); // store the case constant. out_file(num & 0xff); // store the case constant. out_file(num / 256); if_end_backfill = 0; // No backfill here. get_non_comment_keyword(); // get next keyword // While syntactically a THEN is not required, it makes the events case statement // a lot more readable, so enforce it here. Note that the if-statement compare // expression will leave the terminating token in keyp. if (strcmp (keyp, "THEN")) nsyn_error (keyp, "Missing THEN in case statement IS section\n"); // first IS will not be preceded by a IS goto-END. Signify the end // of the back annotate chain by putting in a 0 here. first_ifctr = new_ifctr(); iftable[first_ifctr][0] = bytectr + TOKEN_CFG_OFFSET; // set location of backfill if (verbose_debug == 1) printf(" backannotate case 0 ptr %x loc %x\n",first_ifctr,bytectr); out_file (0x00); // end of backfill chain out_file (0x00); // Get new keyword for the process_then (actually process_is, but is handled // the same way as process_then) execution process_then(); // This may create new iftable entries. Will always // end with a terminating keyword or duplicate operand. // NOTE: the process_then just executed (for the if-statement // then action list) will leave the terminating token word in the // keyp location. if (verbose_debug == 1) printf("first IS backannotate, first_ifctr is %x, bytectr %x\n",first_ifctr, bytectr + TOKEN_CFG_OFFSET); // backannotate will point to token following, IS/THEN/ELSE/END iftable[first_ifctr][1] = bytectr + TOKEN_CFG_OFFSET; // val to backannotate break; ////////////////////////////// second IS ///////////////////////////////// case 1: out_file (TOKEN_CTL_NEXT_IS); // now pointing after compare-exp if_state = 2; // no longer is first // The second IS will be preceded by a IS goto-END. Signify the end // of the back annotate chain by putting in a 0 here. // Note no iftable entry is created--only one is needed, this is generated at the // END statement. out_file (0x00); // end of backfill chain, this is the out_file (0x00); // IS end-goto index. if_end_backfill = bytectr - 2 + TOKEN_CFG_OFFSET; // save away chain index if (verbose_debug == 1) printf("Second IS, now set new if_end_backfill to %x\n",if_end_backfill); // This is a case statement (keyword "IS"), must retrieve a 16 bit constant, // put it in the file. if (!(read_word (keyp))) break; num = getnumber (keyp); // backannotate from previous stage will go here (the +3 offset) if (verbose_debug == 1) printf("Storing second constant %x at %x\n",num,bytectr + TOKEN_CFG_OFFSET); // out_file(num % 256); // store the case constant. out_file (num & 0xff); // maintain backfill chain out_file(num / 256); // While syntactically a THEN is not required, it makes the events case statement // a lot more readable, so enforce it here read_word (keyp); if (strcmp (keyp, "THEN")) nsyn_error (keyp, "Missing THEN in case statement IS section\n"); first_ifctr = new_ifctr(); // put in entry for IS end-goto iftable[first_ifctr][0] = bytectr + TOKEN_CFG_OFFSET; // set dest location out_file (0x00); // This is the IS skip forward address out_file (0x00); // if compare doesnt match this IS value process_then(); // This may create new iftable entries. Will always // end with a terminating keyword or duplicate operand. // NOTE: the process_then just executed (for the if-statement // then action list) will leave the terminating token word in the // keyp location. if (verbose_debug == 1) printf("second IS backannotate, first_ifctr is %x, bytectr %x\n",first_ifctr, bytectr + TOKEN_CFG_OFFSET); // second ifctr wont have the destination set until the next IS/ELSE/END // Store index to new IS/ELSE/END token iftable[first_ifctr][1] = bytectr + TOKEN_CFG_OFFSET; // val to backannotate break; ////////////////////////////// third and all IS ///////////////////////////////// case 2: out_file (TOKEN_CTL_NEXT_IS); // now pointing after compare-exp // The second IS will be preceded by a IS goto-END. Signify the end // of the back annotate chain by putting in the next chain entry here. // Note no iftable entry is created--only one is needed, this is generated at the // END statement. // out_file (if_end_backfill % 256); // maintain backfill chain out_file (if_end_backfill & 0xff); // maintain backfill chain out_file (if_end_backfill / 256); if (verbose_debug == 1) printf("third IS statement, stuffed if_end_backfill %x,\n",if_end_backfill); if_end_backfill = bytectr - 2 + TOKEN_CFG_OFFSET; // set up for next chain link if (verbose_debug == 1) printf(" now set new if_end_backfill to %x\n",if_end_backfill); // This is a case statement (keyword "IS"), must retrieve a 16 bit constant, // put it in the file. if (!(read_word (keyp))) break; num = getnumber (keyp); // Previous backannotate will point to here (the +3 offset) out_file(num % 256); // store the case constant. out_file(num / 256); // While syntactically a THEN is not required, it makes the events case statement // a lot more readable, so enforce it here read_word (keyp); if (strcmp (keyp, "THEN")) nsyn_error (keyp, "Missing THEN in case statement IS section\n"); first_ifctr = new_ifctr(); // put in entry for IS end-goto iftable[first_ifctr][0] = bytectr + TOKEN_CFG_OFFSET; // set dest location out_file (0x00); // This is the IS skip forward address out_file (0x00); // if compare doesnt match this IS value process_then(); // This may create new iftable entries. Will always // end with a terminating keyword or duplicate operand. // NOTE: the process_then just executed (for the if-statement // then action list) will leave the terminating token word in the // keyp location. if (verbose_debug == 1) printf("third IS backannotate, first_ifctr is %x, bytectr %x\n",first_ifctr, bytectr + TOKEN_CFG_OFFSET); // backannotate to next IS/THEN/ELSE/END iftable[first_ifctr][1] = bytectr + TOKEN_CFG_OFFSET; // val to backannotate break; } // OK, processed a keyword, so now get a new one, if is an IS keyword, continue // processing this IF-IS statement } // If not an IS keyword, will get here, can only be an ELSE or END //////////////////// IS type ELSE ////////////////////////////////////////// // ELSE in an IS statement is simple, but still must put in the prior IS-goto end // stuff. if (!strcmp (keyp, "ELSE")) // If is else, end with jumpto end { if (verbose_debug == 1) printf("Got ELSE of IS clause, always execute, no skip\n"); out_file (TOKEN_CTL_ELSE); // else token ends previous THEN section // The ELSE will be preceded by a IS goto-END. Signify the end // of the back annotate chain by putting in the next chain entry here. // Note no iftable entry is created--only one is needed, this is generated at the // END statement. out_file (if_end_backfill & 0xff); // maintain backfill chain out_file (if_end_backfill / 256); if_end_backfill = bytectr - 2 + TOKEN_CFG_OFFSET; // set up for next chain link if (verbose_debug == 1) printf("third IS statement, if_end_backfill is %x, backfill %x\n",if_end_backfill, bytectr + TOKEN_CFG_OFFSET); // Now just execute, no skip is needed because this must always execute. process_then(); // This may create new iftable entries. Will always // end with a terminating keyword or duplicate operand. // NOTE: the process_then just executed (for the if-statement // then action list) will leave the terminating token word in the // keyp location. } //////////////////// IS type END ////////////////////////////////////////// // Each case (IS term) must finish by doing a process_then. So, done_type // will have a valid terminator.. if (!strcmp (keyp, "END")) // If is else, end with jumpto end { out_file (TOKEN_IF_END); endcount++; // keep track of number of ends to match ifs if (verbose_debug == 1) printf("Got END of IS clause, terminating case statement\n"); // when END encountered in an IS clause, must create the end jump goto chain // for all IS's. Do this by creating the one iftable entry for the chain. Iftable // processing will then read the chain entry, if not zero, will set every value // in the chain to the goto-end address. Thus every IS THEN section will end // with a jump to the end location. Fall-through, unlike C code, is not allowed. // Now create a new entry for all IS clause goto exits. if (if_end_backfill != 0) { first_ifctr = new_ifctr(); // put in entry for IS end-goto iftable[first_ifctr][0] = if_end_backfill; // MUST BACKFILL ONTO THE TOKEN_IF_END! iftable[first_ifctr][1] = bytectr + TOKEN_CFG_OFFSET - 1; // set dest location if (verbose_debug == 1) printf("Set END of IS chain, term %d: {0] %x, [1] %x\n",first_ifctr, if_end_backfill,bytectr + TOKEN_CFG_OFFSET); } } else { nsyn_error (keyp, "Missing END in case statement IS section"); } } else // IF must be followed with either an IS or THEN keyword. { nsyn_error (keyp, "IF statement missing THEN or IS clause\n"); } } ////////////////////////////////////////////////////// // process_then(), what to do after if-expression test // This must contain a list of actions. Since some actions have // process_equation in them, syntactically there is no way to know // when an equation has finished. To fix this, process equation // looks for two consecutive operands, thus assuming that the // first operator terminates the expression. This routine // picks up the operand that is already in the keyp location. // If the operand is not recognized, this routine terminates // without error. If an action is found, actions are continued // until a non-action keyword (normally ELSE/IS/END) is encountered. ////////////////////////////////////////////////////// void process_then() { int done = false; // Retrieve the first action keyword. Normally, process_xxx routines // ASSUME that the keyp string already has a keyword in it, but // process_then always comes after the THEN keyword, so that's // what's in the keyp. So, a get_non_comment_keyword is necessary // at the start. However, don't put that in the while loop, since // each dothenxxx routine must check for all strings (if an equation // call is needed), so it will end with the action keyword already // in keyp. get_non_comment_keyword(); // get next valid keyword // This routine will always // continue to fetch until done, which means that AFTER the process_then, // a keyword that is NOT an action will be sitting in keyp and must // be processed. Note that it will never be an action (actions will // be continuously processed until there are no more). This means // that normally there must be an ELSE/IS/END keyword when process_then is // done. while (!done) // take this out, never is done (aborts with return) { // All actions must end on a new keyword. if (verbose_debug == 1) printf("Compile ACTION keyword %s at line %d, byte %x\n",keyp,linenum,bytectr+TOKEN_CFG_OFFSET); if (!strcmp (keyp, "X10")) dothenx10 (); // else if (!strcmp (keyp, "ALLLIGHTSON")) // doallon (); // // else if (!strcmp (keyp, "ALLUNITSOFF")) // doalloff (); else if (!strcmp (keyp, "TIMER")) dothentmr (); else if (!strcmp (keyp, "OUTPUT")) dothenout (); // else if (!strcmp (keyp, "NETBIT")) // dothennet (); else if (!strcmp (keyp, "VARIABLE")) dothenvar (); // else if (!strcmp (keyp, "DAC")) // dodac (); // All of these special commands such as LCD are actually RS485 module // commands--to prevent HCS_C Xpress from having to maintain support for // these, they will be replaced with a generic command structure. For // example, this LCD command is replaced with HCSNET = "TERM1=This is a string". // else if (!strcmp (keyp, "LCD")) // no longer supported else if (!strcmp (keyp, "HCSNET")) do_hcsnet_msg (); else if (!strcmp (keyp, "VIDEO")) do_video_msg (); else if (!strcmp (keyp, "VOICE")) do_voice (); // else if (!strcmp (keyp, "LPT")) // no longer supported // dodiomsg (); // // else if (!strcmp (keyp, "MCIR")) // no longer supported // domcirmsg (); // // else if (!strcmp (keyp, "TV")) // no longer supported // dotvmsg (); else if (!strcmp (keyp, "CONSOLE")) doconmsg (); // else if (!strcmp (keyp, "NETWORK")) // no longer supported // donetmsg (); // // else if (!strcmp (keyp, "MODEMINIT")) // no longer supported // dominit (); else if (!strcmp (keyp, "REFRESH")) dorefresh (); else if (!strcmp (keyp, "LOG")) { // dolog processes an equation, so expressions must use a terminate // symbol. dolog (); } // else if (!strcmp (keyp, "MODEMRINGS")) // no longer supported // domrings (); // // else if (!strcmp (keyp, "NETBYTE")) // dothennbyt (); // else if (!strcmp (keyp, "HCS_FPGA")) dothenhcsfpga (); else if (!strcmp (keyp, "HCS_STRING")) dothenhcsstr (); // NONE OF THESE COMMANDS ARE IMPLEMENTED YET // else if (!strcmp (keyp, "PHONE")) // dothenphone (); // // else if (!strcmp (keyp, "INC")) // not currently supported // doinc (); // // else if (!strcmp (keyp, "DEC")) // not currently supported // dodec (); // else if (!strcmp (keyp, "WAIT") & sequence) // not currently supported // dowait (); // // else if (!strcmp (keyp, "DIALSTR") & sequence) // dodialstr (); // // else if (!strcmp (keyp, "DIALDIGIT") & sequence) // dodialdigit (); // // else if (!strcmp (keyp, "DIALNUMBER") & sequence) // dodialnumber (); // // else if (!strcmp (keyp, "SAY")) // dosay (); // // else if (!strcmp (keyp, "SAYW") & sequence) // dosayw (); // // else if (!strcmp (keyp, "CLEARTOTAL")) // docleartotal (); // // else if (!strcmp (keyp, "PWMTOTAL")) // dopwmtotal (); // // else if (!strcmp (keyp, "PWMHIGH")) // dopwmhigh (); // // else if (!strcmp (keyp, "BITDIRECTION")) // dobitdir (); // // else if (!strcmp (keyp, "KEYPADTIMEOUT")) // dokeypto (); // // else if (!strcmp (keyp, "RESETIO")) // out_file (0x8B); // // else if (!strcmp (keyp, "CLEARVARIABLES")) // out_file (0x8C); // // else if (!strcmp (keyp, "CLEARTIMERS")) // out_file (0x8D); // // else if (!strcmp (keyp, "CLEARLOG")) // out_file (0x8E); // // else if (!strcmp (keyp, "OFFHOOK") & sequence) // out_file (0x90); // // else if (!strcmp (keyp, "ONHOOK")) // out_file (0x91); else if (!strcmp (keyp, "IF")) { if (verbose_debug == 1) printf("Action level IF keyword %s at line %d, byte %x\n",keyp,linenum,bytectr+TOKEN_CFG_OFFSET); do_if_token(); get_non_comment_keyword(); // get next valid keyword } else { if (verbose_debug == 1) printf("Finished process_then, no more action keywords %s (%x)\n",keyp,keyp[0]); return; // didnt find a key word, so stop. } } return; } void ProcessDEFINE (void) { int i; // Init labels for (i = 0; *label[i] != 0; i++) // Initialize the label list. { } // Now look for defines get_non_comment_keyword(); // get next valid keyword strcpy (label[i], keyp); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); read_line (keyp); strcpy (def[i], keyp); strcat (def[i], " "); *label[i + 1] = 0; // Used to have a NULL } // Process config tells the HCS_C which controller is used, either the // old HCS_C or the new HCS_CX. In addition, the compiler needs to set // up memory space for each HCSNET device. This is done by creating // an array that contains the HCSNET device type, the device address, // the device receive array area, and whether and how often HCSNET is // to sample the device. void ProcessCONFIG (void) { int working_addr; int i; for (i = 0; i < NUM_HCSNET_DEVICES; i++) hcsnet_device_array[i] = 0; // initialize array to all invalid device hcsnet_device_ptr = 0; // Now process configuration get_non_comment_keyword(); // get next valid keyword keyp = keyw; if (!strcmp (keyp, "SC")) { read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); read_word (keyp); sc = 1; // default controller is HCS_C if (!strcmp (keyp, "HCS_C")) { sc = 1; } else if (!strcmp (keyp, "HCS_CX")) // high speed controller { sc = 2; } else nsyn_error (keyp, "Unknown controller. Expecting HCS_C or HCS_CX"); } // DEVICE syntax sets up an array element for each device in the list. // Each array element has the following components: // device number (0 means not a valid device) // device address (0 to 31) // device receive array location (2 bytes) // device sampling rate in seconds. // // Use the following code for devices: // 0: invalid device // 1: IMG (imaging board) // 2: CMB (combo board) // // the syntax is as follows: // DEVICE=CMB23 1 while (strcmp (keyp, "END")) { if (!strcmp (keyp, "HCSNET")) { working_addr = read_prefix(keyp); //get the ID and the address if (!strcmp (keyp, "IMG")) { hcsnet_device_array[hcsnet_device_ptr] = ID_HCSNET_IMG; hcsnet_device_array[hcsnet_device_ptr + 1] = working_addr; hcsnet_device_ptr += 2; if (verbose_debug == 1) printf("HCSNET Device added: ID HCSNET_IMG_ID address %d\n",working_addr); } else if (!strcmp (keyp, "CMB")) { hcsnet_device_array[hcsnet_device_ptr] = ID_HCSNET_CMB; hcsnet_device_array[hcsnet_device_ptr + 1] = working_addr; hcsnet_device_ptr += 2; if (verbose_debug == 1) printf("HCSNET Device added: ID HCSNET_CMB_ID address %d\n",working_addr); } else if (!strcmp (keyp, "TERM")) { hcsnet_device_array[hcsnet_device_ptr] = ID_HCSNET_TERM; hcsnet_device_array[hcsnet_device_ptr + 1] = working_addr; hcsnet_device_ptr += 2; if (verbose_debug == 1) printf("HCSNET Device added: ID HCSNET_CMB_ID address %d\n",working_addr); } else { nval_error (keyp, "Invalid HCSNET device. "); } } else { nval_error (keyp, "Invalid configuration device. "); } get_non_comment_keyword(); // get next valid keyword } } void ProcessDISPLAY (void) { char ch; get_non_comment_keyword(); // get next valid keyword if (strcmp (keyp, "MODULES")) nsyn_error (keyp, "Expecting MODULES"); read_word (keyp); if (strcmp (keyp, "=")) nsyn_error (keyp, "Expecting \"=\""); read_line (keyp); while (*keyp != 0) // Used to have a NULL { ch = *keyp++; if ((ch >= 'A') & (ch <= 'P')) house_used |= (1 << (int) (ch - 'A')); } } void compile (char *infile) { int i, c; int done = 0; unsigned int chain_link,new_link; int max_chainlinks; // Initialize each if table else flag to zero for (i = 0; i < MAX_IFS; i++) iftable[i][0] = 0; // Init the if_stack (keeps state for if statements) // open the token file open_files (infile, "events.bin"); if (errors) return; // just die *label[0] = 0; // used to have a NULL sptr = substring; printf ("\nCompiling events.hcs file...\n"); keyp = keyw; keyp[0] = 0x55; // init to some non-zero (EOF) value. done = 0; while ((keyp[0] != 0) && (done == 0)) // do until end of file { get_non_comment_keyword(); // get next valid keyword if (!strcmp (keyp, "DEFINE")) ProcessDEFINE (); else if (!strcmp (keyp, "CONFIG")) // this is for later ProcessCONFIG (); else if (!strcmp (keyp, "DISPLAY")) ProcessDISPLAY (); else if (!strcmp (keyp, "BEGIN")) { printf ("\nCompiling XPRESS Section, starting at line %d",linenum); out_file_no_check (version / 100); // 0 out_file_no_check (version % 100); // 1 // Right now, configure 32 devices for (i = 0; i <= 0x2c; i++) out_file_no_check(hcsnet_device_array[i]); // 4-0x2e done = 1; } else nsyn_error (keyp, "Unknown section ID (DEFINE/CONFIG/DISPLAY/BEGIN)"); } // done with the configuration section /////////////////////////////////////////////////////////// // Here is the loop that processes if, case, or subroutine statements. // The case statement has the same syntax as if, but adds the // IS keyword which saves away the compare expression value onto // the if_stack, is followed by an integer, and is completed // as if a normal IF THEN statement: // // IF expression // IS integer_val THEN // do_stuff // IS integer_val THEN // do_stuff // ELSE // do_stuff // END // // The IS keyword saves the compare value (if first occurance) // and then compares against the integer val. If there is a match, // the offset following the integer is skipped and the then portion // is executed, else the offset is taken. If another IS occurs, // the preceding THEN section gets a terminating jump to the END // statement, the compare value is read again, and the process // repeated. If the ELSE is encountered, the compare value is // popped off the stack but execution continues as a normal IF // ELSE clause. /////////////////////////////////////////////////////////// keyp[0] = 0xa; // start with a CR just to get it started while (keyp[0] != 0) // end of file { get_non_comment_keyword(); // get next valid keyword if (keyp[0] == 0) break; // EOF, stop compile if (verbose_debug == 1) printf("First level IF keyword %s at line %d, byte %x\n",keyp,linenum,bytectr+TOKEN_CFG_OFFSET); if (!strcmp (keyp, "IF")) { // if (verbose_debug == 1) //printf("initial if started\n"); do_if_token(); } else // UNDEFINED WORD, CREATE A SUBROUTINE CALL { if (verbose_debug == 1) printf("Compile ERROR: keyword %s (%x)\n",keyp,keyp[0]); nsyn_error (keyp, "Expecting IF statement"); } } out_file (TOKEN_ENDTABLE); out_file (TOKEN_ENDTABLE); out_file (TOKEN_ENDTABLE); // // Successful download requires three of these, this is checked out_file (TOKEN_ENDPGM); out_file (TOKEN_ENDPGM); out_file (TOKEN_ENDPGM); // First, do s console dump of the iftable. // if (verbose_debug == 1) // printf ("\n IF link table dump\n"); // i = 1; // only valid locations from 1 up. // while (iftable[i][0] != 0) // { // if (verbose_debug == 1) // printf(" Writing link %d: [%x] <-%x\n",i,iftable[i][0],iftable[i][1]); // i++; // } // if (verbose_debug == 1) // printf(" Writing link %d: [%x] <-%x\n",i,iftable[i][0],iftable[i][1]); /* Now do a second pass, inserting all IF, CASE, and subroutine call offsets */ // Now for each entry, go back and rewind the file to the stored locations. // This will seek the output file at the bytecnt location. It will then store // item 2 (the computed address offset) into the file. i = 1; // only valid locations from 1 up. link_count = 0; while ((iftable[i][0] != 0) && (link_count < MAX_LINK_LIMIT)) // while (iftable[i][0] != 0) { // if (verbose_debug == 1) // printf("Writing link %x: %x <- %x \n",i,iftable[i][0],iftable[i][1]); chain_link = iftable[i][0]; // first, retrieve the head of the chain max_chainlinks = 0; while ((max_chainlinks < MAX_CHAINLINK_LIMIT) && (chain_link != 0)) { // First, retrieve chain value--if zero, no chaining. This allows repeated // backannotation of some addresses, specifically the IS statement end-goto. // Each IS will be preceded by a goto to the end of the case statement, they // all have the same end destination address. This cute scheme saves on // the number of backannotated addresses that have to be stored. fseek (outfile, (long)chain_link, SEEK_SET); new_link = fgetc(outfile); new_link += (fgetc(outfile) << 8); // if (verbose_debug == 1) // printf(" writing link: %x with value %x (new_link overwritten %x)\n", // chain_link,iftable[i][1],new_link); if (new_link <= bytectr + TOKEN_CFG_OFFSET) { fseek (outfile, (long)chain_link, SEEK_SET); // Save the current backannotated address into the file. if (verbose_debug == 1) printf(" Writing link %d: [%x] <-%x, overwriting %x\n", i,chain_link,iftable[i][1],new_link); fputc (iftable[i][1] & 0xff, outfile); fputc ((iftable[i][1] & 0xff00) >> 8, outfile); // Now point to chained backannotate location, if any--0 means none chain_link = new_link; } else { if (verbose_debug == 1) printf(" Bad link reference %d: chain_link %x points to new_link %x\n", i,chain_link,new_link); nsyn_error (keyp, "Linking error, invalid link reference"); break; } max_chainlinks += 1; } if (max_chainlinks == MAX_CHAINLINK_LIMIT) nsyn_error (keyp, "Linking error, could not resolve a chain of IS references"); link_count += 1; i++; } if (link_count == MAX_LINK_LIMIT) nsyn_error (keyp, "Linking error, could not resolve an IF reference"); /* Finally, do a third pass to establish a checksum */ printf ("\nCalculating program checksum..."); i = 0; fseek (outfile, TOKEN_CFG_OFFSET, SEEK_SET); while ((c = fgetc (outfile)) != EOF) i = i + c; fseek (outfile, 0, SEEK_END); fputc (-(i % 256), outfile); close_files (); // ifcount just counts number of "IF"s found, endcount counts the // number of "END"s found. They must match else syntax problem. if (ifcount != endcount) { printf ("\nMissing END somewhere in program"); errors++; } } main (int argc, char *argv[]) { char *file, name[16]; printf ("\n\nXPRESS Compiler"); printf ("\n Version %1d.%02d", version / 100, version % 100); printf ("\n Copyright (c) 2005, Robert Morrison, ZetaEngineering"); printf ("\n Copyright (c) 1999, Creative Control Concepts"); printf ("\n Copyright (c) 1992-1998, Circuit Cellar Inc.\n"); /* setcbrk (1); // Turn on ctrl-break checking */ file = name; if (argc < 2) compile ("EVENTS.HCS"); else { if (strchr (argv[1], '.')) compile (argv[1]); else { strcpy (file, argv[1]); strcat (file, ".HCS"); compile (file); } } if (errors) { if (errors == 1) printf ("\n\n>>> %i error encountered during compile. <<< \a\n", errors); else printf ("\n\n>>> %i errors encountered during compile. <<< \a\n", errors); } else printf ("\n\n*** Compile complete. No errors. ***\n"); //return (errors); return (0); }