diff options
Diffstat (limited to 'ldmicro/simulate.cpp')
-rw-r--r-- | ldmicro/simulate.cpp | 1420 |
1 files changed, 718 insertions, 702 deletions
diff --git a/ldmicro/simulate.cpp b/ldmicro/simulate.cpp index ecb9b0e..0b3c206 100644 --- a/ldmicro/simulate.cpp +++ b/ldmicro/simulate.cpp @@ -89,8 +89,8 @@ static int IntPc; // A window to allow simulation with the UART stuff (insert keystrokes into // the program, view the output, like a terminal window). -static HWND UartSimulationWindow; -static HWND UartSimulationTextControl; +static HWID UartSimulationWindow; +static HWID UartSimulationTextControl; static LONG_PTR PrevTextProc; static int QueuedUartCharacter = -1; @@ -106,117 +106,117 @@ static char *MarkUsedVariable(char *name, DWORD flag); // Looks in the SingleBitItems list; if an item is not present then it is // FALSE by default. //----------------------------------------------------------------------------- -// static BOOL SingleBitOn(char *name) -// { -// int i; -// for(i = 0; i < SingleBitItemsCount; i++) { -// if(strcmp(SingleBitItems[i].name, name)==0) { -// return SingleBitItems[i].powered; -// } -// } -// return FALSE; -// } +static BOOL SingleBitOn(char *name) +{ + int i; + for(i = 0; i < SingleBitItemsCount; i++) { + if(strcmp(SingleBitItems[i].name, name)==0) { + return SingleBitItems[i].powered; + } + } + return FALSE; +} //----------------------------------------------------------------------------- // Set the state of a single-bit item. Adds it to the list if it is not there // already. //----------------------------------------------------------------------------- -// static void SetSingleBit(char *name, BOOL state) -// { -// int i; -// for(i = 0; i < SingleBitItemsCount; i++) { -// if(strcmp(SingleBitItems[i].name, name)==0) { -// SingleBitItems[i].powered = state; -// return; -// } -// } -// if(i < MAX_IO) { -// strcpy(SingleBitItems[i].name, name); -// SingleBitItems[i].powered = state; -// SingleBitItemsCount++; -// } -// } +static void SetSingleBit(char *name, BOOL state) +{ + int i; + for(i = 0; i < SingleBitItemsCount; i++) { + if(strcmp(SingleBitItems[i].name, name)==0) { + SingleBitItems[i].powered = state; + return; + } + } + if(i < MAX_IO) { + strcpy(SingleBitItems[i].name, name); + SingleBitItems[i].powered = state; + SingleBitItemsCount++; + } +} //----------------------------------------------------------------------------- // Count a timer up (i.e. increment its associated count by 1). Must already // exist in the table. //----------------------------------------------------------------------------- -// static void IncrementVariable(char *name) -// { -// int i; -// for(i = 0; i < VariablesCount; i++) { -// if(strcmp(Variables[i].name, name)==0) { -// (Variables[i].val)++; -// return; -// } -// } -// oops(); -// } +static void IncrementVariable(char *name) +{ + int i; + for(i = 0; i < VariablesCount; i++) { + if(strcmp(Variables[i].name, name)==0) { + (Variables[i].val)++; + return; + } + } + oops(); +} //----------------------------------------------------------------------------- // Set a variable to a value. //----------------------------------------------------------------------------- -// static void SetSimulationVariable(char *name, SWORD val) -// { -// int i; -// for(i = 0; i < VariablesCount; i++) { -// if(strcmp(Variables[i].name, name)==0) { -// Variables[i].val = val; -// return; -// } -// } -// MarkUsedVariable(name, VAR_FLAG_OTHERWISE_FORGOTTEN); -// SetSimulationVariable(name, val); -// } +static void SetSimulationVariable(char *name, SWORD val) +{ + int i; + for(i = 0; i < VariablesCount; i++) { + if(strcmp(Variables[i].name, name)==0) { + Variables[i].val = val; + return; + } + } + MarkUsedVariable(name, VAR_FLAG_OTHERWISE_FORGOTTEN); + SetSimulationVariable(name, val); +} //----------------------------------------------------------------------------- // Read a variable's value. //----------------------------------------------------------------------------- -// SWORD GetSimulationVariable(char *name) -// { -// int i; -// for(i = 0; i < VariablesCount; i++) { -// if(strcmp(Variables[i].name, name)==0) { -// return Variables[i].val; -// } -// } -// MarkUsedVariable(name, VAR_FLAG_OTHERWISE_FORGOTTEN); -// return GetSimulationVariable(name); -// } +SWORD GetSimulationVariable(char *name) +{ + int i; + for(i = 0; i < VariablesCount; i++) { + if(strcmp(Variables[i].name, name)==0) { + return Variables[i].val; + } + } + MarkUsedVariable(name, VAR_FLAG_OTHERWISE_FORGOTTEN); + return GetSimulationVariable(name); +} //----------------------------------------------------------------------------- // Set the shadow copy of a variable associated with a READ ADC operation. This // will get committed to the real copy when the rung-in condition to the // READ ADC is true. //----------------------------------------------------------------------------- -// void SetAdcShadow(char *name, SWORD val) -// { -// int i; -// for(i = 0; i < AdcShadowsCount; i++) { -// if(strcmp(AdcShadows[i].name, name)==0) { -// AdcShadows[i].val = val; -// return; -// } -// } -// strcpy(AdcShadows[i].name, name); -// AdcShadows[i].val = val; -// AdcShadowsCount++; -// } +void SetAdcShadow(char *name, SWORD val) +{ + int i; + for(i = 0; i < AdcShadowsCount; i++) { + if(strcmp(AdcShadows[i].name, name)==0) { + AdcShadows[i].val = val; + return; + } + } + strcpy(AdcShadows[i].name, name); + AdcShadows[i].val = val; + AdcShadowsCount++; +} //----------------------------------------------------------------------------- // Return the shadow value of a variable associated with a READ ADC. This is // what gets copied into the real variable when an ADC read is simulated. //----------------------------------------------------------------------------- -// SWORD GetAdcShadow(char *name) -// { -// int i; -// for(i = 0; i < AdcShadowsCount; i++) { -// if(strcmp(AdcShadows[i].name, name)==0) { -// return AdcShadows[i].val; -// } -// } -// return 0; -// } +SWORD GetAdcShadow(char *name) +{ + int i; + for(i = 0; i < AdcShadowsCount; i++) { + if(strcmp(AdcShadows[i].name, name)==0) { + return AdcShadows[i].val; + } + } + return 0; +} //----------------------------------------------------------------------------- // Mark how a variable is used; a series of flags that we can OR together, @@ -224,62 +224,62 @@ static char *MarkUsedVariable(char *name, DWORD flag); // (e.g. just a TON, an RTO with its reset, etc.). Returns NULL for success, // else an error string. //----------------------------------------------------------------------------- -// static char *MarkUsedVariable(char *name, DWORD flag) -// { -// int i; -// for(i = 0; i < VariablesCount; i++) { -// if(strcmp(Variables[i].name, name)==0) { -// break; -// } -// } -// if(i >= MAX_IO) return ""; - -// if(i == VariablesCount) { -// strcpy(Variables[i].name, name); -// Variables[i].usedFlags = 0; -// Variables[i].val = 0; -// VariablesCount++; -// } - -// switch(flag) { -// case VAR_FLAG_TOF: -// if(Variables[i].usedFlags != 0) -// return _("TOF: variable cannot be used elsewhere"); -// break; - -// case VAR_FLAG_TON: -// if(Variables[i].usedFlags != 0) -// return _("TON: variable cannot be used elsewhere"); -// break; +static char *MarkUsedVariable(char *name, DWORD flag) +{ + int i; + for(i = 0; i < VariablesCount; i++) { + if(strcmp(Variables[i].name, name)==0) { + break; + } + } + if(i >= MAX_IO) return ""; + + if(i == VariablesCount) { + strcpy(Variables[i].name, name); + Variables[i].usedFlags = 0; + Variables[i].val = 0; + VariablesCount++; + } + + switch(flag) { + case VAR_FLAG_TOF: + if(Variables[i].usedFlags != 0) + return _("TOF: variable cannot be used elsewhere"); + break; + + case VAR_FLAG_TON: + if(Variables[i].usedFlags != 0) + return _("TON: variable cannot be used elsewhere"); + break; -// case VAR_FLAG_RTO: -// if(Variables[i].usedFlags & ~VAR_FLAG_RES) -// return _("RTO: variable can only be used for RES elsewhere"); -// break; - -// case VAR_FLAG_CTU: -// case VAR_FLAG_CTD: -// case VAR_FLAG_CTC: -// case VAR_FLAG_RES: -// case VAR_FLAG_ANY: -// break; - -// case VAR_FLAG_OTHERWISE_FORGOTTEN: -// if(name[0] != '$') { -// Error(_("Variable '%s' not assigned to, e.g. with a " -// "MOV statement, an ADD statement, etc.\r\n\r\n" -// "This is probably a programming error; now it " -// "will always be zero."), name); -// } -// break; - -// default: -// oops(); -// } - -// Variables[i].usedFlags |= flag; -// return NULL; -// } + case VAR_FLAG_RTO: + if(Variables[i].usedFlags & ~VAR_FLAG_RES) + return _("RTO: variable can only be used for RES elsewhere"); + break; + + case VAR_FLAG_CTU: + case VAR_FLAG_CTD: + case VAR_FLAG_CTC: + case VAR_FLAG_RES: + case VAR_FLAG_ANY: + break; + + case VAR_FLAG_OTHERWISE_FORGOTTEN: + if(name[0] != '$') { + Error(_("Variable '%s' not assigned to, e.g. with a " + "MOV statement, an ADD statement, etc.\r\n\r\n" + "This is probably a programming error; now it " + "will always be zero."), name); + } + break; + + default: + oops(); + } + + Variables[i].usedFlags |= flag; + return NULL; +} //----------------------------------------------------------------------------- // Check for duplicate uses of a single variable. For example, there should @@ -287,207 +287,207 @@ static char *MarkUsedVariable(char *name, DWORD flag); // to have an RTO with the same name as its reset; in fact, verify that // there must be a reset for each RTO. //----------------------------------------------------------------------------- -// static void MarkWithCheck(char *name, int flag) -// { -// char *s = MarkUsedVariable(name, flag); -// if(s) { -// Error(_("Variable for '%s' incorrectly assigned: %s."), name, s); -// } -// } - -// static void CheckVariableNamesCircuit(int which, void *elem) -// { -// ElemLeaf *l = (ElemLeaf *)elem; -// char *name = NULL; -// DWORD flag; - -// switch(which) { -// case ELEM_SERIES_SUBCKT: { -// int i; -// ElemSubcktSeries *s = (ElemSubcktSeries *)elem; -// for(i = 0; i < s->count; i++) { -// CheckVariableNamesCircuit(s->contents[i].which, -// s->contents[i].d.any); -// } -// break; -// } - -// case ELEM_PARALLEL_SUBCKT: { -// int i; -// ElemSubcktParallel *p = (ElemSubcktParallel *)elem; -// for(i = 0; i < p->count; i++) { -// CheckVariableNamesCircuit(p->contents[i].which, -// p->contents[i].d.any); -// } -// break; -// } +static void MarkWithCheck(char *name, int flag) +{ + char *s = MarkUsedVariable(name, flag); + if(s) { + Error(_("Variable for '%s' incorrectly assigned: %s."), name, s); + } +} + +static void CheckVariableNamesCircuit(int which, void *elem) +{ + ElemLeaf *l = (ElemLeaf *)elem; + char *name = NULL; + DWORD flag; + + switch(which) { + case ELEM_SERIES_SUBCKT: { + int i; + ElemSubcktSeries *s = (ElemSubcktSeries *)elem; + for(i = 0; i < s->count; i++) { + CheckVariableNamesCircuit(s->contents[i].which, + s->contents[i].d.any); + } + break; + } + + case ELEM_PARALLEL_SUBCKT: { + int i; + ElemSubcktParallel *p = (ElemSubcktParallel *)elem; + for(i = 0; i < p->count; i++) { + CheckVariableNamesCircuit(p->contents[i].which, + p->contents[i].d.any); + } + break; + } -// case ELEM_RTO: -// case ELEM_TOF: -// case ELEM_TON: -// if(which == ELEM_RTO) -// flag = VAR_FLAG_RTO; -// else if(which == ELEM_TOF) -// flag = VAR_FLAG_TOF; -// else if(which == ELEM_TON) -// flag = VAR_FLAG_TON; -// else oops(); - -// MarkWithCheck(l->d.timer.name, flag); - -// break; - -// case ELEM_CTU: -// case ELEM_CTD: -// case ELEM_CTC: -// if(which == ELEM_CTU) -// flag = VAR_FLAG_CTU; -// else if(which == ELEM_CTD) -// flag = VAR_FLAG_CTD; -// else if(which == ELEM_CTC) -// flag = VAR_FLAG_CTC; -// else oops(); - -// MarkWithCheck(l->d.counter.name, flag); - -// break; - -// case ELEM_RES: -// MarkWithCheck(l->d.reset.name, VAR_FLAG_RES); -// break; - -// case ELEM_MOVE: -// MarkWithCheck(l->d.move.dest, VAR_FLAG_ANY); -// break; - -// case ELEM_LOOK_UP_TABLE: -// MarkWithCheck(l->d.lookUpTable.dest, VAR_FLAG_ANY); -// break; - -// case ELEM_PIECEWISE_LINEAR: -// MarkWithCheck(l->d.piecewiseLinear.dest, VAR_FLAG_ANY); -// break; - -// case ELEM_READ_ADC: -// MarkWithCheck(l->d.readAdc.name, VAR_FLAG_ANY); -// break; - -// case ELEM_ADD: -// case ELEM_SUB: -// case ELEM_MUL: -// case ELEM_DIV: -// MarkWithCheck(l->d.math.dest, VAR_FLAG_ANY); -// break; - -// case ELEM_UART_RECV: -// MarkWithCheck(l->d.uart.name, VAR_FLAG_ANY); -// break; - -// case ELEM_SHIFT_REGISTER: { -// int i; -// for(i = 1; i < l->d.shiftRegister.stages; i++) { -// char str[MAX_NAME_LEN+10]; -// sprintf(str, "%s%d", l->d.shiftRegister.name, i); -// MarkWithCheck(str, VAR_FLAG_ANY); -// } -// break; -// } - -// case ELEM_PERSIST: -// case ELEM_FORMATTED_STRING: -// case ELEM_SET_PWM: -// case ELEM_MASTER_RELAY: -// case ELEM_UART_SEND: -// case ELEM_PLACEHOLDER: -// case ELEM_COMMENT: -// case ELEM_OPEN: -// case ELEM_SHORT: -// case ELEM_COIL: -// case ELEM_CONTACTS: -// case ELEM_ONE_SHOT_RISING: -// case ELEM_ONE_SHOT_FALLING: -// case ELEM_EQU: -// case ELEM_NEQ: -// case ELEM_GRT: -// case ELEM_GEQ: -// case ELEM_LES: -// case ELEM_LEQ: -// break; - -// default: -// oops(); -// } -// } - -// static void CheckVariableNames(void) -// { -// int i; -// for(i = 0; i < Prog.numRungs; i++) { -// CheckVariableNamesCircuit(ELEM_SERIES_SUBCKT, Prog.rungs[i]); -// } -// } + case ELEM_RTO: + case ELEM_TOF: + case ELEM_TON: + if(which == ELEM_RTO) + flag = VAR_FLAG_RTO; + else if(which == ELEM_TOF) + flag = VAR_FLAG_TOF; + else if(which == ELEM_TON) + flag = VAR_FLAG_TON; + else oops(); + + MarkWithCheck(l->d.timer.name, flag); + + break; + + case ELEM_CTU: + case ELEM_CTD: + case ELEM_CTC: + if(which == ELEM_CTU) + flag = VAR_FLAG_CTU; + else if(which == ELEM_CTD) + flag = VAR_FLAG_CTD; + else if(which == ELEM_CTC) + flag = VAR_FLAG_CTC; + else oops(); + + MarkWithCheck(l->d.counter.name, flag); + + break; + + case ELEM_RES: + MarkWithCheck(l->d.reset.name, VAR_FLAG_RES); + break; + + case ELEM_MOVE: + MarkWithCheck(l->d.move.dest, VAR_FLAG_ANY); + break; + + case ELEM_LOOK_UP_TABLE: + MarkWithCheck(l->d.lookUpTable.dest, VAR_FLAG_ANY); + break; + + case ELEM_PIECEWISE_LINEAR: + MarkWithCheck(l->d.piecewiseLinear.dest, VAR_FLAG_ANY); + break; + + case ELEM_READ_ADC: + MarkWithCheck(l->d.readAdc.name, VAR_FLAG_ANY); + break; + + case ELEM_ADD: + case ELEM_SUB: + case ELEM_MUL: + case ELEM_DIV: + MarkWithCheck(l->d.math.dest, VAR_FLAG_ANY); + break; + + case ELEM_UART_RECV: + MarkWithCheck(l->d.uart.name, VAR_FLAG_ANY); + break; + + case ELEM_SHIFT_REGISTER: { + int i; + for(i = 1; i < l->d.shiftRegister.stages; i++) { + char str[MAX_NAME_LEN+10]; + sprintf(str, "%s%d", l->d.shiftRegister.name, i); + MarkWithCheck(str, VAR_FLAG_ANY); + } + break; + } + + case ELEM_PERSIST: + case ELEM_FORMATTED_STRING: + case ELEM_SET_PWM: + case ELEM_MASTER_RELAY: + case ELEM_UART_SEND: + case ELEM_PLACEHOLDER: + case ELEM_COMMENT: + case ELEM_OPEN: + case ELEM_SHORT: + case ELEM_COIL: + case ELEM_CONTACTS: + case ELEM_ONE_SHOT_RISING: + case ELEM_ONE_SHOT_FALLING: + case ELEM_EQU: + case ELEM_NEQ: + case ELEM_GRT: + case ELEM_GEQ: + case ELEM_LES: + case ELEM_LEQ: + break; + + default: + oops(); + } +} + +static void CheckVariableNames(void) +{ + int i; + for(i = 0; i < Prog.numRungs; i++) { + CheckVariableNamesCircuit(ELEM_SERIES_SUBCKT, Prog.rungs[i]); + } +} //----------------------------------------------------------------------------- // The IF condition is true. Execute the body, up until the ELSE or the // END IF, and then skip the ELSE if it is present. Called with PC on the // IF, returns with PC on the END IF. //----------------------------------------------------------------------------- -// static void IfConditionTrue(void) -// { -// IntPc++; -// // now PC is on the first statement of the IF body -// SimulateIntCode(); -// // now PC is on the ELSE or the END IF -// if(IntCode[IntPc].op == INT_ELSE) { -// int nesting = 1; -// for(; ; IntPc++) { -// if(IntPc >= IntCodeLen) oops(); - -// if(IntCode[IntPc].op == INT_END_IF) { -// nesting--; -// } else if(INT_IF_GROUP(IntCode[IntPc].op)) { -// nesting++; -// } -// if(nesting == 0) break; -// } -// } else if(IntCode[IntPc].op == INT_END_IF) { -// return; -// } else { -// oops(); -// } -// } +static void IfConditionTrue(void) +{ + IntPc++; + // now PC is on the first statement of the IF body + SimulateIntCode(); + // now PC is on the ELSE or the END IF + if(IntCode[IntPc].op == INT_ELSE) { + int nesting = 1; + for(; ; IntPc++) { + if(IntPc >= IntCodeLen) oops(); + + if(IntCode[IntPc].op == INT_END_IF) { + nesting--; + } else if(INT_IF_GROUP(IntCode[IntPc].op)) { + nesting++; + } + if(nesting == 0) break; + } + } else if(IntCode[IntPc].op == INT_END_IF) { + return; + } else { + oops(); + } +} //----------------------------------------------------------------------------- // The IF condition is false. Skip the body, up until the ELSE or the END // IF, and then execute the ELSE if it is present. Called with PC on the IF, // returns with PC on the END IF. //----------------------------------------------------------------------------- -// static void IfConditionFalse(void) -// { -// int nesting = 0; -// for(; ; IntPc++) { -// if(IntPc >= IntCodeLen) oops(); - -// if(IntCode[IntPc].op == INT_END_IF) { -// nesting--; -// } else if(INT_IF_GROUP(IntCode[IntPc].op)) { -// nesting++; -// } else if(IntCode[IntPc].op == INT_ELSE && nesting == 1) { -// break; -// } -// if(nesting == 0) break; -// } - -// // now PC is on the ELSE or the END IF -// if(IntCode[IntPc].op == INT_ELSE) { -// IntPc++; -// SimulateIntCode(); -// } else if(IntCode[IntPc].op == INT_END_IF) { -// return; -// } else { -// oops(); -// } -// } +static void IfConditionFalse(void) +{ + int nesting = 0; + for(; ; IntPc++) { + if(IntPc >= IntCodeLen) oops(); + + if(IntCode[IntPc].op == INT_END_IF) { + nesting--; + } else if(INT_IF_GROUP(IntCode[IntPc].op)) { + nesting++; + } else if(IntCode[IntPc].op == INT_ELSE && nesting == 1) { + break; + } + if(nesting == 0) break; + } + + // now PC is on the ELSE or the END IF + if(IntCode[IntPc].op == INT_ELSE) { + IntPc++; + SimulateIntCode(); + } else if(IntCode[IntPc].op == INT_END_IF) { + return; + } else { + oops(); + } +} //----------------------------------------------------------------------------- // Evaluate a circuit, calling ourselves recursively to evaluate if/else @@ -495,229 +495,231 @@ static char *MarkUsedVariable(char *name, DWORD flag); // internal tables. Returns when it reaches an end if or an else construct, // or at the end of the program. //----------------------------------------------------------------------------- -// static void SimulateIntCode(void) -// { -// for(; IntPc < IntCodeLen; IntPc++) { -// IntOp *a = &IntCode[IntPc]; -// switch(a->op) { -// case INT_SIMULATE_NODE_STATE: -// if(*(a->poweredAfter) != SingleBitOn(a->name1)) -// NeedRedraw = TRUE; -// *(a->poweredAfter) = SingleBitOn(a->name1); -// break; - -// case INT_SET_BIT: -// SetSingleBit(a->name1, TRUE); -// break; - -// case INT_CLEAR_BIT: -// SetSingleBit(a->name1, FALSE); -// break; - -// case INT_COPY_BIT_TO_BIT: -// SetSingleBit(a->name1, SingleBitOn(a->name2)); -// break; - -// case INT_SET_VARIABLE_TO_LITERAL: -// if(GetSimulationVariable(a->name1) != -// a->literal && a->name1[0] != '$') -// { -// NeedRedraw = TRUE; -// } -// SetSimulationVariable(a->name1, a->literal); -// break; - -// case INT_SET_VARIABLE_TO_VARIABLE: -// if(GetSimulationVariable(a->name1) != -// GetSimulationVariable(a->name2)) -// { -// NeedRedraw = TRUE; -// } -// SetSimulationVariable(a->name1, -// GetSimulationVariable(a->name2)); -// break; - -// case INT_INCREMENT_VARIABLE: -// IncrementVariable(a->name1); -// break; - -// { -// SWORD v; -// case INT_SET_VARIABLE_ADD: -// v = GetSimulationVariable(a->name2) + -// GetSimulationVariable(a->name3); -// goto math; -// case INT_SET_VARIABLE_SUBTRACT: -// v = GetSimulationVariable(a->name2) - -// GetSimulationVariable(a->name3); -// goto math; -// case INT_SET_VARIABLE_MULTIPLY: -// v = GetSimulationVariable(a->name2) * -// GetSimulationVariable(a->name3); -// goto math; -// case INT_SET_VARIABLE_DIVIDE: -// if(GetSimulationVariable(a->name3) != 0) { -// v = GetSimulationVariable(a->name2) / -// GetSimulationVariable(a->name3); -// } else { -// v = 0; -// Error(_("Division by zero; halting simulation")); -// StopSimulation(); -// } -// goto math; -// math: -// if(GetSimulationVariable(a->name1) != v) { -// NeedRedraw = TRUE; -// SetSimulationVariable(a->name1, v); -// } -// break; -// } - -// #define IF_BODY \ -// { \ -// IfConditionTrue(); \ -// } else { \ -// IfConditionFalse(); \ -// } -// case INT_IF_BIT_SET: -// if(SingleBitOn(a->name1)) -// IF_BODY -// break; - -// case INT_IF_BIT_CLEAR: -// if(!SingleBitOn(a->name1)) -// IF_BODY -// break; - -// case INT_IF_VARIABLE_LES_LITERAL: -// if(GetSimulationVariable(a->name1) < a->literal) -// IF_BODY -// break; - -// case INT_IF_VARIABLE_EQUALS_VARIABLE: -// if(GetSimulationVariable(a->name1) == -// GetSimulationVariable(a->name2)) -// IF_BODY -// break; - -// case INT_IF_VARIABLE_GRT_VARIABLE: -// if(GetSimulationVariable(a->name1) > -// GetSimulationVariable(a->name2)) -// IF_BODY -// break; - -// case INT_SET_PWM: -// // Dummy call will cause a warning if no one ever assigned -// // to that variable. -// (void)GetSimulationVariable(a->name1); -// break; - -// // Don't try to simulate the EEPROM stuff: just hold the EEPROM -// // busy all the time, so that the program never does anything -// // with it. -// case INT_EEPROM_BUSY_CHECK: -// SetSingleBit(a->name1, TRUE); -// break; - -// case INT_EEPROM_READ: -// case INT_EEPROM_WRITE: -// oops(); -// break; - -// case INT_READ_ADC: -// // Keep the shadow copies of the ADC variables because in -// // the real device they will not be updated until an actual -// // read is performed, which occurs only for a true rung-in -// // condition there. -// SetSimulationVariable(a->name1, GetAdcShadow(a->name1)); -// break; - -// case INT_UART_SEND: -// if(SingleBitOn(a->name2) && (SimulateUartTxCountdown == 0)) { -// SimulateUartTxCountdown = 2; -// AppendToUartSimulationTextControl( -// (BYTE)GetSimulationVariable(a->name1)); -// } -// if(SimulateUartTxCountdown == 0) { -// SetSingleBit(a->name2, FALSE); -// } else { -// SetSingleBit(a->name2, TRUE); -// } -// break; - -// case INT_UART_RECV: -// if(QueuedUartCharacter >= 0) { -// SetSingleBit(a->name2, TRUE); -// SetSimulationVariable(a->name1, (SWORD)QueuedUartCharacter); -// QueuedUartCharacter = -1; -// } else { -// SetSingleBit(a->name2, FALSE); -// } -// break; - -// case INT_END_IF: -// case INT_ELSE: -// return; - -// case INT_COMMENT: -// break; +static void SimulateIntCode(void) +{ + for(; IntPc < IntCodeLen; IntPc++) { + IntOp *a = &IntCode[IntPc]; + switch(a->op) { + case INT_SIMULATE_NODE_STATE: + if(*(a->poweredAfter) != SingleBitOn(a->name1)) + NeedRedraw = TRUE; + *(a->poweredAfter) = SingleBitOn(a->name1); + break; + + case INT_SET_BIT: + SetSingleBit(a->name1, TRUE); + break; + + case INT_CLEAR_BIT: + SetSingleBit(a->name1, FALSE); + break; + + case INT_COPY_BIT_TO_BIT: + SetSingleBit(a->name1, SingleBitOn(a->name2)); + break; + + case INT_SET_VARIABLE_TO_LITERAL: + if(GetSimulationVariable(a->name1) != + a->literal && a->name1[0] != '$') + { + NeedRedraw = TRUE; + } + SetSimulationVariable(a->name1, a->literal); + break; + + case INT_SET_VARIABLE_TO_VARIABLE: + if(GetSimulationVariable(a->name1) != + GetSimulationVariable(a->name2)) + { + NeedRedraw = TRUE; + } + SetSimulationVariable(a->name1, + GetSimulationVariable(a->name2)); + break; + + case INT_INCREMENT_VARIABLE: + IncrementVariable(a->name1); + break; + + { + SWORD v; + case INT_SET_VARIABLE_ADD: + v = GetSimulationVariable(a->name2) + + GetSimulationVariable(a->name3); + goto math; + case INT_SET_VARIABLE_SUBTRACT: + v = GetSimulationVariable(a->name2) - + GetSimulationVariable(a->name3); + goto math; + case INT_SET_VARIABLE_MULTIPLY: + v = GetSimulationVariable(a->name2) * + GetSimulationVariable(a->name3); + goto math; + case INT_SET_VARIABLE_DIVIDE: + if(GetSimulationVariable(a->name3) != 0) { + v = GetSimulationVariable(a->name2) / + GetSimulationVariable(a->name3); + } else { + v = 0; + Error(_("Division by zero; halting simulation")); + StopSimulation(); + } + goto math; +math: + if(GetSimulationVariable(a->name1) != v) { + NeedRedraw = TRUE; + SetSimulationVariable(a->name1, v); + } + break; + } + +#define IF_BODY \ + { \ + IfConditionTrue(); \ + } else { \ + IfConditionFalse(); \ + } + case INT_IF_BIT_SET: + if(SingleBitOn(a->name1)) + IF_BODY + break; + + case INT_IF_BIT_CLEAR: + if(!SingleBitOn(a->name1)) + IF_BODY + break; + + case INT_IF_VARIABLE_LES_LITERAL: + if(GetSimulationVariable(a->name1) < a->literal) + IF_BODY + break; + + case INT_IF_VARIABLE_EQUALS_VARIABLE: + if(GetSimulationVariable(a->name1) == + GetSimulationVariable(a->name2)) + IF_BODY + break; + + case INT_IF_VARIABLE_GRT_VARIABLE: + if(GetSimulationVariable(a->name1) > + GetSimulationVariable(a->name2)) + IF_BODY + break; + + case INT_SET_PWM: + // Dummy call will cause a warning if no one ever assigned + // to that variable. + (void)GetSimulationVariable(a->name1); + break; + + // Don't try to simulate the EEPROM stuff: just hold the EEPROM + // busy all the time, so that the program never does anything + // with it. + case INT_EEPROM_BUSY_CHECK: + SetSingleBit(a->name1, TRUE); + break; + + case INT_EEPROM_READ: + case INT_EEPROM_WRITE: + oops(); + break; + + case INT_READ_ADC: + // Keep the shadow copies of the ADC variables because in + // the real device they will not be updated until an actual + // read is performed, which occurs only for a true rung-in + // condition there. + SetSimulationVariable(a->name1, GetAdcShadow(a->name1)); + break; + + case INT_UART_SEND: + if(SingleBitOn(a->name2) && (SimulateUartTxCountdown == 0)) { + SimulateUartTxCountdown = 2; + AppendToUartSimulationTextControl( + (BYTE)GetSimulationVariable(a->name1)); + } + if(SimulateUartTxCountdown == 0) { + SetSingleBit(a->name2, FALSE); + } else { + SetSingleBit(a->name2, TRUE); + } + break; + + case INT_UART_RECV: + if(QueuedUartCharacter >= 0) { + SetSingleBit(a->name2, TRUE); + SetSimulationVariable(a->name1, (SWORD)QueuedUartCharacter); + QueuedUartCharacter = -1; + } else { + SetSingleBit(a->name2, FALSE); + } + break; + + case INT_END_IF: + case INT_ELSE: + return; + + case INT_COMMENT: + break; -// default: -// oops(); -// break; -// } -// } -// } + default: + oops(); + break; + } + } +} //----------------------------------------------------------------------------- // Called by the Windows timer that triggers cycles when we are running // in real time. //----------------------------------------------------------------------------- -// void CALLBACK PlcCycleTimer(HWND hwnd, UINT msg, UINT_PTR id, DWORD time) -// { -// int i; -// for(i = 0; i < CyclesPerTimerTick; i++) { -// SimulateOneCycle(FALSE); -// } -// } +BOOL PlcCycleTimer(BOOL kill = FALSE) +{ + for(int i = 0; i < CyclesPerTimerTick; i++) { + SimulateOneCycle(FALSE); + } + + return !kill; +} //----------------------------------------------------------------------------- // Simulate one cycle of the PLC. Update everything, and keep track of whether // any outputs have changed. If so, force a screen refresh. If requested do // a screen refresh regardless. //----------------------------------------------------------------------------- -// void SimulateOneCycle(BOOL forceRefresh) -// { -// // When there is an error message up, the modal dialog makes its own -// // event loop, and there is risk that we would go recursive. So let -// // us fix that. (Note that there are no concurrency issues; we really -// // would get called recursively, not just reentrantly.) -// static BOOL Simulating = FALSE; +void SimulateOneCycle(BOOL forceRefresh) +{ + // When there is an error message up, the modal dialog makes its own + // event loop, and there is risk that we would go recursive. So let + // us fix that. (Note that there are no concurrency issues; we really + // would get called recursively, not just reentrantly.) + static BOOL Simulating = FALSE; -// if(Simulating) return; -// Simulating = TRUE; + if(Simulating) return; + Simulating = TRUE; -// NeedRedraw = FALSE; + NeedRedraw = FALSE; -// if(SimulateUartTxCountdown > 0) { -// SimulateUartTxCountdown--; -// } else { -// SimulateUartTxCountdown = 0; -// } + if(SimulateUartTxCountdown > 0) { + SimulateUartTxCountdown--; + } else { + SimulateUartTxCountdown = 0; + } -// IntPc = 0; -// SimulateIntCode(); + IntPc = 0; + SimulateIntCode(); -// if(NeedRedraw || SimulateRedrawAfterNextCycle || forceRefresh) { -// InvalidateRect(MainWindow, NULL, FALSE); -// ListView_RedrawItems(IoList, 0, Prog.io.count - 1); -// } + if(NeedRedraw || SimulateRedrawAfterNextCycle || forceRefresh) { + InvalidateRect(DrawWindow, NULL, FALSE); + gtk_widget_queue_draw(DrawWindow); + // ListView_RedrawItems(IoList, 0, Prog.io.count - 1); + } -// SimulateRedrawAfterNextCycle = FALSE; -// if(NeedRedraw) SimulateRedrawAfterNextCycle = TRUE; + SimulateRedrawAfterNextCycle = FALSE; + if(NeedRedraw) SimulateRedrawAfterNextCycle = TRUE; -// Simulating = FALSE; -// } + Simulating = FALSE; +} //----------------------------------------------------------------------------- // Start the timer that we use to trigger PLC cycles in approximately real @@ -725,81 +727,81 @@ static char *MarkUsedVariable(char *name, DWORD flag); // is about as fast as anyone could follow by eye. Faster timers will just // go instantly. //----------------------------------------------------------------------------- -// void StartSimulationTimer(void) -// { -// int p = Prog.cycleTime/1000; -// if(p < 5) { -// SetTimer(MainWindow, TIMER_SIMULATE, 10, PlcCycleTimer); -// CyclesPerTimerTick = 10000 / Prog.cycleTime; -// } else { -// SetTimer(MainWindow, TIMER_SIMULATE, p, PlcCycleTimer); -// CyclesPerTimerTick = 1; -// } -// } +void StartSimulationTimer(void) +{ + int p = Prog.cycleTime/1000; + if(p < 5) { + SetTimer(MainWindow, TIMER_SIMULATE, 10, PlcCycleTimer); + CyclesPerTimerTick = 10000 / Prog.cycleTime; + } else { + SetTimer(MainWindow, TIMER_SIMULATE, p, PlcCycleTimer); + CyclesPerTimerTick = 1; + } +} //----------------------------------------------------------------------------- // Clear out all the parameters relating to the previous simulation. //----------------------------------------------------------------------------- -// void ClearSimulationData(void) -// { -// VariablesCount = 0; -// SingleBitItemsCount = 0; -// AdcShadowsCount = 0; -// QueuedUartCharacter = -1; -// SimulateUartTxCountdown = 0; +void ClearSimulationData(void) +{ + VariablesCount = 0; + SingleBitItemsCount = 0; + AdcShadowsCount = 0; + QueuedUartCharacter = -1; + SimulateUartTxCountdown = 0; -// CheckVariableNames(); + CheckVariableNames(); -// SimulateRedrawAfterNextCycle = TRUE; + SimulateRedrawAfterNextCycle = TRUE; -// if(!GenerateIntermediateCode()) { -// ToggleSimulationMode(); -// return; -// } + if(!GenerateIntermediateCode()) { + ToggleSimulationMode(); + return; + } -// SimulateOneCycle(TRUE); -// } + SimulateOneCycle(TRUE); +} //----------------------------------------------------------------------------- // Provide a description for an item (Xcontacts, Ycoil, Rrelay, Ttimer, // or other) in the I/O list. //----------------------------------------------------------------------------- -// void DescribeForIoList(char *name, char *out) -// { -// switch(name[0]) { -// case 'R': -// case 'X': -// case 'Y': -// sprintf(out, "%d", SingleBitOn(name)); -// break; - -// case 'T': { -// double dtms = GetSimulationVariable(name) * -// (Prog.cycleTime / 1000.0); -// if(dtms < 1000) { -// sprintf(out, "%.2f ms", dtms); -// } else { -// sprintf(out, "%.3f s", dtms / 1000); -// } -// break; -// } -// default: { -// SWORD v = GetSimulationVariable(name); -// sprintf(out, "%hd (0x%04hx)", v, v); -// break; -// } -// } -// } +void DescribeForIoList(char *name, char *out) +{ + switch(name[0]) { + case 'R': + case 'X': + case 'Y': + sprintf(out, "%d", SingleBitOn(name)); + break; + + case 'T': { + double dtms = GetSimulationVariable(name) * + (Prog.cycleTime / 1000.0); + if(dtms < 1000) { + sprintf(out, "%.2f ms", dtms); + } else { + sprintf(out, "%.3f s", dtms / 1000); + } + break; + } + default: { + SWORD v = GetSimulationVariable(name); + sprintf(out, "%hd (0x%04hx)", v, v); + break; + } + } +} //----------------------------------------------------------------------------- // Toggle the state of a contact input; for simulation purposes, so that we // can set the input state of the program. //----------------------------------------------------------------------------- -// void SimulationToggleContact(char *name) -// { -// SetSingleBit(name, !SingleBitOn(name)); -// ListView_RedrawItems(IoList, 0, Prog.io.count - 1); -// } +void SimulationToggleContact(char *name) +{ + SetSingleBit(name, !SingleBitOn(name)); + // ListView_RedrawItems(IoList, 0, Prog.io.count - 1); +} //----------------------------------------------------------------------------- // Dialog proc for the popup that lets you interact with the UART stuff. @@ -835,140 +837,154 @@ static char *MarkUsedVariable(char *name, DWORD flag); //----------------------------------------------------------------------------- // Intercept WM_CHAR messages that to the terminal simulation window so that // we can redirect them to the PLC program. -//----------------------------------------------------------------------------- -// static LRESULT CALLBACK UartSimulationTextProc(HWND hwnd, UINT msg, -// WPARAM wParam, LPARAM lParam) -// { -// if(msg == WM_CHAR) { -// QueuedUartCharacter = (BYTE)wParam; -// return 0; -// } - -// return CallWindowProc((WNDPROC)PrevTextProc, hwnd, msg, wParam, lParam); -// } +// +// Ported: Read and write text fron the text view widget. +//----------------------------------------------------------------------------- +static void UartSimulationTextProc(HWID hwid, UINT umsg, char *text, UINT uszbuf) +{ + switch(umsg) + { + case WM_SETTEXT: + { + GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(hwid)); + gtk_text_buffer_set_text (buffer, text, -1); + gtk_text_view_set_buffer (GTK_TEXT_VIEW(hwid), buffer); + + GtkTextIter end; + gtk_text_buffer_get_end_iter (buffer, &end); + gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW(hwid), &end, 0.2, FALSE, 1, 1); + break; + } + case WM_SETTEXT_END: + { + GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(hwid)); + gtk_text_buffer_insert_at_cursor (buffer, text, -1); + gtk_text_view_set_buffer (GTK_TEXT_VIEW(hwid), buffer); + + GtkTextIter end; + gtk_text_buffer_get_end_iter (buffer, &end); + gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW(hwid), &end, 0.2, FALSE, 1, 1); + break; + } + case WM_GETTEXT: + { + GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(hwid)); + GtkTextIter start, end; + gtk_text_buffer_get_start_iter (buffer, &start); + gtk_text_buffer_get_end_iter (buffer, &end); + + char *txtBuf = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); + + strcpy(text, txtBuf); + strcat(text, "\0"); + g_free(txtBuf); + break; + } + default: + break; + } +} //----------------------------------------------------------------------------- // Pop up the UART simulation window; like a terminal window where the // characters that you type go into UART RECV instruction and whatever // the program puts into UART SEND shows up as text. //----------------------------------------------------------------------------- -// void ShowUartSimulationWindow(void) -// { -// WNDCLASSEX wc; -// memset(&wc, 0, sizeof(wc)); -// wc.cbSize = sizeof(wc); - -// wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC | -// CS_DBLCLKS; -// wc.lpfnWndProc = (WNDPROC)UartSimulationProc; -// wc.hInstance = Instance; -// wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW; -// wc.lpszClassName = "LDmicroUartSimulationWindow"; -// wc.lpszMenuName = NULL; -// wc.hCursor = LoadCursor(NULL, IDC_ARROW); - -// RegisterClassEx(&wc); - -// DWORD TerminalX = 200, TerminalY = 200, TerminalW = 300, TerminalH = 150; - -// ThawDWORD(TerminalX); -// ThawDWORD(TerminalY); -// ThawDWORD(TerminalW); -// ThawDWORD(TerminalH); - -// if(TerminalW > 800) TerminalW = 100; -// if(TerminalH > 800) TerminalH = 100; - -// RECT r; -// GetClientRect(GetDesktopWindow(), &r); -// if(TerminalX >= (DWORD)(r.right - 10)) TerminalX = 100; -// if(TerminalY >= (DWORD)(r.bottom - 10)) TerminalY = 100; - -// UartSimulationWindow = CreateWindowClient(WS_EX_TOOLWINDOW | -// WS_EX_APPWINDOW, "LDmicroUartSimulationWindow", -// "UART Simulation (Terminal)", WS_VISIBLE | WS_SIZEBOX, -// TerminalX, TerminalY, TerminalW, TerminalH, -// NULL, NULL, Instance, NULL); - -// UartSimulationTextControl = CreateWindowEx(0, WC_EDIT, "", WS_CHILD | -// WS_CLIPSIBLINGS | WS_VISIBLE | ES_AUTOVSCROLL | ES_MULTILINE | -// WS_VSCROLL, 0, 0, TerminalW, TerminalH, UartSimulationWindow, NULL, -// Instance, NULL); - -// HFONT fixedFont = CreateFont(14, 0, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE, -// ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, -// FF_DONTCARE, "Lucida Console"); -// if(!fixedFont) -// fixedFont = (HFONT)GetStockObject(SYSTEM_FONT); - -// SendMessage((HWND)UartSimulationTextControl, WM_SETFONT, (WPARAM)fixedFont, -// TRUE); - -// PrevTextProc = SetWindowLongPtr(UartSimulationTextControl, -// GWLP_WNDPROC, (LONG_PTR)UartSimulationTextProc); - -// ShowWindow(UartSimulationWindow, TRUE); -// SetFocus(MainWindow); -// } +void ShowUartSimulationWindow(void) +{ + DWORD TerminalX = 200, TerminalY = 200, TerminalW = 300, TerminalH = 150; + + ThawDWORD(TerminalX); + ThawDWORD(TerminalY); + ThawDWORD(TerminalW); + ThawDWORD(TerminalH); + + if(TerminalW > 800) TerminalW = 100; + if(TerminalH > 800) TerminalH = 100; + + UartSimulationWindow = CreateWindowClient(GTK_WINDOW_TOPLEVEL, GDK_WINDOW_TYPE_HINT_NORMAL, + "UART Simulation (Terminal)", TerminalX, TerminalY, TerminalW, TerminalH, NULL); + /// remove close button + gtk_window_set_deletable (GTK_WINDOW(UartSimulationWindow), FALSE); + + UartSimulationTextControl = gtk_text_view_new(); + + gtk_widget_override_font(GTK_WIDGET(UartSimulationTextControl), pango_font_description_from_string("Lucida Console")); + + /// Add text view into a scrolled window to enable scrolling functionality + HWID TextViewScroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (TextViewScroll), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_widget_set_hexpand(GTK_WIDGET(TextViewScroll), TRUE); + gtk_widget_set_vexpand(GTK_WIDGET(TextViewScroll), TRUE); + + gtk_container_add (GTK_CONTAINER(TextViewScroll), UartSimulationTextControl); + gtk_container_add (GTK_CONTAINER(UartSimulationWindow), TextViewScroll); + + gtk_widget_show_all(UartSimulationWindow); + + gtk_window_set_keep_above (GTK_WINDOW(MainWindow), TRUE); + gtk_window_set_focus_visible (GTK_WINDOW(MainWindow), TRUE); + gtk_window_set_keep_above (GTK_WINDOW(MainWindow), FALSE); +} //----------------------------------------------------------------------------- // Get rid of the UART simulation terminal-type window. //----------------------------------------------------------------------------- -// void DestroyUartSimulationWindow(void) -// { -// // Try not to destroy the window if it is already destroyed; that is -// // not for the sake of the window, but so that we don't trash the -// // stored position. -// if(UartSimulationWindow == NULL) return; +void DestroyUartSimulationWindow(void) +{ + // Try not to destroy the window if it is already destroyed; that is + // not for the sake of the window, but so that we don't trash the + // stored position. + if(UartSimulationWindow == NULL) return; -// DWORD TerminalX, TerminalY, TerminalW, TerminalH; -// RECT r; + DWORD TerminalX, TerminalY, TerminalW, TerminalH; + RECT r; -// GetClientRect(UartSimulationWindow, &r); -// TerminalW = r.right - r.left; -// TerminalH = r.bottom - r.top; + GetClientRect(UartSimulationWindow, &r); + TerminalW = r.right - r.left; + TerminalH = r.bottom - r.top; -// GetWindowRect(UartSimulationWindow, &r); -// TerminalX = r.left; -// TerminalY = r.top; + GetWindowRect(UartSimulationWindow, &r); + TerminalX = r.left; + TerminalY = r.top; -// FreezeDWORD(TerminalX); -// FreezeDWORD(TerminalY); -// FreezeDWORD(TerminalW); -// FreezeDWORD(TerminalH); + FreezeDWORD(TerminalX); + FreezeDWORD(TerminalY); + FreezeDWORD(TerminalW); + FreezeDWORD(TerminalH); -// DestroyWindow(UartSimulationWindow); -// UartSimulationWindow = NULL; -// } + DestroyWindow(UartSimulationWindow); + UartSimulationWindow = NULL; +} //----------------------------------------------------------------------------- // Append a received character to the terminal buffer. //----------------------------------------------------------------------------- -// static void AppendToUartSimulationTextControl(BYTE b) -// { -// char append[5]; - -// if((isalnum(b) || strchr("[]{};':\",.<>/?`~ !@#$%^&*()-=_+|", b) || -// b == '\r' || b == '\n') && b != '\0') -// { -// append[0] = b; -// append[1] = '\0'; -// } else { -// sprintf(append, "\\x%02x", b); -// } +static void AppendToUartSimulationTextControl(BYTE b) +{ + char append[5]; -// #define MAX_SCROLLBACK 256 -// char buf[MAX_SCROLLBACK]; + if((isalnum(b) || strchr("[]{};':\",.<>/?`~ !@#$%^&*()-=_+|", b) || + b == '\r' || b == '\n') && b != '\0') + { + append[0] = b; + append[1] = '\0'; + } else { + sprintf(append, "\\x%02x", b); + } -// SendMessage(UartSimulationTextControl, WM_GETTEXT, (WPARAM)sizeof(buf), -// (LPARAM)buf); +#define MAX_SCROLLBACK 256 + char buf[MAX_SCROLLBACK]; -// int overBy = (strlen(buf) + strlen(append) + 1) - sizeof(buf); -// if(overBy > 0) { -// memmove(buf, buf + overBy, strlen(buf)); -// } -// strcat(buf, append); + UartSimulationTextProc(UartSimulationTextControl, WM_GETTEXT, buf, strlen(buf)); -// SendMessage(UartSimulationTextControl, WM_SETTEXT, 0, (LPARAM)buf); -// SendMessage(UartSimulationTextControl, EM_LINESCROLL, 0, (LPARAM)INT_MAX); -// } + int overBy = (strlen(buf) + strlen(append) + 1) - sizeof(buf); + if(overBy > 0) { + memmove(buf, buf + overBy, strlen(buf)); + } + strcat(buf, append); + + UartSimulationTextProc(UartSimulationTextControl, WM_SETTEXT, buf, strlen(buf)); +} |