249 lines
7.6 KiB
C++
249 lines
7.6 KiB
C++
//-----------------------------------------------------------------------------
|
|
// 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/>.
|
|
//------
|
|
//
|
|
// A crunched-down version of the intermediate code (e.g. assigning addresses
|
|
// to all the variables instead of just working with their names), suitable
|
|
// for interpretation.
|
|
// Jonathan Westhues, Aug 2005
|
|
//-----------------------------------------------------------------------------
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <setjmp.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "ldmicro.h"
|
|
#include "intcode.h"
|
|
|
|
static char Variables[MAX_IO][MAX_NAME_LEN];
|
|
static int VariablesCount;
|
|
|
|
static char InternalRelays[MAX_IO][MAX_NAME_LEN];
|
|
static int InternalRelaysCount;
|
|
|
|
typedef struct {
|
|
WORD op;
|
|
WORD name1;
|
|
WORD name2;
|
|
WORD name3;
|
|
SWORD literal;
|
|
} BinOp;
|
|
|
|
static BinOp OutProg[MAX_INT_OPS];
|
|
|
|
static WORD AddrForInternalRelay(char *name)
|
|
{
|
|
int i;
|
|
for(i = 0; i < InternalRelaysCount; i++) {
|
|
if(strcmp(InternalRelays[i], name)==0) {
|
|
return i;
|
|
}
|
|
}
|
|
strcpy(InternalRelays[i], name);
|
|
InternalRelaysCount++;
|
|
return i;
|
|
}
|
|
|
|
static WORD AddrForVariable(char *name)
|
|
{
|
|
int i;
|
|
for(i = 0; i < VariablesCount; i++) {
|
|
if(strcmp(Variables[i], name)==0) {
|
|
return i;
|
|
}
|
|
}
|
|
strcpy(Variables[i], name);
|
|
VariablesCount++;
|
|
return i;
|
|
}
|
|
|
|
static void Write(FILE *f, BinOp *op)
|
|
{
|
|
BYTE *b = (BYTE *)op;
|
|
int i;
|
|
for(i = 0; i < sizeof(*op); i++) {
|
|
fprintf(f, "%02x", b[i]);
|
|
}
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
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_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) 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);
|
|
}
|