diff options
Diffstat (limited to 'ldmicro/circuit.cpp')
-rw-r--r-- | ldmicro/circuit.cpp | 950 |
1 files changed, 950 insertions, 0 deletions
diff --git a/ldmicro/circuit.cpp b/ldmicro/circuit.cpp new file mode 100644 index 0000000..1d9d3d7 --- /dev/null +++ b/ldmicro/circuit.cpp @@ -0,0 +1,950 @@ +//----------------------------------------------------------------------------- +// 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/>. +//------ +// +// Routines for modifying the circuit: add a particular element at a +// particular point, delete the selected element, etc. +// Jonathan Westhues, Oct 2004 +//----------------------------------------------------------------------------- +#include <linuxUI.h> +#include <stdio.h> +#include <stdlib.h> + +#include "ldmicro.h" + + +static ElemSubcktSeries *LoadSeriesFromFile(FILE *f); + +//----------------------------------------------------------------------------- +// Convenience routines for allocating frequently-used data structures. +//----------------------------------------------------------------------------- +ElemLeaf *AllocLeaf(void) +{ + return (ElemLeaf *)CheckMalloc(sizeof(ElemLeaf)); +} +ElemSubcktSeries *AllocSubcktSeries(void) +{ + return (ElemSubcktSeries *)CheckMalloc(sizeof(ElemSubcktSeries)); +} +ElemSubcktParallel *AllocSubcktParallel(void) +{ + return (ElemSubcktParallel *)CheckMalloc(sizeof(ElemSubcktParallel)); +} + +//----------------------------------------------------------------------------- +// Routine that does the actual work of adding a leaf element to the left/ +// right of or above/below the selected element. If we are adding left/right +// in a series circuit then it's easy; just increase length of that +// subcircuit and stick it in. Same goes for above/below in a parallel +// subcircuit. If we are adding above/below in a series circuit or left/right +// in a parallel circuit then we must create a new parallel (for series) or +// series (for parallel) subcircuit with 2 elements, one for the previously +// selected element and one for the new element. Calls itself recursively on +// all subcircuits. Returns TRUE if it or a child made the addition. +//----------------------------------------------------------------------------- +static BOOL AddLeafWorker(int which, void *any, int newWhich, ElemLeaf *newElem) +{ + int i; + + switch(which) { + case ELEM_SERIES_SUBCKT: { + ElemSubcktSeries *s = (ElemSubcktSeries *)any; + for(i = 0; i < s->count; i++) { + if(s->contents[i].d.any == Selected) { + break; + } + if(s->contents[i].which == ELEM_PARALLEL_SUBCKT) { + if(AddLeafWorker(ELEM_PARALLEL_SUBCKT, s->contents[i].d.any, + newWhich, newElem)) + { + return TRUE; + } + } + } + if(i == s->count) break; + if(s->contents[i].which == ELEM_PLACEHOLDER) { + // Special case--placeholders are replaced. They only appear + // in the empty series subcircuit that I generate for them, + // so there is no need to consider them anywhere but here. + // If we copy instead of replacing then the DisplayMatrix + // tables don't get all messed up. + memcpy(s->contents[i].d.leaf, newElem, sizeof(ElemLeaf)); + s->contents[i].d.leaf->selectedState = SELECTED_LEFT; + CheckFree(newElem); + s->contents[i].which = newWhich; + SelectedWhich = newWhich; + return TRUE; + } + if(s->count >= (MAX_ELEMENTS_IN_SUBCKT-1)) { + Error(_("Too many elements in subcircuit!")); + return TRUE; + } + switch(Selected->selectedState) { + case SELECTED_LEFT: + memmove(&s->contents[i+1], &s->contents[i], + (s->count - i)*sizeof(s->contents[0])); + s->contents[i].d.leaf = newElem; + s->contents[i].which = newWhich; + (s->count)++; + break; + + case SELECTED_RIGHT: + memmove(&s->contents[i+2], &s->contents[i+1], + (s->count - i - 1)*sizeof(s->contents[0])); + s->contents[i+1].d.leaf = newElem; + s->contents[i+1].which = newWhich; + (s->count)++; + break; + + case SELECTED_BELOW: + case SELECTED_ABOVE: { + ElemSubcktParallel *p = AllocSubcktParallel(); + p->count = 2; + + int t; + t = (Selected->selectedState == SELECTED_ABOVE) ? 0 : 1; + p->contents[t].which = newWhich; + p->contents[t].d.leaf = newElem; + t = (Selected->selectedState == SELECTED_ABOVE) ? 1 : 0; + p->contents[t].which = s->contents[i].which; + p->contents[t].d.any = s->contents[i].d.any; + + s->contents[i].which = ELEM_PARALLEL_SUBCKT; + s->contents[i].d.parallel = p; + break; + } + default: + oops(); + break; + } + return TRUE; + break; + } + case ELEM_PARALLEL_SUBCKT: { + ElemSubcktParallel *p = (ElemSubcktParallel *)any; + for(i = 0; i < p->count; i++) { + if(p->contents[i].d.any == Selected) { + break; + } + if(p->contents[i].which == ELEM_SERIES_SUBCKT) { + if(AddLeafWorker(ELEM_SERIES_SUBCKT, p->contents[i].d.any, + newWhich, newElem)) + { + return TRUE; + } + } + } + if(i == p->count) break; + if(p->count >= (MAX_ELEMENTS_IN_SUBCKT-1)) { + Error(_("Too many elements in subcircuit!")); + return TRUE; + } + switch(Selected->selectedState) { + case SELECTED_ABOVE: + memmove(&p->contents[i+1], &p->contents[i], + (p->count - i)*sizeof(p->contents[0])); + p->contents[i].d.leaf = newElem; + p->contents[i].which = newWhich; + (p->count)++; + break; + + case SELECTED_BELOW: + memmove(&p->contents[i+2], &p->contents[i+1], + (p->count - i - 1)*sizeof(p->contents[0])); + p->contents[i+1].d.leaf = newElem; + p->contents[i+1].which = newWhich; + (p->count)++; + break; + + case SELECTED_LEFT: + case SELECTED_RIGHT: { + ElemSubcktSeries *s = AllocSubcktSeries(); + s->count = 2; + + int t; + t = (Selected->selectedState == SELECTED_LEFT) ? 0 : 1; + s->contents[t].which = newWhich; + s->contents[t].d.leaf = newElem; + t = (Selected->selectedState == SELECTED_LEFT) ? 1 : 0; + s->contents[t].which = p->contents[i].which; + s->contents[t].d.any = p->contents[i].d.any; + + p->contents[i].which = ELEM_SERIES_SUBCKT; + p->contents[i].d.series = s; + break; + } + default: + oops(); + break; + } + return TRUE; + break; + } + } + + return FALSE; +} + +//----------------------------------------------------------------------------- +// Add the specified leaf node in the position indicated by the cursor. We +// will search through the entire program using AddLeafWorker to find the +// insertion point, and AddLeafWorker will stick it in at the requested +// location and return TRUE. We return TRUE if it worked, else FALSE. +//----------------------------------------------------------------------------- +static BOOL AddLeaf(int newWhich, ElemLeaf *newElem) +{ + if(!Selected || Selected->selectedState == SELECTED_NONE) return FALSE; + + int i; + for(i = 0; i < Prog.numRungs; i++) { + if(AddLeafWorker(ELEM_SERIES_SUBCKT, Prog.rungs[i], newWhich, newElem)) + { + WhatCanWeDoFromCursorAndTopology(); + return TRUE; + } + } + return FALSE; +} + +//----------------------------------------------------------------------------- +// Routines to allocate memory for a new circuit element (contact, coil, etc.) +// and insert it into the current program with AddLeaf. Fill in some default +// parameters, name etc. when we create the leaf; user can change them later. +//----------------------------------------------------------------------------- +void AddComment(char *str) +{ + if(!CanInsertComment) return; + + ElemLeaf *c = AllocLeaf(); + strcpy(c->d.comment.str, str); + + AddLeaf(ELEM_COMMENT, c); +} +void AddContact(void) +{ + if(!CanInsertOther) return; + + ElemLeaf *c = AllocLeaf(); + strcpy(c->d.contacts.name, "Xnew"); + c->d.contacts.negated = FALSE; + + AddLeaf(ELEM_CONTACTS, c); +} +void AddCoil(void) +{ + if(!CanInsertEnd) return; + + ElemLeaf *c = AllocLeaf(); + strcpy(c->d.coil.name, "Ynew"); + c->d.coil.negated = FALSE; + c->d.coil.setOnly = FALSE; + c->d.coil.resetOnly = FALSE; + + AddLeaf(ELEM_COIL, c); +} +void AddTimer(int which) +{ + if(!CanInsertOther) return; + + ElemLeaf *t = AllocLeaf(); + strcpy(t->d.timer.name, "Tnew"); + t->d.timer.delay = 100000; + + AddLeaf(which, t); +} +void AddEmpty(int which) +{ + if(!CanInsertOther) return; + + ElemLeaf *t = AllocLeaf(); + AddLeaf(which, t); +} +void AddReset(void) +{ + if(!CanInsertEnd) return; + + ElemLeaf *t = AllocLeaf(); + strcpy(t->d.reset.name, "Tnew"); + AddLeaf(ELEM_RES, t); +} +void AddMasterRelay(void) +{ + if(!CanInsertEnd) return; + + ElemLeaf *t = AllocLeaf(); + AddLeaf(ELEM_MASTER_RELAY, t); +} +void AddShiftRegister(void) +{ + if(!CanInsertEnd) return; + + ElemLeaf *t = AllocLeaf(); + strcpy(t->d.shiftRegister.name, "reg"); + t->d.shiftRegister.stages = 7; + AddLeaf(ELEM_SHIFT_REGISTER, t); +} +void AddFormattedString(void) +{ + if(!CanInsertOther) return; + + ElemLeaf *t = AllocLeaf(); + strcpy(t->d.fmtdStr.var, "var"); + strcpy(t->d.fmtdStr.string, "value: \\3\\r\\n"); + AddLeaf(ELEM_FORMATTED_STRING, t); +} +void AddLookUpTable(void) +{ + if(!CanInsertEnd) return; + + ElemLeaf *t = AllocLeaf(); + strcpy(t->d.lookUpTable.dest, "dest"); + strcpy(t->d.lookUpTable.index, "index"); + t->d.lookUpTable.count = 0; + t->d.lookUpTable.editAsString = 0; + AddLeaf(ELEM_LOOK_UP_TABLE, t); +} +void AddPiecewiseLinear(void) +{ + if(!CanInsertEnd) return; + + ElemLeaf *t = AllocLeaf(); + strcpy(t->d.piecewiseLinear.dest, "yvar"); + strcpy(t->d.piecewiseLinear.index, "xvar"); + t->d.piecewiseLinear.count = 0; + AddLeaf(ELEM_PIECEWISE_LINEAR, t); +} +void AddMove(void) +{ + if(!CanInsertEnd) return; + + ElemLeaf *t = AllocLeaf(); + strcpy(t->d.move.dest, "dest"); + strcpy(t->d.move.src, "src"); + AddLeaf(ELEM_MOVE, t); +} +void AddMath(int which) +{ + if(!CanInsertEnd) return; + + ElemLeaf *t = AllocLeaf(); + strcpy(t->d.math.dest, "dest"); + strcpy(t->d.math.op1, "src"); + strcpy(t->d.math.op2, "1"); + AddLeaf(which, t); +} +void AddCmp(int which) +{ + if(!CanInsertOther) return; + + ElemLeaf *t = AllocLeaf(); + strcpy(t->d.cmp.op1, "var"); + strcpy(t->d.cmp.op2, "1"); + AddLeaf(which, t); +} +void AddCounter(int which) +{ + if(which == ELEM_CTC) { + if(!CanInsertEnd) return; + } else { + if(!CanInsertOther) return; + } + + ElemLeaf *t = AllocLeaf(); + strcpy(t->d.counter.name, "Cnew"); + t->d.counter.max = 0; + AddLeaf(which, t); +} +void AddReadAdc(void) +{ + if(!CanInsertEnd) return; + + ElemLeaf *t = AllocLeaf(); + strcpy(t->d.readAdc.name, "Anew"); + AddLeaf(ELEM_READ_ADC, t); +} +void AddSetPwm(void) +{ + if(!CanInsertEnd) return; + + ElemLeaf *t = AllocLeaf(); + strcpy(t->d.setPwm.name, "duty_cycle"); + t->d.setPwm.targetFreq = 1000; + AddLeaf(ELEM_SET_PWM, t); +} +void AddUart(int which) +{ + if(!CanInsertOther) return; + + ElemLeaf *t = AllocLeaf(); + strcpy(t->d.uart.name, "char"); + AddLeaf(which, t); +} +void AddPersist(void) +{ + if(!CanInsertEnd) return; + + ElemLeaf *t = AllocLeaf(); + strcpy(t->d.persist.var, "saved"); + AddLeaf(ELEM_PERSIST, t); +} + +//----------------------------------------------------------------------------- +// Any subcircuit containing only one element should be collapsed into its +// parent. Call ourselves recursively to do this on every child of the passed +// subcircuit. The passed subcircuit will not be collapsed itself, so that +// the rung subcircuit may contain only one element as a special case. Returns +// TRUE if it or a child made any changes, and for completeness must be +// called iteratively on the root till it doesn't do anything. +//----------------------------------------------------------------------------- +static BOOL CollapseUnnecessarySubckts(int which, void *any) +{ + BOOL modified = FALSE; + + ok(); + switch(which) { + case ELEM_SERIES_SUBCKT: { + ElemSubcktSeries *s = (ElemSubcktSeries *)any; + int i; + for(i = 0; i < s->count; i++) { + if(s->contents[i].which == ELEM_PARALLEL_SUBCKT) { + ElemSubcktParallel *p = s->contents[i].d.parallel; + if(p->count == 1) { + if(p->contents[0].which == ELEM_SERIES_SUBCKT) { + // merge the two series subcircuits + ElemSubcktSeries *s2 = p->contents[0].d.series; + int makeSpaces = s2->count - 1; + memmove(&s->contents[i+makeSpaces+1], + &s->contents[i+1], + (s->count - i - 1)*sizeof(s->contents[0])); + memcpy(&s->contents[i], &s2->contents[0], + (s2->count)*sizeof(s->contents[0])); + s->count += makeSpaces; + CheckFree(s2); + } else { + s->contents[i].which = p->contents[0].which; + s->contents[i].d.any = p->contents[0].d.any; + } + CheckFree(p); + modified = TRUE; + } else { + if(CollapseUnnecessarySubckts(ELEM_PARALLEL_SUBCKT, + s->contents[i].d.parallel)) + { + modified = TRUE; + } + } + } + // else a leaf, not a problem + } + break; + } + case ELEM_PARALLEL_SUBCKT: { + ElemSubcktParallel *p = (ElemSubcktParallel *)any; + int i; + for(i = 0; i < p->count; i++) { + if(p->contents[i].which == ELEM_SERIES_SUBCKT) { + ElemSubcktSeries *s = p->contents[i].d.series; + if(s->count == 1) { + if(s->contents[0].which == ELEM_PARALLEL_SUBCKT) { + // merge the two parallel subcircuits + ElemSubcktParallel *p2 = s->contents[0].d.parallel; + int makeSpaces = p2->count - 1; + memmove(&p->contents[i+makeSpaces+1], + &p->contents[i+1], + (p->count - i - 1)*sizeof(p->contents[0])); + memcpy(&p->contents[i], &p2->contents[0], + (p2->count)*sizeof(p->contents[0])); + p->count += makeSpaces; + CheckFree(p2); + } else { + p->contents[i].which = s->contents[0].which; + p->contents[i].d.any = s->contents[0].d.any; + } + CheckFree(s); + modified = TRUE; + } else { + if(CollapseUnnecessarySubckts(ELEM_SERIES_SUBCKT, + p->contents[i].d.series)) + { + modified = TRUE; + } + } + } + // else a leaf, not a problem + } + break; + } + + default: + oops(); + break; + } + ok(); + return modified; +} + +//----------------------------------------------------------------------------- +// Delete the selected leaf element from the circuit. Just pull it out of the +// subcircuit that it was in before, and don't worry about creating +// subcircuits with only one element; we will clean that up later in a second +// pass. Returns TRUE if it or a child (calls self recursively) found and +// removed the element. +//----------------------------------------------------------------------------- +static BOOL DeleteSelectedFromSubckt(int which, void *any) +{ + ok(); + switch(which) { + case ELEM_SERIES_SUBCKT: { + ElemSubcktSeries *s = (ElemSubcktSeries *)any; + int i; + for(i = 0; i < s->count; i++) { + if(s->contents[i].d.any == Selected) { + ForgetFromGrid(s->contents[i].d.any); + CheckFree(s->contents[i].d.any); + memmove(&s->contents[i], &s->contents[i+1], + (s->count - i - 1)*sizeof(s->contents[0])); + (s->count)--; + return TRUE; + } + if(s->contents[i].which == ELEM_PARALLEL_SUBCKT) { + if(DeleteSelectedFromSubckt(ELEM_PARALLEL_SUBCKT, + s->contents[i].d.any)) + { + return TRUE; + } + } + } + break; + } + case ELEM_PARALLEL_SUBCKT: { + ElemSubcktParallel *p = (ElemSubcktParallel *)any; + int i; + for(i = 0; i < p->count; i++) { + if(p->contents[i].d.any == Selected) { + ForgetFromGrid(p->contents[i].d.any); + CheckFree(p->contents[i].d.any); + memmove(&p->contents[i], &p->contents[i+1], + (p->count - i - 1)*sizeof(p->contents[0])); + (p->count)--; + return TRUE; + } + if(p->contents[i].which == ELEM_SERIES_SUBCKT) { + if(DeleteSelectedFromSubckt(ELEM_SERIES_SUBCKT, + p->contents[i].d.any)) + { + return TRUE; + } + } + } + break; + } + default: + oops(); + break; + } + return FALSE; +} + +//----------------------------------------------------------------------------- +// Delete the selected item from the program. Just call +// DeleteSelectedFromSubckt on every rung till we find it. +//----------------------------------------------------------------------------- +void DeleteSelectedFromProgram(void) +{ + if(!Selected || Selected->selectedState == SELECTED_NONE) return; + int i = RungContainingSelected(); + if(i < 0) return; + + if(Prog.rungs[i]->count == 1 && + Prog.rungs[i]->contents[0].which != ELEM_PARALLEL_SUBCKT) + { + Prog.rungs[i]->contents[0].which = ELEM_PLACEHOLDER; + SelectedWhich = ELEM_PLACEHOLDER; + Selected->selectedState = SELECTED_LEFT; + WhatCanWeDoFromCursorAndTopology(); + return; + } + + int gx, gy; + if(!FindSelected(&gx, &gy)) { + gx = 0; + gy = 0; + } + + if(DeleteSelectedFromSubckt(ELEM_SERIES_SUBCKT, Prog.rungs[i])) { + while(CollapseUnnecessarySubckts(ELEM_SERIES_SUBCKT, Prog.rungs[i])) + ; + WhatCanWeDoFromCursorAndTopology(); + MoveCursorNear(gx, gy); + return; + } +} + +//----------------------------------------------------------------------------- +// Free a circuit and all of its subcircuits. Calls self recursively to do +// so. +//----------------------------------------------------------------------------- +void FreeCircuit(int which, void *any) +{ + ok(); + switch(which) { + case ELEM_SERIES_SUBCKT: { + ElemSubcktSeries *s = (ElemSubcktSeries *)any; + int i; + for(i = 0; i < s->count; i++) { + FreeCircuit(s->contents[i].which, s->contents[i].d.any); + } + CheckFree(s); + break; + } + case ELEM_PARALLEL_SUBCKT: { + ElemSubcktParallel *p = (ElemSubcktParallel *)any; + int i; + for(i = 0; i < p->count; i++) { + FreeCircuit(p->contents[i].which, p->contents[i].d.any); + } + CheckFree(p); + break; + } + CASE_LEAF + ForgetFromGrid(any); + CheckFree(any); + break; + + default: + oops(); + break; + } + ok(); +} + +//----------------------------------------------------------------------------- +// Free the entire program. +//----------------------------------------------------------------------------- +void FreeEntireProgram(void) +{ + ForgetEverything(); + + int i; + for(i = 0; i < Prog.numRungs; i++) { + FreeCircuit(ELEM_SERIES_SUBCKT, Prog.rungs[i]); + } + Prog.numRungs = 0; + Prog.cycleTime = 10000; + Prog.mcuClock = 4000000; + Prog.baudRate = 2400; + Prog.io.count = 0; + Prog.mcu = NULL; +} + +//----------------------------------------------------------------------------- +// Returns true if the given subcircuit contains the given leaf. +//----------------------------------------------------------------------------- +static BOOL ContainsElem(int which, void *any, ElemLeaf *seek) +{ + switch(which) { + case ELEM_SERIES_SUBCKT: { + ElemSubcktSeries *s = (ElemSubcktSeries *)any; + int i; + for(i = 0; i < s->count; i++) { + if(ContainsElem(s->contents[i].which, s->contents[i].d.any, + seek)) + { + return TRUE; + } + } + break; + } + case ELEM_PARALLEL_SUBCKT: { + ElemSubcktParallel *p = (ElemSubcktParallel *)any; + int i; + for(i = 0; i < p->count; i++) { + if(ContainsElem(p->contents[i].which, p->contents[i].d.any, + seek)) + { + return TRUE; + } + } + break; + } + CASE_LEAF + if(any == seek) + return TRUE; + break; + + default: + oops(); + } + return FALSE; +} + +//----------------------------------------------------------------------------- +// Use ContainsElem to find the rung containing the cursor. +//----------------------------------------------------------------------------- +int RungContainingSelected(void) +{ + int i; + for(i = 0; i < Prog.numRungs; i++) { + if(ContainsElem(ELEM_SERIES_SUBCKT, Prog.rungs[i], Selected)) { + return i; + } + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Delete the rung that contains the cursor. +//----------------------------------------------------------------------------- +void DeleteSelectedRung(void) +{ + if(Prog.numRungs == 1) { + Error(_("Cannot delete rung; program must have at least one rung.")); + return; + } + + int gx, gy; + BOOL foundCursor; + foundCursor = FindSelected(&gx, &gy); + + int i = RungContainingSelected(); + if(i < 0) return; + + FreeCircuit(ELEM_SERIES_SUBCKT, Prog.rungs[i]); + Prog.numRungs--; + memmove(&Prog.rungs[i], &Prog.rungs[i+1], + (Prog.numRungs - i)*sizeof(Prog.rungs[0])); + + if(foundCursor) MoveCursorNear(gx, gy); + + WhatCanWeDoFromCursorAndTopology(); +} + +//----------------------------------------------------------------------------- +// Allocate a new `empty' rung, with only a single relay coil at the end. All +// the UI code assumes that rungs always have a coil in them, so it would +// add a lot of nasty special cases to create rungs totally empty. +//----------------------------------------------------------------------------- +static ElemSubcktSeries *AllocEmptyRung(void) +{ + ElemSubcktSeries *s = AllocSubcktSeries(); + s->count = 1; + s->contents[0].which = ELEM_PLACEHOLDER; + ElemLeaf *l = AllocLeaf(); + s->contents[0].d.leaf = l; + + return s; +} + +//----------------------------------------------------------------------------- +// Insert a rung either before or after the rung that contains the cursor. +//----------------------------------------------------------------------------- +void InsertRung(BOOL afterCursor) +{ + if(Prog.numRungs >= (MAX_RUNGS - 1)) { + Error(_("Too many rungs!")); + return; + } + + int i = RungContainingSelected(); + if(i < 0) return; + + if(afterCursor) i++; + memmove(&Prog.rungs[i+1], &Prog.rungs[i], + (Prog.numRungs - i)*sizeof(Prog.rungs[0])); + Prog.rungs[i] = AllocEmptyRung(); + (Prog.numRungs)++; + + WhatCanWeDoFromCursorAndTopology(); +} + +//----------------------------------------------------------------------------- +// Swap the row containing the selected element with the one under it, or do +// nothing if the rung is the last in the program. +//----------------------------------------------------------------------------- +void PushRungDown(void) +{ + int i = RungContainingSelected(); + if(i == (Prog.numRungs-1)) return; + + ElemSubcktSeries *temp = Prog.rungs[i]; + Prog.rungs[i] = Prog.rungs[i+1]; + Prog.rungs[i+1] = temp; + + WhatCanWeDoFromCursorAndTopology(); + ScrollSelectedIntoViewAfterNextPaint = TRUE; +} + +//----------------------------------------------------------------------------- +// Swap the row containing the selected element with the one above it, or do +// nothing if the rung is the last in the program. +//----------------------------------------------------------------------------- +void PushRungUp(void) +{ + int i = RungContainingSelected(); + if(i == 0) return; + + ElemSubcktSeries *temp = Prog.rungs[i]; + Prog.rungs[i] = Prog.rungs[i-1]; + Prog.rungs[i-1] = temp; + + WhatCanWeDoFromCursorAndTopology(); + ScrollSelectedIntoViewAfterNextPaint = TRUE; +} + +//----------------------------------------------------------------------------- +// Start a new project. Give them one rung, with a coil (that they can +// never delete) and nothing else. +//----------------------------------------------------------------------------- +void NewProgram(void) +{ + UndoFlush(); + FreeEntireProgram(); + + Prog.numRungs = 1; + Prog.rungs[0] = AllocEmptyRung(); +} + +//----------------------------------------------------------------------------- +// Worker for ItemIsLastInCircuit. Basically we look for seek in the given +// circuit, trying to see whether it occupies the last position in a series +// subcircuit (which may be in a parallel subcircuit that is in the last +// position of a series subcircuit that may be in a parallel subcircuit that +// etc.) +//----------------------------------------------------------------------------- +static void LastInCircuit(int which, void *any, ElemLeaf *seek, + BOOL *found, BOOL *andItemAfter) +{ + switch(which) { + case ELEM_PARALLEL_SUBCKT: { + ElemSubcktParallel *p = (ElemSubcktParallel *)any; + int i; + for(i = 0; i < p->count; i++) { + LastInCircuit(p->contents[i].which, p->contents[i].d.any, seek, + found, andItemAfter); + if(*found) return; + } + break; + } + case ELEM_SERIES_SUBCKT: { + ElemSubcktSeries *s = (ElemSubcktSeries *)any; + LastInCircuit(s->contents[s->count-1].which, + s->contents[s->count-1].d.any, seek, found, andItemAfter); + break; + } + default: + if(*found) *andItemAfter = TRUE; + if(any == seek) *found = TRUE; + break; + } +} + +//----------------------------------------------------------------------------- +// Is an item the last one in the circuit (i.e. does one of its terminals go +// to the rightmost bus bar)? We need to know this because that is the only +// circumstance in which it is okay to insert a coil, RES, etc. after +// something +//----------------------------------------------------------------------------- +BOOL ItemIsLastInCircuit(ElemLeaf *item) +{ + int i = RungContainingSelected(); + if(i < 0) return FALSE; + + BOOL found = FALSE; + BOOL andItemAfter = FALSE; + + LastInCircuit(ELEM_SERIES_SUBCKT, Prog.rungs[i], item, &found, + &andItemAfter); + + if(found) return !andItemAfter; + return FALSE; +} + +//----------------------------------------------------------------------------- +// Returns TRUE if the subcircuit contains any of the given instruction +// types (ELEM_....), else FALSE. +//----------------------------------------------------------------------------- +static BOOL ContainsWhich(int which, void *any, int seek1, int seek2, int seek3) +{ + switch(which) { + case ELEM_PARALLEL_SUBCKT: { + ElemSubcktParallel *p = (ElemSubcktParallel *)any; + int i; + for(i = 0; i < p->count; i++) { + if(ContainsWhich(p->contents[i].which, p->contents[i].d.any, + seek1, seek2, seek3)) + { + return TRUE; + } + } + break; + } + case ELEM_SERIES_SUBCKT: { + ElemSubcktSeries *s = (ElemSubcktSeries *)any; + int i; + for(i = 0; i < s->count; i++) { + if(ContainsWhich(s->contents[i].which, s->contents[i].d.any, + seek1, seek2, seek3)) + { + return TRUE; + } + } + break; + } + default: + if(which == seek1 || which == seek2 || which == seek3) { + return TRUE; + } + break; + } + return FALSE; +} + +//----------------------------------------------------------------------------- +// Are either of the UART functions (send or recv) used? Need to know this +// to know whether we must receive their pins. +//----------------------------------------------------------------------------- +BOOL UartFunctionUsed(void) +{ + int i; + for(i = 0; i < Prog.numRungs; i++) { + if(ContainsWhich(ELEM_SERIES_SUBCKT, Prog.rungs[i], + ELEM_UART_RECV, ELEM_UART_SEND, ELEM_FORMATTED_STRING)) + { + return TRUE; + } + } + return FALSE; +} + +//----------------------------------------------------------------------------- +// Is the PWM function used? Need to know this to know whether we must reserve +// the pin. +//----------------------------------------------------------------------------- +BOOL PwmFunctionUsed(void) +{ + int i; + for(i = 0; i < Prog.numRungs; i++) { + if(ContainsWhich(ELEM_SERIES_SUBCKT, Prog.rungs[i], ELEM_SET_PWM, + -1, -1)) + { + return TRUE; + } + } + return FALSE; +} |