📄 intcode.cpp
字号:
//-----------------------------------------------------------------------------
// 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/>.
//------
//
// Generate intermediate code for the ladder logic. Basically generate code
// for a `virtual machine' with operations chosen to be easy to compile to
// AVR or PIC16 code.
// Jonathan Westhues, Nov 2004
//-----------------------------------------------------------------------------
#include <windows.h>
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
#include "ldmicro.h"
#include "intcode.h"
IntOp IntCode[MAX_INT_OPS];
int IntCodeLen;
static DWORD GenSymCountParThis;
static DWORD GenSymCountParOut;
static DWORD GenSymCountOneShot;
static DWORD GenSymCountFormattedString;
static WORD EepromAddrFree;
//-----------------------------------------------------------------------------
// Pretty-print the intermediate code to a file, for debugging purposes.
//-----------------------------------------------------------------------------
void IntDumpListing(char *outFile)
{
FILE *f = fopen(outFile, "w");
if(!f) {
Error("Couldn't dump intermediate code to '%s'.", outFile);
}
int i;
int indent = 0;
for(i = 0; i < IntCodeLen; i++) {
if(IntCode[i].op == INT_END_IF) indent--;
if(IntCode[i].op == INT_ELSE) indent--;
fprintf(f, "%3d:", i);
int j;
for(j = 0; j < indent; j++) fprintf(f, " ");
switch(IntCode[i].op) {
case INT_SET_BIT:
fprintf(f, "set bit '%s'", IntCode[i].name1);
break;
case INT_CLEAR_BIT:
fprintf(f, "clear bit '%s'", IntCode[i].name1);
break;
case INT_COPY_BIT_TO_BIT:
fprintf(f, "let bit '%s' := '%s'", IntCode[i].name1,
IntCode[i].name2);
break;
case INT_SET_VARIABLE_TO_LITERAL:
fprintf(f, "let var '%s' := %d", IntCode[i].name1,
IntCode[i].literal);
break;
case INT_SET_VARIABLE_TO_VARIABLE:
fprintf(f, "let var '%s' := '%s'", IntCode[i].name1,
IntCode[i].name2);
break;
case INT_SET_VARIABLE_ADD:
fprintf(f, "let var '%s' := '%s' + '%s'", IntCode[i].name1,
IntCode[i].name2, IntCode[i].name3);
break;
case INT_SET_VARIABLE_SUBTRACT:
fprintf(f, "let var '%s' := '%s' - '%s'", IntCode[i].name1,
IntCode[i].name2, IntCode[i].name3);
break;
case INT_SET_VARIABLE_MULTIPLY:
fprintf(f, "let var '%s' := '%s' * '%s'", IntCode[i].name1,
IntCode[i].name2, IntCode[i].name3);
break;
case INT_SET_VARIABLE_DIVIDE:
fprintf(f, "let var '%s' := '%s' / '%s'", IntCode[i].name1,
IntCode[i].name2, IntCode[i].name3);
break;
case INT_INCREMENT_VARIABLE:
fprintf(f, "increment '%s'", IntCode[i].name1);
break;
case INT_READ_ADC:
fprintf(f, "read adc '%s'", IntCode[i].name1);
break;
case INT_SET_PWM:
fprintf(f, "set pwm '%s' %s Hz", IntCode[i].name1,
IntCode[i].name2);
break;
case INT_EEPROM_BUSY_CHECK:
fprintf(f, "set bit '%s' if EEPROM busy", IntCode[i].name1);
break;
case INT_EEPROM_READ:
fprintf(f, "read EEPROM[%d,%d+1] into '%s'",
IntCode[i].literal, IntCode[i].literal, IntCode[i].name1);
break;
case INT_EEPROM_WRITE:
fprintf(f, "write '%s' into EEPROM[%d,%d+1]",
IntCode[i].name1, IntCode[i].literal, IntCode[i].literal);
break;
case INT_UART_SEND:
fprintf(f, "uart send from '%s', done? into '%s'",
IntCode[i].name1, IntCode[i].name2);
break;
case INT_UART_RECV:
fprintf(f, "uart recv int '%s', have? into '%s'",
IntCode[i].name1, IntCode[i].name2);
break;
case INT_IF_BIT_SET:
fprintf(f, "if '%s' {", IntCode[i].name1); indent++;
break;
case INT_IF_BIT_CLEAR:
fprintf(f, "if not '%s' {", IntCode[i].name1); indent++;
break;
case INT_IF_VARIABLE_LES_LITERAL:
fprintf(f, "if '%s' < %d {", IntCode[i].name1,
IntCode[i].literal); indent++;
break;
case INT_IF_VARIABLE_EQUALS_VARIABLE:
fprintf(f, "if '%s' == '%s' {", IntCode[i].name1,
IntCode[i].name2); indent++;
break;
case INT_IF_VARIABLE_GRT_VARIABLE:
fprintf(f, "if '%s' > '%s' {", IntCode[i].name1,
IntCode[i].name2); indent++;
break;
case INT_END_IF:
fprintf(f, "}");
break;
case INT_ELSE:
fprintf(f, "} else {"); indent++;
break;
case INT_SIMULATE_NODE_STATE:
// simulation-only; the real code generators don't care
break;
case INT_COMMENT:
fprintf(f, "# %s", IntCode[i].name1);
break;
default:
oops();
}
fprintf(f, "\n");
fflush(f);
}
fclose(f);
}
//-----------------------------------------------------------------------------
// Convert a hex digit (0-9a-fA-F) to its hex value, or return -1 if the
// character is not a hex digit.
//-----------------------------------------------------------------------------
int HexDigit(int c)
{
if((c >= '0') && (c <= '9')) {
return c - '0';
} else if((c >= 'a') && (c <= 'f')) {
return 10 + (c - 'a');
} else if((c >= 'A') && (c <= 'F')) {
return 10 + (c - 'A');
}
return -1;
}
//-----------------------------------------------------------------------------
// Generate a unique symbol (unique with each call) having the given prefix
// guaranteed not to conflict with any user symbols.
//-----------------------------------------------------------------------------
static void GenSymParThis(char *dest)
{
sprintf(dest, "$parThis_%04x", GenSymCountParThis);
GenSymCountParThis++;
}
static void GenSymParOut(char *dest)
{
sprintf(dest, "$parOut_%04x", GenSymCountParOut);
GenSymCountParOut++;
}
static void GenSymOneShot(char *dest)
{
sprintf(dest, "$oneShot_%04x", GenSymCountOneShot);
GenSymCountOneShot++;
}
static void GenSymFormattedString(char *dest)
{
sprintf(dest, "$formattedString_%04x", GenSymCountFormattedString);
GenSymCountFormattedString++;
}
//-----------------------------------------------------------------------------
// Compile an instruction to the program.
//-----------------------------------------------------------------------------
static void Op(int op, char *name1, char *name2, char *name3, SWORD lit)
{
IntCode[IntCodeLen].op = op;
if(name1) strcpy(IntCode[IntCodeLen].name1, name1);
if(name2) strcpy(IntCode[IntCodeLen].name2, name2);
if(name3) strcpy(IntCode[IntCodeLen].name3, name3);
IntCode[IntCodeLen].literal = lit;
IntCodeLen++;
}
static void Op(int op, char *name1, char *name2, SWORD lit)
{
Op(op, name1, name2, NULL, lit);
}
static void Op(int op, char *name1, SWORD lit)
{
Op(op, name1, NULL, NULL, lit);
}
static void Op(int op, char *name1, char *name2)
{
Op(op, name1, name2, NULL, 0);
}
static void Op(int op, char *name1)
{
Op(op, name1, NULL, NULL, 0);
}
static void Op(int op)
{
Op(op, NULL, NULL, NULL, 0);
}
//-----------------------------------------------------------------------------
// Compile the instruction that the simulator uses to keep track of which
// nodes are energized (so that it can display which branches of the circuit
// are energized onscreen). The MCU code generators ignore this, of course.
//-----------------------------------------------------------------------------
static void SimState(BOOL *b, char *name)
{
IntCode[IntCodeLen].op = INT_SIMULATE_NODE_STATE;
strcpy(IntCode[IntCodeLen].name1, name);
IntCode[IntCodeLen].poweredAfter = b;
IntCodeLen++;
}
//-----------------------------------------------------------------------------
// printf-like comment function
//-----------------------------------------------------------------------------
void Comment(char *str, ...)
{
va_list f;
char buf[MAX_NAME_LEN];
va_start(f, str);
vsprintf(buf, str, f);
Op(INT_COMMENT, buf);
}
//-----------------------------------------------------------------------------
// Calculate the period in scan units from the period in microseconds, and
// raise an error if the given period is unachievable.
//-----------------------------------------------------------------------------
static int TimerPeriod(ElemLeaf *l)
{
int period = (l->d.timer.delay / Prog.cycleTime) - 1;
if(period < 1) {
Error(_("Timer period too short (needs faster cycle time)."));
CompileError();
}
if(period >= (1 << 15)) {
Error(_("Timer period too long (max 32767 times cycle time); use a "
"slower cycle time."));
CompileError();
}
return period;
}
//-----------------------------------------------------------------------------
// Is an expression that could be either a variable name or a number a number?
//-----------------------------------------------------------------------------
static BOOL IsNumber(char *str)
{
if(*str == '-' || isdigit(*str)) {
return TRUE;
} else if(*str == '\'') {
// special case--literal single character
return TRUE;
} else {
return FALSE;
}
}
//-----------------------------------------------------------------------------
// Try to turn a string into a 16-bit constant, and raise an error if
// something bad happens when we do so (e.g. out of range).
//-----------------------------------------------------------------------------
SWORD CheckMakeNumber(char *str)
{
int val;
if(*str == '\'') {
val = str[1];
} else {
val = atoi(str);
}
if(val < -32768 || val > 32767) {
Error(_("Constant %d out of range: -32768 to 32767 inclusive."), val);
CompileError();
}
return (SWORD)val;
}
//-----------------------------------------------------------------------------
// Return an integer power of ten.
//-----------------------------------------------------------------------------
static int TenToThe(int x)
{
int i;
int r = 1;
for(i = 0; i < x; i++) {
r *= 10;
}
return r;
}
//-----------------------------------------------------------------------------
// Compile code to evaluate the given bit of ladder logic. The rung input
// state is in stateInOut before calling and will be in stateInOut after
// calling.
//-----------------------------------------------------------------------------
static char *VarFromExpr(char *expr, char *tempName)
{
if(IsNumber(expr)) {
Op(INT_SET_VARIABLE_TO_LITERAL, tempName, CheckMakeNumber(expr));
return tempName;
} else {
return expr;
}
}
static void IntCodeFromCircuit(int which, void *any, char *stateInOut)
{
ElemLeaf *l = (ElemLeaf *)any;
switch(which) {
case ELEM_SERIES_SUBCKT: {
int i;
ElemSubcktSeries *s = (ElemSubcktSeries *)any;
Comment("start series [");
for(i = 0; i < s->count; i++) {
IntCodeFromCircuit(s->contents[i].which, s->contents[i].d.any,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -