summaryrefslogtreecommitdiff
path: root/ldmicro/ansic.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ldmicro/ansic.cpp')
-rw-r--r--ldmicro/ansic.cpp427
1 files changed, 427 insertions, 0 deletions
diff --git a/ldmicro/ansic.cpp b/ldmicro/ansic.cpp
new file mode 100644
index 0000000..ca13836
--- /dev/null
+++ b/ldmicro/ansic.cpp
@@ -0,0 +1,427 @@
+//-----------------------------------------------------------------------------
+// Copyright 2007 Jonathan Westhues
+//
+// This file is part of LDmicro.
+//
+// LDmicro is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// LDmicro 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 LDmicro. If not, see <http://www.gnu.org/licenses/>.
+//------
+//
+// Write the program as ANSI C source. This is very simple, because the
+// intermediate code structure is really a lot like C. Someone else will be
+// responsible for calling us with appropriate timing.
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+#include "linuxUI.h"
+#include <stdio.h>
+#include <setjmp.h>
+#include <stdlib.h>
+
+#include "ldmicro.h"
+#include "intcode.h"
+
+static char SeenVariables[MAX_IO][MAX_NAME_LEN];
+int SeenVariablesCount;
+
+//-----------------------------------------------------------------------------
+// Have we seen a variable before? If not then no need to generate code for
+// it, otherwise we will have to make a declaration, and mark it as seen.
+//-----------------------------------------------------------------------------
+static BOOL SeenVariable(char *name)
+{
+ int i;
+ for(i = 0; i < SeenVariablesCount; i++) {
+ if(strcmp(SeenVariables[i], name)==0) {
+ return TRUE;
+ }
+ }
+ if(i >= MAX_IO) oops();
+ strcpy(SeenVariables[i], name);
+ SeenVariablesCount++;
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Turn an internal symbol into a C name; only trick is that internal symbols
+// use $ for symbols that the int code generator needed for itself, so map
+// that into something okay for C.
+//-----------------------------------------------------------------------------
+#define ASBIT 1
+#define ASINT 2
+static char *MapSym(char *str, int how)
+{
+ if(!str) return NULL;
+
+ static char AllRets[16][MAX_NAME_LEN+30];
+ static int RetCnt;
+
+ RetCnt = (RetCnt + 1) & 15;
+
+ char *ret = AllRets[RetCnt];
+
+ // The namespace for bit and integer variables is distinct.
+ char bit_int;
+ if(how == ASBIT) {
+ bit_int = 'b';
+ } else if(how == ASINT) {
+ bit_int = 'i';
+ } else {
+ oops();
+ }
+
+ // User and internal symbols are distinguished.
+ if(*str == '$') {
+ sprintf(ret, "I_%c_%s", bit_int, str+1);
+ } else {
+ sprintf(ret, "U_%c_%s", bit_int, str);
+ }
+ return ret;
+}
+
+//-----------------------------------------------------------------------------
+// Generate a declaration for an integer var; easy, a static 16-bit qty.
+//-----------------------------------------------------------------------------
+static void DeclareInt(FILE *f, char *str)
+{
+ fprintf(f, "STATIC SWORD %s = 0;\n", str);
+}
+
+//-----------------------------------------------------------------------------
+// Generate a declaration for a bit var; three cases, input, output, and
+// internal relay. An internal relay is just a BOOL variable, but for an
+// input or an output someone else must provide read/write functions.
+//-----------------------------------------------------------------------------
+static void DeclareBit(FILE *f, char *str)
+{
+ // The mapped symbol has the form U_b_{X,Y,R}name, so look at character
+ // four to determine if it's an input, output, internal relay.
+ if(str[4] == 'X') {
+ fprintf(f, "\n");
+ fprintf(f, "/* You provide this function. */\n");
+ fprintf(f, "PROTO(extern BOOL Read_%s(void);)\n", str);
+ fprintf(f, "\n");
+ } else if(str[4] == 'Y') {
+ fprintf(f, "\n");
+ fprintf(f, "/* You provide these functions. */\n");
+ fprintf(f, "PROTO(BOOL Read_%s(void);)\n", str);
+ fprintf(f, "PROTO(void Write_%s(BOOL v);)\n", str);
+ fprintf(f, "\n");
+ } else {
+ fprintf(f, "STATIC BOOL %s = 0;\n", str);
+ fprintf(f, "#define Read_%s() %s\n", str, str);
+ fprintf(f, "#define Write_%s(x) %s = x\n", str, str);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Generate declarations for all the 16-bit/single bit variables in the ladder
+// program.
+//-----------------------------------------------------------------------------
+static void GenerateDeclarations(FILE *f)
+{
+ int i;
+ for(i = 0; i < IntCodeLen; i++) {
+ char *bitVar1 = NULL, *bitVar2 = NULL;
+ char *intVar1 = NULL, *intVar2 = NULL, *intVar3 = NULL;
+
+ switch(IntCode[i].op) {
+ case INT_SET_BIT:
+ case INT_CLEAR_BIT:
+ bitVar1 = IntCode[i].name1;
+ break;
+
+ case INT_COPY_BIT_TO_BIT:
+ bitVar1 = IntCode[i].name1;
+ bitVar2 = IntCode[i].name2;
+ break;
+
+ case INT_SET_VARIABLE_TO_LITERAL:
+ intVar1 = IntCode[i].name1;
+ break;
+
+ case INT_SET_VARIABLE_TO_VARIABLE:
+ intVar1 = IntCode[i].name1;
+ intVar2 = IntCode[i].name2;
+ break;
+
+ case INT_SET_VARIABLE_DIVIDE:
+ case INT_SET_VARIABLE_MULTIPLY:
+ case INT_SET_VARIABLE_SUBTRACT:
+ case INT_SET_VARIABLE_ADD:
+ intVar1 = IntCode[i].name1;
+ intVar2 = IntCode[i].name2;
+ intVar3 = IntCode[i].name3;
+ break;
+
+ case INT_INCREMENT_VARIABLE:
+ case INT_READ_ADC:
+ case INT_SET_PWM:
+ intVar1 = IntCode[i].name1;
+ break;
+
+ case INT_UART_RECV:
+ case INT_UART_SEND:
+ intVar1 = IntCode[i].name1;
+ bitVar1 = IntCode[i].name2;
+ break;
+
+ case INT_IF_BIT_SET:
+ case INT_IF_BIT_CLEAR:
+ bitVar1 = IntCode[i].name1;
+ break;
+
+ case INT_IF_VARIABLE_LES_LITERAL:
+ intVar1 = IntCode[i].name1;
+ break;
+
+ case INT_IF_VARIABLE_EQUALS_VARIABLE:
+ case INT_IF_VARIABLE_GRT_VARIABLE:
+ intVar1 = IntCode[i].name1;
+ intVar2 = IntCode[i].name2;
+ break;
+
+ case INT_END_IF:
+ case INT_ELSE:
+ case INT_COMMENT:
+ case INT_SIMULATE_NODE_STATE:
+ case INT_EEPROM_BUSY_CHECK:
+ case INT_EEPROM_READ:
+ case INT_EEPROM_WRITE:
+ break;
+
+ default:
+ oops();
+ }
+ bitVar1 = MapSym(bitVar1, ASBIT);
+ bitVar2 = MapSym(bitVar2, ASBIT);
+
+ intVar1 = MapSym(intVar1, ASINT);
+ intVar2 = MapSym(intVar2, ASINT);
+ intVar3 = MapSym(intVar3, ASINT);
+
+ if(bitVar1 && !SeenVariable(bitVar1)) DeclareBit(f, bitVar1);
+ if(bitVar2 && !SeenVariable(bitVar2)) DeclareBit(f, bitVar2);
+
+ if(intVar1 && !SeenVariable(intVar1)) DeclareInt(f, intVar1);
+ if(intVar2 && !SeenVariable(intVar2)) DeclareInt(f, intVar2);
+ if(intVar3 && !SeenVariable(intVar3)) DeclareInt(f, intVar3);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Actually generate the C source for the program.
+//-----------------------------------------------------------------------------
+static void GenerateAnsiC(FILE *f)
+{
+ int i;
+ int indent = 1;
+ for(i = 0; i < IntCodeLen; i++) {
+
+ if(IntCode[i].op == INT_END_IF) indent--;
+ if(IntCode[i].op == INT_ELSE) indent--;
+
+ int j;
+ for(j = 0; j < indent; j++) fprintf(f, " ");
+
+ switch(IntCode[i].op) {
+ case INT_SET_BIT:
+ fprintf(f, "Write_%s(1);\n", MapSym(IntCode[i].name1, ASBIT));
+ break;
+
+ case INT_CLEAR_BIT:
+ fprintf(f, "Write_%s(0);\n", MapSym(IntCode[i].name1, ASBIT));
+ break;
+
+ case INT_COPY_BIT_TO_BIT:
+ fprintf(f, "Write_%s(Read_%s());\n",
+ MapSym(IntCode[i].name1, ASBIT),
+ MapSym(IntCode[i].name2, ASBIT));
+ break;
+
+ case INT_SET_VARIABLE_TO_LITERAL:
+ fprintf(f, "%s = %d;\n", MapSym(IntCode[i].name1, ASINT),
+ IntCode[i].literal);
+ break;
+
+ case INT_SET_VARIABLE_TO_VARIABLE:
+ fprintf(f, "%s = %s;\n", MapSym(IntCode[i].name1, ASINT),
+ MapSym(IntCode[i].name2, ASINT));
+ break;
+
+ {
+ char op;
+ case INT_SET_VARIABLE_ADD: op = '+'; goto arith;
+ case INT_SET_VARIABLE_SUBTRACT: op = '-'; goto arith;
+ case INT_SET_VARIABLE_MULTIPLY: op = '*'; goto arith;
+ case INT_SET_VARIABLE_DIVIDE: op = '/'; goto arith;
+ arith:
+ fprintf(f, "%s = %s %c %s;\n",
+ MapSym(IntCode[i].name1, ASINT),
+ MapSym(IntCode[i].name2, ASINT),
+ op,
+ MapSym(IntCode[i].name3, ASINT) );
+ break;
+ }
+
+ case INT_INCREMENT_VARIABLE:
+ fprintf(f, "%s++;\n", MapSym(IntCode[i].name1, ASINT));
+ break;
+
+ case INT_IF_BIT_SET:
+ fprintf(f, "if(Read_%s()) {\n",
+ MapSym(IntCode[i].name1, ASBIT));
+ indent++;
+ break;
+
+ case INT_IF_BIT_CLEAR:
+ fprintf(f, "if(!Read_%s()) {\n",
+ MapSym(IntCode[i].name1, ASBIT));
+ indent++;
+ break;
+
+ case INT_IF_VARIABLE_LES_LITERAL:
+ fprintf(f, "if(%s < %d) {\n", MapSym(IntCode[i].name1, ASINT),
+ IntCode[i].literal);
+ indent++;
+ break;
+
+ case INT_IF_VARIABLE_EQUALS_VARIABLE:
+ fprintf(f, "if(%s == %s) {\n", MapSym(IntCode[i].name1, ASINT),
+ MapSym(IntCode[i].name2, ASINT));
+ indent++;
+ break;
+
+ case INT_IF_VARIABLE_GRT_VARIABLE:
+ fprintf(f, "if(%s > %s) {\n", MapSym(IntCode[i].name1, ASINT),
+ MapSym(IntCode[i].name2, ASINT));
+ indent++;
+ break;
+
+ case INT_END_IF:
+ fprintf(f, "}\n");
+ break;
+
+ case INT_ELSE:
+ fprintf(f, "} else {\n"); indent++;
+ break;
+
+ case INT_SIMULATE_NODE_STATE:
+ // simulation-only
+ fprintf(f, "\n");
+ break;
+
+ case INT_COMMENT:
+ if(IntCode[i].name1[0]) {
+ fprintf(f, "/* %s */\n", IntCode[i].name1);
+ } else {
+ fprintf(f, "\n");
+ }
+ break;
+
+ case INT_EEPROM_BUSY_CHECK:
+ case INT_EEPROM_READ:
+ case INT_EEPROM_WRITE:
+ case INT_READ_ADC:
+ case INT_SET_PWM:
+ case INT_UART_RECV:
+ case INT_UART_SEND:
+ Error(_("ANSI C target does not support peripherals "
+ "(UART, PWM, ADC, EEPROM). Skipping that instruction."));
+ break;
+
+ default:
+ oops();
+ }
+ }
+}
+
+void CompileAnsiC(char *dest)
+{
+ SeenVariablesCount = 0;
+
+ FILE *f = fopen(dest, "w");
+ if(!f) {
+ Error(_("Couldn't open file '%s'"), dest);
+ return;
+ }
+
+ fprintf(f,
+"/* This is auto-generated code from LDmicro. Do not edit this file! Go\n"
+" back to the ladder diagram source for changes in the logic, and make\n"
+" any C additions either in ladder.h or in additional .c files linked\n"
+" against this one. */\n"
+"\n"
+"/* You must provide ladder.h; there you must provide:\n"
+" * a typedef for SWORD and BOOL, signed 16 bit and boolean types\n"
+" (probably typedef signed short SWORD; typedef unsigned char BOOL;)\n"
+"\n"
+" You must also provide implementations of all the I/O read/write\n"
+" either as inlines in the header file or in another source file. (The\n"
+" I/O functions are all declared extern.)\n"
+"\n"
+" See the generated source code (below) for function names. */\n"
+"#include \"ladder.h\"\n"
+"\n"
+"/* Define EXTERN_EVERYTHING in ladder.h if you want all symbols extern.\n"
+" This could be useful to implement `magic variables,' so that for\n"
+" example when you write to the ladder variable duty_cycle, your PLC\n"
+" runtime can look at the C variable U_duty_cycle and use that to set\n"
+" the PWM duty cycle on the micro. That way you can add support for\n"
+" peripherals that LDmicro doesn't know about. */\n"
+"#ifdef EXTERN_EVERYTHING\n"
+"#define STATIC \n"
+"#else\n"
+"#define STATIC static\n"
+"#endif\n"
+"\n"
+"/* Define NO_PROTOTYPES if you don't want LDmicro to provide prototypes for\n"
+" all the I/O functions (Read_U_xxx, Write_U_xxx) that you must provide.\n"
+" If you define this then you must provide your own prototypes for these\n"
+" functions in ladder.h, or provide definitions (e.g. as inlines or macros)\n"
+" for them in ladder.h. */\n"
+"#ifdef NO_PROTOTYPES\n"
+"#define PROTO(x)\n"
+"#else\n"
+"#define PROTO(x) x\n"
+"#endif\n"
+"\n"
+"/* U_xxx symbols correspond to user-defined names. There is such a symbol\n"
+" for every internal relay, variable, timer, and so on in the ladder\n"
+" program. I_xxx symbols are internally generated. */\n"
+ );
+
+ // now generate declarations for all variables
+ GenerateDeclarations(f);
+
+ fprintf(f,
+"\n"
+"\n"
+"/* Call this function once per PLC cycle. You are responsible for calling\n"
+" it at the interval that you specified in the MCU configuration when you\n"
+" generated this code. */\n"
+"void PlcCycle(void)\n"
+"{\n"
+ );
+
+ GenerateAnsiC(f);
+
+ fprintf(f, "}\n");
+ fclose(f);
+
+ char str[MAX_PATH+500];
+ sprintf(str, _("Compile successful; wrote C source code to '%s'.\r\n\r\n"
+ "This is not a complete C program. You have to provide the runtime "
+ "and all the I/O routines. See the comments in the source code for "
+ "information about how to do this."), dest);
+ CompileSuccessfulMessage(str);
+}