//-----------------------------------------------------------------------------
// 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 .
//------
//
// Routines common to the code generators for all processor architectures.
// Jonathan Westhues, Nov 2004
//-----------------------------------------------------------------------------
#include
#include
#include
#include "ldmicro.h"
// If we encounter an error while compiling then it's convenient to break
// out of the possibly-deeply-recursed function we're in.
jmp_buf CompileErrorBuf;
// Assignment of the internal relays to memory, efficient, one bit per
// relay.
static struct {
char name[MAX_NAME_LEN];
DWORD addr;
int bit;
BOOL assignedTo;
} InternalRelays[MAX_IO];
static int InternalRelayCount;
// Assignment of the `variables,' used for timers, counters, arithmetic, and
// other more general things. Allocate 2 octets (16 bits) per.
static struct {
char name[MAX_NAME_LEN];
DWORD addrl;
DWORD addrh;
} Variables[MAX_IO];
static int VariableCount;
#define NO_MEMORY 0xffffffff
static DWORD NextBitwiseAllocAddr;
static int NextBitwiseAllocBit;
static int MemOffset;
//-----------------------------------------------------------------------------
// Forget what memory has been allocated on the target, so we start from
// everything free.
//-----------------------------------------------------------------------------
void AllocStart(void)
{
NextBitwiseAllocAddr = NO_MEMORY;
MemOffset = 0;
InternalRelayCount = 0;
VariableCount = 0;
}
//-----------------------------------------------------------------------------
// Return the address of a previously unused octet of RAM on the target, or
// signal an error if there is no more available.
//-----------------------------------------------------------------------------
DWORD AllocOctetRam(void)
{
if(MemOffset >= Prog.mcu->ram[0].len) {
Error(_("Out of memory; simplify program or choose "
"microcontroller with more memory."));
CompileError();
}
MemOffset++;
return Prog.mcu->ram[0].start + MemOffset - 1;
}
//-----------------------------------------------------------------------------
// Return the address (octet address) and bit of a previously unused bit of
// RAM on the target.
//-----------------------------------------------------------------------------
void AllocBitRam(DWORD *addr, int *bit)
{
if(NextBitwiseAllocAddr != NO_MEMORY) {
*addr = NextBitwiseAllocAddr;
*bit = NextBitwiseAllocBit;
NextBitwiseAllocBit++;
if(NextBitwiseAllocBit > 7) {
NextBitwiseAllocAddr = NO_MEMORY;
}
return;
}
*addr = AllocOctetRam();
*bit = 0;
NextBitwiseAllocAddr = *addr;
NextBitwiseAllocBit = 1;
}
//-----------------------------------------------------------------------------
// Return the address (octet) and bit of the bit in memory that represents the
// given input or output pin. Raises an internal error if the specified name
// is not present in the I/O list or a user error if a pin has not been
// assigned to that I/O name. Will allocate if it no memory allocated for it
// yet, else will return the previously allocated bit.
//-----------------------------------------------------------------------------
static void MemForPin(char *name, DWORD *addr, int *bit, BOOL asInput)
{
int i;
for(i = 0; i < Prog.io.count; i++) {
if(strcmp(Prog.io.assignment[i].name, name)==0)
break;
}
if(i >= Prog.io.count) oops();
if(asInput && Prog.io.assignment[i].type == IO_TYPE_DIG_OUTPUT) oops();
if(!asInput && Prog.io.assignment[i].type == IO_TYPE_DIG_INPUT) oops();
int pin = Prog.io.assignment[i].pin;
for(i = 0; i < Prog.mcu->pinCount; i++) {
if(Prog.mcu->pinInfo[i].pin == pin)
break;
}
if(i >= Prog.mcu->pinCount) {
Error(_("Must assign pins for all I/O.\r\n\r\n"
"'%s' is not assigned."), name);
CompileError();
}
McuIoPinInfo *iop = &Prog.mcu->pinInfo[i];
if(asInput) {
*addr = Prog.mcu->inputRegs[iop->port - 'A'];
} else {
*addr = Prog.mcu->outputRegs[iop->port - 'A'];
}
*bit = iop->bit;
}
//-----------------------------------------------------------------------------
// Determine the mux register settings to read a particular ADC channel.
//-----------------------------------------------------------------------------
BYTE MuxForAdcVariable(char *name)
{
int i;
for(i = 0; i < Prog.io.count; i++) {
if(strcmp(Prog.io.assignment[i].name, name)==0)
break;
}
if(i >= Prog.io.count) oops();
int j;
for(j = 0; j < Prog.mcu->adcCount; j++) {
if(Prog.mcu->adcInfo[j].pin == Prog.io.assignment[i].pin) {
break;
}
}
if(j == Prog.mcu->adcCount) {
Error(_("Must assign pins for all ADC inputs (name '%s')."), name);
CompileError();
}
return Prog.mcu->adcInfo[j].muxRegValue;
}
//-----------------------------------------------------------------------------
// Allocate the two octets (16-bit count) for a variable, used for a variety
// of purposes.
//-----------------------------------------------------------------------------
void MemForVariable(char *name, DWORD *addrl, DWORD *addrh)
{
int i;
for(i = 0; i < VariableCount; i++) {
if(strcmp(name, Variables[i].name)==0) break;
}
if(i >= MAX_IO) {
Error(_("Internal limit exceeded (number of vars)"));
CompileError();
}
if(i == VariableCount) {
VariableCount++;
strcpy(Variables[i].name, name);
Variables[i].addrl = AllocOctetRam();
Variables[i].addrh = AllocOctetRam();
}
*addrl = Variables[i].addrl;
*addrh = Variables[i].addrh;
}
//-----------------------------------------------------------------------------
// Allocate or retrieve the bit of memory assigned to an internal relay or
// other thing that requires a single bit of storage.
//-----------------------------------------------------------------------------
static void MemForBitInternal(char *name, DWORD *addr, int *bit, BOOL writeTo)
{
int i;
for(i = 0; i < InternalRelayCount; i++) {
if(strcmp(name, InternalRelays[i].name)==0)
break;
}
if(i >= MAX_IO) {
Error(_("Internal limit exceeded (number of vars)"));
CompileError();
}
if(i == InternalRelayCount) {
InternalRelayCount++;
strcpy(InternalRelays[i].name, name);
AllocBitRam(&InternalRelays[i].addr, &InternalRelays[i].bit);
InternalRelays[i].assignedTo = FALSE;
}
*addr = InternalRelays[i].addr;
*bit = InternalRelays[i].bit;
if(writeTo) {
InternalRelays[i].assignedTo = TRUE;
}
}
//-----------------------------------------------------------------------------
// Retrieve the bit to read to determine whether a set of contacts is open
// or closed. Contacts could be internal relay, output pin, or input pin,
// or one of the internal state variables ($xxx) from the int code generator.
//-----------------------------------------------------------------------------
void MemForSingleBit(char *name, BOOL forRead, DWORD *addr, int *bit)
{
switch(name[0]) {
case 'X':
if(!forRead) oops();
MemForPin(name, addr, bit, TRUE);
break;
case 'Y':
MemForPin(name, addr, bit, FALSE);
break;
case 'R':
case '$':
MemForBitInternal(name, addr, bit, !forRead);
break;
default:
oops();
break;
}
}
//-----------------------------------------------------------------------------
// Retrieve the bit to write to set the state of an output.
//-----------------------------------------------------------------------------
void MemForCoil(char *name, DWORD *addr, int *bit)
{
switch(name[0]) {
case 'Y':
MemForPin(name, addr, bit, FALSE);
break;
case 'R':
MemForBitInternal(name, addr, bit, TRUE);
break;
default:
oops();
break;
}
}
//-----------------------------------------------------------------------------
// Do any post-compilation sanity checks necessary.
//-----------------------------------------------------------------------------
void MemCheckForErrorsPostCompile(void)
{
int i;
for(i = 0; i < InternalRelayCount; i++) {
if(!InternalRelays[i].assignedTo) {
Error(
_("Internal relay '%s' never assigned; add its coil somewhere."),
InternalRelays[i].name);
CompileError();
}
}
}
//-----------------------------------------------------------------------------
// From the I/O list, determine which pins are inputs and which pins are
// outputs, and pack that in 8-bit format as we will need to write to the
// TRIS or DDR registers. ADC pins are neither inputs nor outputs.
//-----------------------------------------------------------------------------
void BuildDirectionRegisters(BYTE *isInput, BYTE *isOutput)
{
memset(isOutput, 0x00, MAX_IO_PORTS);
memset(isInput, 0x00, MAX_IO_PORTS);
BOOL usedUart = UartFunctionUsed();
BOOL usedPwm = PwmFunctionUsed();
int i;
for(i = 0; i < Prog.io.count; i++) {
int pin = Prog.io.assignment[i].pin;
if(Prog.io.assignment[i].type == IO_TYPE_DIG_OUTPUT ||
Prog.io.assignment[i].type == IO_TYPE_DIG_INPUT)
{
int j;
for(j = 0; j < Prog.mcu->pinCount; j++) {
McuIoPinInfo *iop = &Prog.mcu->pinInfo[j];
if(iop->pin == pin) {
if(Prog.io.assignment[i].type == IO_TYPE_DIG_INPUT) {
isInput[iop->port - 'A'] |= (1 << iop->bit);
} else {
isOutput[iop->port - 'A'] |= (1 << iop->bit);
}
break;
}
}
if(j >= Prog.mcu->pinCount) {
Error(_("Must assign pins for all I/O.\r\n\r\n"
"'%s' is not assigned."),
Prog.io.assignment[i].name);
CompileError();
}
if(usedUart &&
(pin == Prog.mcu->uartNeeds.rxPin ||
pin == Prog.mcu->uartNeeds.txPin))
{
Error(_("UART in use; pins %d and %d reserved for that."),
Prog.mcu->uartNeeds.rxPin, Prog.mcu->uartNeeds.txPin);
CompileError();
}
if(usedPwm && pin == Prog.mcu->pwmNeedsPin) {
Error(_("PWM in use; pin %d reserved for that."),
Prog.mcu->pwmNeedsPin);
CompileError();
}
}
}
}
//-----------------------------------------------------------------------------
// Display our boilerplate warning that the baud rate error is too high.
//-----------------------------------------------------------------------------
void ComplainAboutBaudRateError(int divisor, double actual, double err)
{
Error(_("UART baud rate generator: divisor=%d actual=%.4f for %.2f%% "
"error.\r\n"
"\r\n"
"This is too large; try a different baud rate (slower "
"probably), or a crystal frequency chosen to be divisible "
"by many common baud rates (e.g. 3.6864 MHz, 14.7456 MHz).\r\n"
"\r\n"
"Code will be generated anyways but serial may be "
"unreliable or completely broken."), divisor, actual, err);
}
//-----------------------------------------------------------------------------
// Display our boilerplate warning that the baud rate is too slow (making
// for an overflowed divisor).
//-----------------------------------------------------------------------------
void ComplainAboutBaudRateOverflow(void)
{
Error(_("UART baud rate generator: too slow, divisor overflows. "
"Use a slower crystal or a faster baud rate.\r\n"
"\r\n"
"Code will be generated anyways but serial will likely be "
"completely broken."));
}