void CompileInterpreted(char *outFile) { FILE *f = fopen(outFile, "w"); if(!f) { Error(_("Couldn't write to '%s'"), outFile); return; } InternalRelaysCount = 0; VariablesCount = 0; fprintf(f, "$$LDcode\n"); int ipc; int outPc; BinOp op; // Convert the if/else structures in the intermediate code to absolute // conditional jumps, to make life a bit easier for the interpreter. #define MAX_IF_NESTING 32 int ifDepth = 0; // PC for the if(...) instruction, which we will complete with the // 'jump to if false' address (which is either the ELSE+1 or the ENDIF+1) int ifOpIf[MAX_IF_NESTING]; // PC for the else instruction, which we will complete with the // 'jump to if reached' address (which is the ENDIF+1) int ifOpElse[MAX_IF_NESTING]; outPc = 0; for(ipc = 0; ipc < IntCodeLen; ipc++) { memset(&op, 0, sizeof(op)); op.op = IntCode[ipc].op; switch(IntCode[ipc].op) { case INT_CLEAR_BIT: case INT_SET_BIT: op.name1 = AddrForInternalRelay(IntCode[ipc].name1); break; case INT_COPY_BIT_TO_BIT: op.name1 = AddrForInternalRelay(IntCode[ipc].name1); op.name2 = AddrForInternalRelay(IntCode[ipc].name2); break; case INT_SET_VARIABLE_TO_LITERAL: op.name1 = AddrForVariable(IntCode[ipc].name1); op.literal = IntCode[ipc].literal; break; case INT_SET_VARIABLE_TO_VARIABLE: op.name1 = AddrForVariable(IntCode[ipc].name1); op.name2 = AddrForVariable(IntCode[ipc].name2); break; case INT_INCREMENT_VARIABLE: op.name1 = AddrForVariable(IntCode[ipc].name1); break; case INT_SET_VARIABLE_ADD: case INT_SET_VARIABLE_SUBTRACT: case INT_SET_VARIABLE_MULTIPLY: case INT_SET_VARIABLE_DIVIDE: op.name1 = AddrForVariable(IntCode[ipc].name1); op.name2 = AddrForVariable(IntCode[ipc].name2); op.name3 = AddrForVariable(IntCode[ipc].name3); break; case INT_IF_BIT_SET: case INT_IF_BIT_CLEAR: op.name1 = AddrForInternalRelay(IntCode[ipc].name1); goto finishIf; case INT_IF_VARIABLE_LES_LITERAL: op.name1 = AddrForVariable(IntCode[ipc].name1); op.literal = IntCode[ipc].literal; goto finishIf; case INT_IF_VARIABLE_EQUALS_VARIABLE: case INT_IF_VARIABLE_GRT_VARIABLE: op.name1 = AddrForVariable(IntCode[ipc].name1); op.name2 = AddrForVariable(IntCode[ipc].name2); goto finishIf; finishIf: ifOpIf[ifDepth] = outPc; ifOpElse[ifDepth] = 0; ifDepth++; // jump target will be filled in later break; case INT_ELSE: ifOpElse[ifDepth-1] = outPc; // jump target will be filled in later break; case INT_END_IF: --ifDepth; if(ifOpElse[ifDepth] == 0) { // There is no else; if should jump straight to the // instruction after this one if the condition is false. OutProg[ifOpIf[ifDepth]].name3 = outPc-1; } else { // There is an else clause; if the if is false then jump // just past the else, and if the else is reached then // jump to the endif. OutProg[ifOpIf[ifDepth]].name3 = ifOpElse[ifDepth]; OutProg[ifOpElse[ifDepth]].name3 = outPc-1; } // But don't generate an instruction for this. continue; case INT_SIMULATE_NODE_STATE: case INT_COMMENT: // Don't care; ignore, and don't generate an instruction. continue; case INT_READ_SFR_LITERAL: case INT_WRITE_SFR_LITERAL: case INT_SET_SFR_LITERAL: case INT_CLEAR_SFR_LITERAL: case INT_TEST_SFR_LITERAL: case INT_READ_SFR_VARIABLE: case INT_WRITE_SFR_VARIABLE: case INT_SET_SFR_VARIABLE: case INT_CLEAR_SFR_VARIABLE: case INT_TEST_SFR_VARIABLE: case INT_TEST_C_SFR_LITERAL: case INT_WRITE_SFR_LITERAL_L: case INT_WRITE_SFR_VARIABLE_L: case INT_SET_SFR_LITERAL_L: case INT_SET_SFR_VARIABLE_L: case INT_CLEAR_SFR_LITERAL_L: case INT_CLEAR_SFR_VARIABLE_L: case INT_TEST_SFR_LITERAL_L: case INT_TEST_SFR_VARIABLE_L: case INT_TEST_C_SFR_VARIABLE: case INT_TEST_C_SFR_LITERAL_L: case INT_TEST_C_SFR_VARIABLE_L: case INT_EEPROM_BUSY_CHECK: case INT_EEPROM_READ: case INT_EEPROM_WRITE: case INT_READ_ADC: case INT_SET_PWM: case INT_UART_SEND: case INT_UART_RECV: default: Error(_("Unsupported op (anything ADC, PWM, UART, EEPROM, SFR..) for " "interpretable target.")); fclose(f); return; } memcpy(&OutProg[outPc], &op, sizeof(op)); outPc++; } int i; for(i = 0; i < outPc; i++) { Write(f, &OutProg[i]); } memset(&op, 0, sizeof(op)); op.op = INT_END_OF_PROGRAM; Write(f, &op); fprintf(f, "$$bits\n"); for(i = 0; i < InternalRelaysCount; i++) { if(InternalRelays[i][0] != '$') { fprintf(f, "%s,%d\n", InternalRelays[i], i); } } fprintf(f, "$$int16s\n"); for(i = 0; i < VariablesCount; i++) { if(Variables[i][0] != '$') { fprintf(f, "%s,%d\n", Variables[i], i); } } fprintf(f, "$$cycle %d us\n", Prog.cycleTime); fclose(f); char str[MAX_PATH+500]; sprintf(str, _("Compile successful; wrote interpretable code to '%s'.\r\n\r\n" "You probably have to adapt the interpreter to your application. See " "the documentation."), outFile); CompileSuccessfulMessage(str); }
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); }