From 88dfbfa8a9ea63b72c5d185e1678c98b7585e8b3 Mon Sep 17 00:00:00 2001
From: Akshay Chipkar
Date: Fri, 22 Apr 2016 18:12:44 +0530
Subject: added all relevant files for ldmicro
---
ldmicro/CHANGES.txt | 182 ++++
ldmicro/COPYING.txt | 675 +++++++++++++
ldmicro/INTERNALS.txt | 137 +++
ldmicro/Makefile | 77 ++
ldmicro/README.txt | 181 ++++
ldmicro/ansic.cpp | 427 ++++++++
ldmicro/arduino.cpp | 1381 +++++++++++++++++++++++++
ldmicro/avr.cpp | 1402 ++++++++++++++++++++++++++
ldmicro/circuit.cpp | 950 +++++++++++++++++
ldmicro/coildialog.cpp | 210 ++++
ldmicro/commentdialog.cpp | 97 ++
ldmicro/compilecommon.cpp | 373 +++++++
ldmicro/confdialog.cpp | 249 +++++
ldmicro/contactsdialog.cpp | 177 ++++
ldmicro/draw.cpp | 1066 ++++++++++++++++++++
ldmicro/draw_outputdev.cpp | 607 +++++++++++
ldmicro/helpdialog.cpp | 308 ++++++
ldmicro/intcode.cpp | 1179 ++++++++++++++++++++++
ldmicro/intcode.h | 83 ++
ldmicro/interpreted.cpp | 248 +++++
ldmicro/iolist.cpp | 885 ++++++++++++++++
ldmicro/lang-de.txt | 762 ++++++++++++++
ldmicro/lang-es.txt | 767 ++++++++++++++
ldmicro/lang-fr.txt | 761 ++++++++++++++
ldmicro/lang-it.txt | 761 ++++++++++++++
ldmicro/lang-make.pl | 66 ++
ldmicro/lang-pt.txt | 765 ++++++++++++++
ldmicro/lang-tables.pl | 52 +
ldmicro/lang-tr.txt | 761 ++++++++++++++
ldmicro/lang.cpp | 78 ++
ldmicro/ldinterpret.c | 380 +++++++
ldmicro/ldinterpret.exe | Bin 0 -> 64512 bytes
ldmicro/ldinterpret.obj | Bin 0 -> 7923 bytes
ldmicro/ldmicro.cpp | 1152 +++++++++++++++++++++
ldmicro/ldmicro.exe.manifest | 22 +
ldmicro/ldmicro.h | 760 ++++++++++++++
ldmicro/ldmicro.ico | Bin 0 -> 10134 bytes
ldmicro/ldmicro.pdb | Bin 0 -> 19456 bytes
ldmicro/ldmicro.rc | 6 +
ldmicro/loadsave.cpp | 649 ++++++++++++
ldmicro/lutdialog.cpp | 565 +++++++++++
ldmicro/maincontrols.cpp | 745 ++++++++++++++
ldmicro/make.bat | 1 +
ldmicro/makeall.pl | 17 +
ldmicro/manual-de.txt | 1075 ++++++++++++++++++++
ldmicro/manual-fr.txt | 1017 +++++++++++++++++++
ldmicro/manual-tr.txt | 790 +++++++++++++++
ldmicro/manual.txt | 969 ++++++++++++++++++
ldmicro/mcutable.h | 862 ++++++++++++++++
ldmicro/mcutablenw.h | 862 ++++++++++++++++
ldmicro/miscutil.cpp | 384 +++++++
ldmicro/pic16.cpp | 1625 ++++++++++++++++++++++++++++++
ldmicro/reg/expected/avr-hw.hex | 330 ++++++
ldmicro/reg/expected/contacts.hex | 21 +
ldmicro/reg/expected/integers.hex | 134 +++
ldmicro/reg/expected/misc-ops.hex | 69 ++
ldmicro/reg/expected/pic16-hw.hex | 189 ++++
ldmicro/reg/expected/timers-counters.hex | 119 +++
ldmicro/reg/go.bat | 1 +
ldmicro/reg/results/avr-hw.hex | 330 ++++++
ldmicro/reg/results/contacts.hex | 21 +
ldmicro/reg/results/integers.hex | 134 +++
ldmicro/reg/results/misc-ops.hex | 69 ++
ldmicro/reg/results/pic16-hw.hex | 189 ++++
ldmicro/reg/results/timers-counters.hex | 119 +++
ldmicro/reg/run-tests.pl | 33 +
ldmicro/reg/tests/avr-hw.ld | 61 ++
ldmicro/reg/tests/contacts.ld | 41 +
ldmicro/reg/tests/integers.ld | 61 ++
ldmicro/reg/tests/misc-ops.ld | 53 +
ldmicro/reg/tests/pic16-hw.ld | 61 ++
ldmicro/reg/tests/timers-counters.ld | 57 ++
ldmicro/resetdialog.cpp | 148 +++
ldmicro/sample/7seg-display.ld | 123 +++
ldmicro/sample/blink.ld | 61 ++
ldmicro/sample/pic-adc.ld | 158 +++
ldmicro/sample/serial.ld | 92 ++
ldmicro/sample/traffic.ld | 73 ++
ldmicro/sample/var-timer.ld | 63 ++
ldmicro/schematic.cpp | 725 +++++++++++++
ldmicro/simpledialog.cpp | 422 ++++++++
ldmicro/simulate.cpp | 972 ++++++++++++++++++
ldmicro/txt2c.pl | 45 +
ldmicro/undoredo.cpp | 249 +++++
ldmicro/vc100.pdb | Bin 0 -> 126976 bytes
85 files changed, 32771 insertions(+)
create mode 100644 ldmicro/CHANGES.txt
create mode 100644 ldmicro/COPYING.txt
create mode 100644 ldmicro/INTERNALS.txt
create mode 100644 ldmicro/Makefile
create mode 100644 ldmicro/README.txt
create mode 100644 ldmicro/ansic.cpp
create mode 100644 ldmicro/arduino.cpp
create mode 100644 ldmicro/avr.cpp
create mode 100644 ldmicro/circuit.cpp
create mode 100644 ldmicro/coildialog.cpp
create mode 100644 ldmicro/commentdialog.cpp
create mode 100644 ldmicro/compilecommon.cpp
create mode 100644 ldmicro/confdialog.cpp
create mode 100644 ldmicro/contactsdialog.cpp
create mode 100644 ldmicro/draw.cpp
create mode 100644 ldmicro/draw_outputdev.cpp
create mode 100644 ldmicro/helpdialog.cpp
create mode 100644 ldmicro/intcode.cpp
create mode 100644 ldmicro/intcode.h
create mode 100644 ldmicro/interpreted.cpp
create mode 100644 ldmicro/iolist.cpp
create mode 100644 ldmicro/lang-de.txt
create mode 100644 ldmicro/lang-es.txt
create mode 100644 ldmicro/lang-fr.txt
create mode 100644 ldmicro/lang-it.txt
create mode 100644 ldmicro/lang-make.pl
create mode 100644 ldmicro/lang-pt.txt
create mode 100644 ldmicro/lang-tables.pl
create mode 100644 ldmicro/lang-tr.txt
create mode 100644 ldmicro/lang.cpp
create mode 100644 ldmicro/ldinterpret.c
create mode 100644 ldmicro/ldinterpret.exe
create mode 100644 ldmicro/ldinterpret.obj
create mode 100644 ldmicro/ldmicro.cpp
create mode 100644 ldmicro/ldmicro.exe.manifest
create mode 100644 ldmicro/ldmicro.h
create mode 100644 ldmicro/ldmicro.ico
create mode 100644 ldmicro/ldmicro.pdb
create mode 100644 ldmicro/ldmicro.rc
create mode 100644 ldmicro/loadsave.cpp
create mode 100644 ldmicro/lutdialog.cpp
create mode 100644 ldmicro/maincontrols.cpp
create mode 100644 ldmicro/make.bat
create mode 100644 ldmicro/makeall.pl
create mode 100644 ldmicro/manual-de.txt
create mode 100644 ldmicro/manual-fr.txt
create mode 100644 ldmicro/manual-tr.txt
create mode 100644 ldmicro/manual.txt
create mode 100644 ldmicro/mcutable.h
create mode 100644 ldmicro/mcutablenw.h
create mode 100644 ldmicro/miscutil.cpp
create mode 100644 ldmicro/pic16.cpp
create mode 100644 ldmicro/reg/expected/avr-hw.hex
create mode 100644 ldmicro/reg/expected/contacts.hex
create mode 100644 ldmicro/reg/expected/integers.hex
create mode 100644 ldmicro/reg/expected/misc-ops.hex
create mode 100644 ldmicro/reg/expected/pic16-hw.hex
create mode 100644 ldmicro/reg/expected/timers-counters.hex
create mode 100644 ldmicro/reg/go.bat
create mode 100644 ldmicro/reg/results/avr-hw.hex
create mode 100644 ldmicro/reg/results/contacts.hex
create mode 100644 ldmicro/reg/results/integers.hex
create mode 100644 ldmicro/reg/results/misc-ops.hex
create mode 100644 ldmicro/reg/results/pic16-hw.hex
create mode 100644 ldmicro/reg/results/timers-counters.hex
create mode 100644 ldmicro/reg/run-tests.pl
create mode 100644 ldmicro/reg/tests/avr-hw.ld
create mode 100644 ldmicro/reg/tests/contacts.ld
create mode 100644 ldmicro/reg/tests/integers.ld
create mode 100644 ldmicro/reg/tests/misc-ops.ld
create mode 100644 ldmicro/reg/tests/pic16-hw.ld
create mode 100644 ldmicro/reg/tests/timers-counters.ld
create mode 100644 ldmicro/resetdialog.cpp
create mode 100644 ldmicro/sample/7seg-display.ld
create mode 100644 ldmicro/sample/blink.ld
create mode 100644 ldmicro/sample/pic-adc.ld
create mode 100644 ldmicro/sample/serial.ld
create mode 100644 ldmicro/sample/traffic.ld
create mode 100644 ldmicro/sample/var-timer.ld
create mode 100644 ldmicro/schematic.cpp
create mode 100644 ldmicro/simpledialog.cpp
create mode 100644 ldmicro/simulate.cpp
create mode 100644 ldmicro/txt2c.pl
create mode 100644 ldmicro/undoredo.cpp
create mode 100644 ldmicro/vc100.pdb
(limited to 'ldmicro')
diff --git a/ldmicro/CHANGES.txt b/ldmicro/CHANGES.txt
new file mode 100644
index 0000000..0271356
--- /dev/null
+++ b/ldmicro/CHANGES.txt
@@ -0,0 +1,182 @@
+
+== Release 2.2
+
+ * Fix a problem with the ANSI C target when the program had bit and
+ integer variables with the same name. Note that this changes the
+ names of the symbols in the generated C program; so a system that
+ uses "magic variables" with this target for I/O must be updated
+ to use the new names.
+
+ * Fix a subtle bug in the PIC16 add and subtract routines, where
+ operations of the form B = A - B could fail.
+
+ * The piecewise linear tables were broken for the AVRs; fix that.
+
+== Release 2.1
+
+ * For the AVR UARTs, poll UDRE instead of TXC. The formatted string op
+ was broken on some targets, should now be fixed.
+
+ * Don't draw selected op in bold font; that looks ugly under Vista.
+
+== Release 2.0
+
+ * Add PIC16F886 and PIC16F887 targets.
+
+ * Fix display bug in the list to select an I/O pin.
+
+ * Fix bug where PIC16 UART locks up forever after a framing error when
+ the cycle time is faster than one byte time.
+
+ * Fix bug where PIC16 outputs could briefly glitch high at startup.
+
+ * Clear PCLATH in PIC16 boot vector, since some bootloaders expect that.
+
+== Release 1.9
+
+ * Modify PIC16 boot vectors to work with many bootloaders.
+
+== Release 1.8
+
+ * Fix modification of a constant string that blew up in new MSVC++
+ compiler.
+
+ * Add Italian, Turkish, Portuguese.
+
+== Release 1.7
+
+ * Make the source compile with latest version of MSVC++; overloaded
+ functions behave a bit differently.
+
+ * Recover from (and ignore) UART errors on the PIC16 target, instead
+ of getting stuck forever.
+
+ * Whenever contacts bound to an output pin (Yfoo) were edited, they
+ reverted to an input pin (Xfoo); now fixed.
+
+ * Don't abort on too-wide program; instead display nice message.
+
+ * It was possible (by adding and deleting contacts/coils with the
+ same name) to end up with two bit variables bound to the same
+ physical I/O pin; now fixed.
+
+ * File -> Open was correct, but Ctrl+O failed to ask about unsaved
+ changes before opening requested file; now both are correct.
+
+ * Add Spanish user interface strings.
+
+== Release 1.6
+
+ * Internationalize the user interface strings; we now have versions
+ in English, French, and German.
+
+ * First source release, under the GPLv3.
+
+== Release 1.5
+
+ * Add untested support for ATmega32.
+
+ * Remove annoying lag in user interface when editing large (hundreds
+ of ops) programs
+
+== Release 1.4
+
+ * Fix a terrible bug in the target for the ATmega8; because there is
+ no PORTA/DDRA/PINA, I broke an assumption in my code and failed
+ to set up the port directions.
+
+== Release 1.3
+
+ * Timer delays are represented as a signed 32-bit integer count
+ of microseconds. If the user provides a delay >= 2**31 us, then
+ show an error instead of just letting things wrap.
+
+ * Change the start-up behaviour of TOF timers. Previously they would
+ start from a count of zero, so they would be on (independent of
+ rung-in) until they counted themselves off. Now they start out
+ at full count (as if rung-in has been low for a very long time),
+ so rung-out is low until rung-in goes high.
+
+== Release 1.2
+
+ * Add an untested target for the ATmega8
+
+ * Add a special instruction to simplify piecewise linear tables
+
+ * Fix some user interface bugs: it was possible to drag the top of the
+ I/O list so high that you couldn't grab it again, and there were
+ some cases in which the pin number associated with UART and PWM
+ variables was not displayed
+
+== Release 1.1
+
+ * Fix persistent variables, which were broken for the PIC16F628
+
+== Release 1.0
+
+ * Fix bug in which the filename that appears in the title bar of the
+ main window failed to get updated when opening/saving a file using
+ the keyboard shortcuts (Ctrl+O/+S)
+
+ * Fix simulation crash when the ladder logic program divides by zero
+
+ * Fix jumpy scrolling on programs with many rungs of logic when the
+ cursor is off-screen
+
+== Release 0.9
+
+ * Fix bug with formatted string op on the AVR
+ * Fix previously-untested ATmega16 and ATmega162 targets, which were
+ completely broken
+
+=== Release 0.8
+
+ * Fix PORTA on the PIC16F819 (came up assigned to ADCs, of course)
+
+=== Release 0.7
+
+ * Support arbitrary character (\xAB) escapes in formatted string op
+ * Fix a bug in which the title bar of the main window was not updated
+
+=== Release 0.6
+
+ * Add formatted text output over serial (e.g. to an LCD or a PC)
+ * Add ability to make variables persistent (i.e. auto-saved in EEPROM)
+ * Add look-up table instructions
+ * Fix a bug with the PORTE pins on some AVRs
+ * Fix miscellaneous user interface bugs
+
+=== Release 0.5
+
+ * Interpretable byte code target
+ * Shift register and master control relay instructions
+
+=== Release 0.4
+
+ * Make ADCs work on the AVRs
+
+=== Release 0.3
+
+ * Support serial for AVR
+ * Support PWM for PIC16 and AVR
+ * Show program filename in title bar of main window
+ * Untested support for PIC16F88, F819, F876
+ * Generate ANSI C code from ladder diagram
+
+=== Release 0.2
+
+ * Support serial communications (using UART), PIC16 only
+ * Support ADC reads, PIC16 only
+ * Simulation environment for ADC and serial
+ * Support ASCII character constant ('a') literals
+ * Fix PORTA pins in PIC16F628 (should assign as GPIO, not to comparator)
+ * Make file open/save dialogs work under Win98
+ * Fix PORTA/PORTE pins in PIC16F877 (should assign as GPIO, not to ADC)
+ * Add ability to comment your program
+ * Fix bug when a relative filename is given on the command line and
+ the `Compile As' dialog is later used to specify a destination in
+ a different directory
+
+=== Release 0.1
+
+ Initial release
diff --git a/ldmicro/COPYING.txt b/ldmicro/COPYING.txt
new file mode 100644
index 0000000..6d9f59c
--- /dev/null
+++ b/ldmicro/COPYING.txt
@@ -0,0 +1,675 @@
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program 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.
+
+ This program 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 this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/ldmicro/INTERNALS.txt b/ldmicro/INTERNALS.txt
new file mode 100644
index 0000000..b84c216
--- /dev/null
+++ b/ldmicro/INTERNALS.txt
@@ -0,0 +1,137 @@
+
+LDMICRO INTERNALS
+=================
+
+This document describes LDmicro's internal structure. I intend it as a
+quick reference for my own use.
+
+
+ADDING A NEW ELEM_XXX LADDER INSTRUCTION
+========================================
+
+It is necessary to make changes in the following places:
+
+ * ldmicro.h -- add the new ELEM_XXX #define
+ -- add the new MNU_XXX for the menu to add it
+ -- add any necessary data structures to ElemLeaf
+ -- add prototypes for newly created extern functions
+ -- if it is a leaf (it almost certainly is), to
+ CASE_LEAF
+
+ * maincontrols. -- add the code to create the menu
+ cpp -- add the code to enable/disable the menu when we
+ go from `edit' to `simulate' mode
+
+ * iolist.cpp -- add to routines that build the I/O list; even if
+ it does not affect the I/O list, it must be added
+ to the case statement in ExtractNamesFromCircuit,
+ so that it explicitly does nothing
+
+ * draw.cpp -- routines to draw the element on the ladder diagram
+
+ * loadsave.cpp -- routines to load and save the element to the
+ xxx.ld file
+
+ * intcode.cpp -- routines to generate intermediate code from the
+ instruction
+
+ * schematic.cpp -- WhatCanWeDoFromCursorAndTopology, update the
+ enabled state of the menus (what can be inserted
+ above/below/left/right, etc.) based on selection
+ -- EditSelectedElement, self-explanatory
+
+ * ldmicro.cpp -- typically menu/keyboard handlers to call the
+ AddXXX function to insert the element
+
+ * circuit.cpp -- the new AddXXX function, to add the element at the
+ cursor point
+
+ * simulate.cpp -- CheckVariableNamesCircuit, the design rules check
+ to ensure that e.g. same Tname isn't used with
+ two timers
+
+ * xxxdialog.cpp -- an `edit element' dialog if necessary, somewhere
+ (most likely to be simpledialog.cpp, using that)
+
+
+REPRESENTATION OF THE PROGRAM
+=============================
+
+(adapted from an email message, so the tone's a little odd, sorry)
+
+A ladder program can be thought of as a series-parallel network. Each
+rung is a series circuit; this circuit contains either leaf elements
+(like contacts, coils, etc.), or parallel circuits. The parallel circuits
+contain either leaf elements or series subcircuits. If you look at a
+.ld file in a text editor then you can see that I chose to represent
+the program in this way.
+
+This representation makes the compiler a relatively simple problem.
+Imagine that you wish to write a routine to evaluate a circuit. This
+routine will take as an input the circuit's rung-in condition, and
+provide as an output its rung-out. For a circuit consisting of a single
+leaf element this is straightforward; if you look at the Allen Bradley
+documentation then you will see that this is actually how they specify
+their instructions. For example, the rung-out condition of a set of
+contacts is false if either its rung-in condition is false or the input
+corresponding to the set of contacts is false. Let us say that for a
+leaf element L, the output
+
+ Rout = L(Rin).
+
+If that element is a set of contacts named `Xin,' then
+
+ L(Rin) := Rin && (Xin is HIGH).
+
+(I will use && for AND, and || for OR.)
+
+Next we must figure out how to compile a series circuit, for example (left
+to right), sub-circuits A, B, and C in series. In that case, the rung-in
+condition for A is the rung-in condition for the circuit. Then the rung-in
+condition for B is the rung-out condition for B, the rung-in condition for C
+is the rung-out condition for B, and the rung-out condition for the whole
+circuit is the rung-out condition for C. So if the whole series circuit is
+X, then
+
+ X(Rin) := C(B(A(Rin))).
+
+Notice that the series circuit is not defined in terms of a boolean AND.
+That would work if you just had contacts, but for ops like TOF, whose
+rung-out can be true when their rung-ins are false, it breaks down.
+
+For a parallel circuit, for example sub-circuits A, B, and C in parallel,
+the rung-in condition for each of the sub-circuits is the rung-in
+condition for the whole circuit, and the rung-out condition is true if
+at least one of the rung-out conditions of the subcircuits is true. So
+if the whole parallel circuit is Y, then
+
+ Y(Rin) := A(Rin) || B(Rin) || C(Rin).
+
+For the series or parallel circuits, the sub-circuits A, B, or C need not
+be leaf elements; they could be parallel or series circuits themselves. In
+that case you would have to apply the appropriate definition, maybe
+recursively (you could have a series circuit that contains a parallel
+circuit that contains another parallel circuit). Once you have written
+the program in that form, as cascaded and OR'd simple functions, any
+textbook in compilers can tell you what to do with it, if it is not
+obvious already.
+
+As I said, though, there are many implementation details. For example,
+I currently support five different targets: PIC16, AVR, ANSI C code,
+interpretable byte code, and the simulator. To reduce the amount of work
+that I must do, I have a simple intermediate representation, with only
+a couple dozen ops. The ladder logic is compiled to intermediate code,
+and the intermediate code is then compiled to whatever I need by the
+appropriate back end. This intermediate code is designed to be as simple
+and as easy to compile as possible, to minimize the time required for me
+to maintain five back ends. That is one of the major reasons why LDmicro
+generates poor code; a more expressive intcode would make it possible
+to write better back ends, but at the cost of implementation time.
+
+If you would like to see how I compile ladder logic to a procedural
+language, then I would suggest that you look at the code generated by the
+ANSI C back end.
+
+
+Jonathan Westhues, Mar 2006, Oct 2007
+
diff --git a/ldmicro/Makefile b/ldmicro/Makefile
new file mode 100644
index 0000000..215e170
--- /dev/null
+++ b/ldmicro/Makefile
@@ -0,0 +1,77 @@
+DEFINES = /D_WIN32_WINNT=0x400 /DISOLATION_AWARE_ENABLED /D_WIN32_IE=0x400 /DWIN32_LEAN_AND_MEAN /DWIN32 /D$(D)
+CFLAGS = /W3 /nologo -I..\common\win32 /O2 /D_CRT_SECURE_NO_WARNINGS /D_DEBUG /Zi
+
+HEADERS = ..\common\win32\freeze.h ldmicro.h mcutable.h intcode.h
+
+OBJDIR = obj
+
+FREEZE = $(OBJDIR)\freeze.obj
+
+LDOBJS = $(OBJDIR)\ldmicro.obj \
+ $(OBJDIR)\maincontrols.obj \
+ $(OBJDIR)\helpdialog.obj \
+ $(OBJDIR)\schematic.obj \
+ $(OBJDIR)\draw.obj \
+ $(OBJDIR)\draw_outputdev.obj \
+ $(OBJDIR)\circuit.obj \
+ $(OBJDIR)\undoredo.obj \
+ $(OBJDIR)\loadsave.obj \
+ $(OBJDIR)\simulate.obj \
+ $(OBJDIR)\commentdialog.obj \
+ $(OBJDIR)\contactsdialog.obj \
+ $(OBJDIR)\coildialog.obj \
+ $(OBJDIR)\simpledialog.obj \
+ $(OBJDIR)\resetdialog.obj \
+ $(OBJDIR)\lutdialog.obj \
+ $(OBJDIR)\confdialog.obj \
+ $(OBJDIR)\iolist.obj \
+ $(OBJDIR)\miscutil.obj \
+ $(OBJDIR)\lang.obj \
+ $(OBJDIR)\intcode.obj \
+ $(OBJDIR)\compilecommon.obj \
+ $(OBJDIR)\ansic.obj \
+ $(OBJDIR)\interpreted.obj \
+ $(OBJDIR)\pic16.obj \
+ $(OBJDIR)\arduino.obj \
+ $(OBJDIR)\avr.obj
+
+HELPOBJ = $(OBJDIR)\helptext.obj
+
+LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib
+
+all: $(OBJDIR)/ldmicro.exe $(OBJDIR)/ldinterpret.exe
+ @cp $(OBJDIR)/ldmicro.exe .
+ @cp $(OBJDIR)/ldinterpret.exe .
+ @cd reg
+ @go.bat
+ @cd ..
+
+clean:
+ rm -f obj/*
+
+lang.cpp: $(OBJDIR)/lang-tables.h
+
+$(OBJDIR)/lang-tables.h: lang*.txt
+ perl lang-tables.pl > $(OBJDIR)/lang-tables.h
+
+$(OBJDIR)/ldinterpret.exe: ldinterpret.c
+ @$(CC) -Fe$(OBJDIR)/ldinterpret.exe $(LIBS) ldinterpret.c
+
+$(OBJDIR)/ldmicro.exe: $(LDOBJS) $(FREEZE) $(HELPOBJ) $(OBJDIR)/ldmicro.res
+ @$(CC) $(DEFINES) $(CFLAGS) -Fe$(OBJDIR)/ldmicro.exe $(LDOBJS) $(FREEZE) $(HELPOBJ) $(OBJDIR)/ldmicro.res $(LIBS)
+
+$(OBJDIR)/ldmicro.res: ldmicro.rc ldmicro.ico
+ @rc ldmicro.rc
+ @mv ldmicro.res $(OBJDIR)
+
+$(LDOBJS): $(@B).cpp $(HEADERS)
+ @$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj $(@B).cpp
+
+$(FREEZE): ..\common\win32\$(@B).cpp $(HEADERS)
+ @$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj ..\common\win32\$(@B).cpp
+
+$(HELPOBJ): $(OBJDIR)/helptext.cpp
+ @$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/helptext.obj $(OBJDIR)/helptext.cpp
+
+$(OBJDIR)/helptext.cpp: manual.txt manual-*.txt
+ perl txt2c.pl > $(OBJDIR)/helptext.cpp
diff --git a/ldmicro/README.txt b/ldmicro/README.txt
new file mode 100644
index 0000000..ead3ee9
--- /dev/null
+++ b/ldmicro/README.txt
@@ -0,0 +1,181 @@
+
+LDmicro is a ladder logic editor, simulator and compiler for 8-bit
+microcontrollers. It can generate native code for Atmel AVR and Microchip
+PIC16 CPUs from a ladder diagram.
+
+This program is free software; see COPYING.txt.
+
+I started work on LDmicro in October 2004.
+
+At present, I receive a steady stream of questions and feature requests,
+that I do not have time to address. I hope that others who find this
+program interesting or useful may be able to complete this work.
+
+
+BUILDING LDMICRO
+================
+
+LDmicro is built using the Microsoft Visual C++ compiler. If that is
+installed correctly, then you should be able to just run
+
+ make.bat
+
+and see everything build. The regression tests (however inadequate;
+see below) will run automatically after the program is built.
+
+Various source and header files are generated automatically. The perl
+scripts to do this are included with this distribution, but it's necessary
+to have a perl.exe in your path somewhere.
+
+The makefile accepts an argument, D=LANG_XX, where XX is the language
+code. make.bat supplies that argument automatically, as LANG_EN (English).
+
+
+HIGH-LEVEL ARCHITECTURE
+=======================
+
+LDmicro is a compiler. Unlike typical compilers, it does not accept a text
+file as its input; instead, the program is edited graphically, within
+LDmicro itself. The editor represents the program as a tree structure,
+where each node represents either an instruction (like relays contacts
+or a coil) or a series or parallel subcircuit.
+
+Based on this tree structure, intcode.cpp generates an intermediate
+`instruction list' representation of the program. This intermediate code
+is then passed to a processor-specific back-end, which generates code
+for the target.
+
+The program may also be simulated; in that case, rather than compiling
+the intermediate code to a .hex file, we interpret the intermediate code
+and display the results in real time.
+
+The intermediate code is useful, because there are fewer intermediate
+code ops than high-level ladder logic instructions.
+
+See INTERNALS.txt for some discussion of LDmicro's internals.
+
+
+POSSIBLE ENHANCEMENTS
+=====================
+
+If you are interested in contributing to LDmicro, then I believe that
+the following work would be useful. The list is in ascending order
+of difficulty.
+
+ * Better regression tests. There is a mechanism for this now (see the
+ reg/... directory), but very few tests. A regression test is just
+ an LDmicro program; I verify that the same input .ld file generates
+ the same output .hex file. The code generators are stupid enough
+ that these don't break all that often (no fancy optimizers that
+ reorder everything based on a small change to the input).
+
+ * Thorough tests of the existing microcontroller targets. This
+ requires appropriate hardware to test against. When I don't have
+ the hardware myself, I will sometimes test against a simulator,
+ but this is not as good as the real thing. The simulation of
+ peripherals is often approximate.
+
+ Even if no bugs are found, the test programs can become regression
+ tests, and we can mark those targets that have been tested as
+ `known good'.
+
+ * More targets for PIC16 and AVR architectures. In general, this
+ should be very easy; just add the appropriate entries in
+ mcutable.h. Some small changes to pic16.cpp or avr.cpp may be
+ required, if the peripheral registers move around.
+
+ I am not interested in new targets that have not been tested
+ against real hardware.
+
+ * The code generator has regression tests now. It would be nice to
+ also have regression tests for the display code. We could do this
+ using the "Export As Text" logic, since that calls the exact same
+ code that's used to display the program on-screen.
+
+ * Bitwise instructions that work on integers (e.g., let internal
+ relay Rfoo equal the seventh bit of integer variable x, set c =
+ a bitwise-and b, etc).
+
+ This would require new intermediate code ops, and thus changes to
+ both the PIC and AVR back ends, and thus testing of those changes.
+ Aside from that, it's very straightforward.
+
+ These new intcode ops could then be used by higher-level ELEM_XXX
+ ops, for example to do bit-banged synchronous serial protocols
+ (I2C, SPI), or to talk to an HD44780-type LCD.
+
+ * Intermediate code ops for look-up tables. At present, these are
+ implemented stupidly, as a chain of if-then statements. This would
+ require changes to all the code generators.
+
+ Look-up tables are important, not just for the `look-up table'
+ instruction, but also for the `formatted string over serial'
+ instruction, where the string is stored in a look-up table.
+
+ * New architecture targets. An architecture target translates from
+ LDmicro's intermediate code (intcode) to a binary for that
+ architecture. This means that it includes a code generator and
+ an assembler. The PIC16 and AVR targets are about 1500 lines of
+ code each.
+
+ A PIC18 target would be nice. I am not interested in a PIC18
+ back-end that does not exploit the additional addressing modes
+ and instructions (like rjmps); it would be easy to hack up the
+ PIC16 back end to generate something that would run on a PIC18,
+ but not useful.
+
+ An 8051 target is the other obvious choice, maybe also for the
+ MSP430.
+
+ * A Linux port. I am told that the current version runs perfectly
+ under WINE, but a native GTK port would be nice. That would be
+ a lot of work.
+
+ I am not interested in porting to a cross-platform GUI toolkit, and
+ thus abandoning the native Win32 user interface. In general, I think
+ that those toolkits end up looking equally wrong on all platforms.
+
+ If I were to undertake this myself, then I would probably end up
+ writing a miniature compatibility layer for the widgets that I
+ actually use, that mapped on to GTK under Linux and Win32 under
+ Windows, and so on. I've seen attempts at general-purpose libraries
+ to do that, but they look extremely complex for what they do.
+
+In general, I am not interested in changes that involve linking against
+non-standard libraries. (By non-standard, I mean anything that's not
+distributed with the operating system.)
+
+
+FOREIGN-LANGUAGE TRANSLATIONS
+=============================
+
+Foreign-language translations of the documentation and user interface
+are always useful. At present, we have English, German, and French.
+
+These do not require changes to the source code; a lookup table of
+translated strings is automatically generated from lang-*.txt during
+the build process. If you are interested in preparing a translation,
+but do not wish to set up tools to build LDmicro, then just mail me the
+lang-xx.txt, and I can build ldmicro-xx.exe for you.
+
+At present, LDmicro does not use Unicode. This means that translations
+into languages not written in the Latin alphabet are tricky. This could
+be changed, of course.
+
+
+FINAL
+=====
+
+I will always respond to bug reports. If LDmicro is behaving incorrectly
+on some target, then please attach the simplest example of a program
+that's misbehaving, and a description of the incorrect behavior.
+
+Please contact me if you have any questions.
+
+
+Jonathan Westhues
+user jwesthues, at host cq.cx
+
+near Seattle, Oct 21, 2007
+
+
diff --git a/ldmicro/ansic.cpp b/ldmicro/ansic.cpp
new file mode 100644
index 0000000..c376a6b
--- /dev/null
+++ b/ldmicro/ansic.cpp
@@ -0,0 +1,427 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Write the program as ANSI C source. This is very simple, because the
+// intermediate code structure is really a lot like C. Someone else will be
+// responsible for calling us with appropriate timing.
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+#include
+
+#include "ldmicro.h"
+#include "intcode.h"
+
+static char SeenVariables[MAX_IO][MAX_NAME_LEN];
+int SeenVariablesCount;
+
+//-----------------------------------------------------------------------------
+// Have we seen a variable before? If not then no need to generate code for
+// it, otherwise we will have to make a declaration, and mark it as seen.
+//-----------------------------------------------------------------------------
+static BOOL SeenVariable(char *name)
+{
+ int i;
+ for(i = 0; i < SeenVariablesCount; i++) {
+ if(strcmp(SeenVariables[i], name)==0) {
+ return TRUE;
+ }
+ }
+ if(i >= MAX_IO) oops();
+ strcpy(SeenVariables[i], name);
+ SeenVariablesCount++;
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Turn an internal symbol into a C name; only trick is that internal symbols
+// use $ for symbols that the int code generator needed for itself, so map
+// that into something okay for C.
+//-----------------------------------------------------------------------------
+#define ASBIT 1
+#define ASINT 2
+static char *MapSym(char *str, int how)
+{
+ if(!str) return NULL;
+
+ static char AllRets[16][MAX_NAME_LEN+30];
+ static int RetCnt;
+
+ RetCnt = (RetCnt + 1) & 15;
+
+ char *ret = AllRets[RetCnt];
+
+ // The namespace for bit and integer variables is distinct.
+ char bit_int;
+ if(how == ASBIT) {
+ bit_int = 'b';
+ } else if(how == ASINT) {
+ bit_int = 'i';
+ } else {
+ oops();
+ }
+
+ // User and internal symbols are distinguished.
+ if(*str == '$') {
+ sprintf(ret, "I_%c_%s", bit_int, str+1);
+ } else {
+ sprintf(ret, "U_%c_%s", bit_int, str);
+ }
+ return ret;
+}
+
+//-----------------------------------------------------------------------------
+// Generate a declaration for an integer var; easy, a static 16-bit qty.
+//-----------------------------------------------------------------------------
+static void DeclareInt(FILE *f, char *str)
+{
+ fprintf(f, "STATIC SWORD %s = 0;\n", str);
+}
+
+//-----------------------------------------------------------------------------
+// Generate a declaration for a bit var; three cases, input, output, and
+// internal relay. An internal relay is just a BOOL variable, but for an
+// input or an output someone else must provide read/write functions.
+//-----------------------------------------------------------------------------
+static void DeclareBit(FILE *f, char *str)
+{
+ // The mapped symbol has the form U_b_{X,Y,R}name, so look at character
+ // four to determine if it's an input, output, internal relay.
+ if(str[4] == 'X') {
+ fprintf(f, "\n");
+ fprintf(f, "/* You provide this function. */\n");
+ fprintf(f, "PROTO(extern BOOL Read_%s(void);)\n", str);
+ fprintf(f, "\n");
+ } else if(str[4] == 'Y') {
+ fprintf(f, "\n");
+ fprintf(f, "/* You provide these functions. */\n");
+ fprintf(f, "PROTO(BOOL Read_%s(void);)\n", str);
+ fprintf(f, "PROTO(void Write_%s(BOOL v);)\n", str);
+ fprintf(f, "\n");
+ } else {
+ fprintf(f, "STATIC BOOL %s = 0;\n", str);
+ fprintf(f, "#define Read_%s() %s\n", str, str);
+ fprintf(f, "#define Write_%s(x) %s = x\n", str, str);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Generate declarations for all the 16-bit/single bit variables in the ladder
+// program.
+//-----------------------------------------------------------------------------
+static void GenerateDeclarations(FILE *f)
+{
+ int i;
+ for(i = 0; i < IntCodeLen; i++) {
+ char *bitVar1 = NULL, *bitVar2 = NULL;
+ char *intVar1 = NULL, *intVar2 = NULL, *intVar3 = NULL;
+
+ switch(IntCode[i].op) {
+ case INT_SET_BIT:
+ case INT_CLEAR_BIT:
+ bitVar1 = IntCode[i].name1;
+ break;
+
+ case INT_COPY_BIT_TO_BIT:
+ bitVar1 = IntCode[i].name1;
+ bitVar2 = IntCode[i].name2;
+ break;
+
+ case INT_SET_VARIABLE_TO_LITERAL:
+ intVar1 = IntCode[i].name1;
+ break;
+
+ case INT_SET_VARIABLE_TO_VARIABLE:
+ intVar1 = IntCode[i].name1;
+ intVar2 = IntCode[i].name2;
+ break;
+
+ case INT_SET_VARIABLE_DIVIDE:
+ case INT_SET_VARIABLE_MULTIPLY:
+ case INT_SET_VARIABLE_SUBTRACT:
+ case INT_SET_VARIABLE_ADD:
+ intVar1 = IntCode[i].name1;
+ intVar2 = IntCode[i].name2;
+ intVar3 = IntCode[i].name3;
+ break;
+
+ case INT_INCREMENT_VARIABLE:
+ case INT_READ_ADC:
+ case INT_SET_PWM:
+ intVar1 = IntCode[i].name1;
+ break;
+
+ case INT_UART_RECV:
+ case INT_UART_SEND:
+ intVar1 = IntCode[i].name1;
+ bitVar1 = IntCode[i].name2;
+ break;
+
+ case INT_IF_BIT_SET:
+ case INT_IF_BIT_CLEAR:
+ bitVar1 = IntCode[i].name1;
+ break;
+
+ case INT_IF_VARIABLE_LES_LITERAL:
+ intVar1 = IntCode[i].name1;
+ break;
+
+ case INT_IF_VARIABLE_EQUALS_VARIABLE:
+ case INT_IF_VARIABLE_GRT_VARIABLE:
+ intVar1 = IntCode[i].name1;
+ intVar2 = IntCode[i].name2;
+ break;
+
+ case INT_END_IF:
+ case INT_ELSE:
+ case INT_COMMENT:
+ case INT_SIMULATE_NODE_STATE:
+ case INT_EEPROM_BUSY_CHECK:
+ case INT_EEPROM_READ:
+ case INT_EEPROM_WRITE:
+ break;
+
+ default:
+ oops();
+ }
+ bitVar1 = MapSym(bitVar1, ASBIT);
+ bitVar2 = MapSym(bitVar2, ASBIT);
+
+ intVar1 = MapSym(intVar1, ASINT);
+ intVar2 = MapSym(intVar2, ASINT);
+ intVar3 = MapSym(intVar3, ASINT);
+
+ if(bitVar1 && !SeenVariable(bitVar1)) DeclareBit(f, bitVar1);
+ if(bitVar2 && !SeenVariable(bitVar2)) DeclareBit(f, bitVar2);
+
+ if(intVar1 && !SeenVariable(intVar1)) DeclareInt(f, intVar1);
+ if(intVar2 && !SeenVariable(intVar2)) DeclareInt(f, intVar2);
+ if(intVar3 && !SeenVariable(intVar3)) DeclareInt(f, intVar3);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Actually generate the C source for the program.
+//-----------------------------------------------------------------------------
+static void GenerateAnsiC(FILE *f)
+{
+ int i;
+ int indent = 1;
+ for(i = 0; i < IntCodeLen; i++) {
+
+ if(IntCode[i].op == INT_END_IF) indent--;
+ if(IntCode[i].op == INT_ELSE) indent--;
+
+ int j;
+ for(j = 0; j < indent; j++) fprintf(f, " ");
+
+ switch(IntCode[i].op) {
+ case INT_SET_BIT:
+ fprintf(f, "Write_%s(1);\n", MapSym(IntCode[i].name1, ASBIT));
+ break;
+
+ case INT_CLEAR_BIT:
+ fprintf(f, "Write_%s(0);\n", MapSym(IntCode[i].name1, ASBIT));
+ break;
+
+ case INT_COPY_BIT_TO_BIT:
+ fprintf(f, "Write_%s(Read_%s());\n",
+ MapSym(IntCode[i].name1, ASBIT),
+ MapSym(IntCode[i].name2, ASBIT));
+ break;
+
+ case INT_SET_VARIABLE_TO_LITERAL:
+ fprintf(f, "%s = %d;\n", MapSym(IntCode[i].name1, ASINT),
+ IntCode[i].literal);
+ break;
+
+ case INT_SET_VARIABLE_TO_VARIABLE:
+ fprintf(f, "%s = %s;\n", MapSym(IntCode[i].name1, ASINT),
+ MapSym(IntCode[i].name2, ASINT));
+ break;
+
+ {
+ char op;
+ case INT_SET_VARIABLE_ADD: op = '+'; goto arith;
+ case INT_SET_VARIABLE_SUBTRACT: op = '-'; goto arith;
+ case INT_SET_VARIABLE_MULTIPLY: op = '*'; goto arith;
+ case INT_SET_VARIABLE_DIVIDE: op = '/'; goto arith;
+ arith:
+ fprintf(f, "%s = %s %c %s;\n",
+ MapSym(IntCode[i].name1, ASINT),
+ MapSym(IntCode[i].name2, ASINT),
+ op,
+ MapSym(IntCode[i].name3, ASINT) );
+ break;
+ }
+
+ case INT_INCREMENT_VARIABLE:
+ fprintf(f, "%s++;\n", MapSym(IntCode[i].name1, ASINT));
+ break;
+
+ case INT_IF_BIT_SET:
+ fprintf(f, "if(Read_%s()) {\n",
+ MapSym(IntCode[i].name1, ASBIT));
+ indent++;
+ break;
+
+ case INT_IF_BIT_CLEAR:
+ fprintf(f, "if(!Read_%s()) {\n",
+ MapSym(IntCode[i].name1, ASBIT));
+ indent++;
+ break;
+
+ case INT_IF_VARIABLE_LES_LITERAL:
+ fprintf(f, "if(%s < %d) {\n", MapSym(IntCode[i].name1, ASINT),
+ IntCode[i].literal);
+ indent++;
+ break;
+
+ case INT_IF_VARIABLE_EQUALS_VARIABLE:
+ fprintf(f, "if(%s == %s) {\n", MapSym(IntCode[i].name1, ASINT),
+ MapSym(IntCode[i].name2, ASINT));
+ indent++;
+ break;
+
+ case INT_IF_VARIABLE_GRT_VARIABLE:
+ fprintf(f, "if(%s > %s) {\n", MapSym(IntCode[i].name1, ASINT),
+ MapSym(IntCode[i].name2, ASINT));
+ indent++;
+ break;
+
+ case INT_END_IF:
+ fprintf(f, "}\n");
+ break;
+
+ case INT_ELSE:
+ fprintf(f, "} else {\n"); indent++;
+ break;
+
+ case INT_SIMULATE_NODE_STATE:
+ // simulation-only
+ fprintf(f, "\n");
+ break;
+
+ case INT_COMMENT:
+ if(IntCode[i].name1[0]) {
+ fprintf(f, "/* %s */\n", IntCode[i].name1);
+ } else {
+ fprintf(f, "\n");
+ }
+ break;
+
+ case INT_EEPROM_BUSY_CHECK:
+ case INT_EEPROM_READ:
+ case INT_EEPROM_WRITE:
+ case INT_READ_ADC:
+ case INT_SET_PWM:
+ case INT_UART_RECV:
+ case INT_UART_SEND:
+ Error(_("ANSI C target does not support peripherals "
+ "(UART, PWM, ADC, EEPROM). Skipping that instruction."));
+ break;
+
+ default:
+ oops();
+ }
+ }
+}
+
+void CompileAnsiC(char *dest)
+{
+ SeenVariablesCount = 0;
+
+ FILE *f = fopen(dest, "w");
+ if(!f) {
+ Error(_("Couldn't open file '%s'"), dest);
+ return;
+ }
+
+ fprintf(f,
+"/* This is auto-generated code from LDmicro. Do not edit this file! Go\n"
+" back to the ladder diagram source for changes in the logic, and make\n"
+" any C additions either in ladder.h or in additional .c files linked\n"
+" against this one. */\n"
+"\n"
+"/* You must provide ladder.h; there you must provide:\n"
+" * a typedef for SWORD and BOOL, signed 16 bit and boolean types\n"
+" (probably typedef signed short SWORD; typedef unsigned char BOOL;)\n"
+"\n"
+" You must also provide implementations of all the I/O read/write\n"
+" either as inlines in the header file or in another source file. (The\n"
+" I/O functions are all declared extern.)\n"
+"\n"
+" See the generated source code (below) for function names. */\n"
+"#include \"ladder.h\"\n"
+"\n"
+"/* Define EXTERN_EVERYTHING in ladder.h if you want all symbols extern.\n"
+" This could be useful to implement `magic variables,' so that for\n"
+" example when you write to the ladder variable duty_cycle, your PLC\n"
+" runtime can look at the C variable U_duty_cycle and use that to set\n"
+" the PWM duty cycle on the micro. That way you can add support for\n"
+" peripherals that LDmicro doesn't know about. */\n"
+"#ifdef EXTERN_EVERYTHING\n"
+"#define STATIC \n"
+"#else\n"
+"#define STATIC static\n"
+"#endif\n"
+"\n"
+"/* Define NO_PROTOTYPES if you don't want LDmicro to provide prototypes for\n"
+" all the I/O functions (Read_U_xxx, Write_U_xxx) that you must provide.\n"
+" If you define this then you must provide your own prototypes for these\n"
+" functions in ladder.h, or provide definitions (e.g. as inlines or macros)\n"
+" for them in ladder.h. */\n"
+"#ifdef NO_PROTOTYPES\n"
+"#define PROTO(x)\n"
+"#else\n"
+"#define PROTO(x) x\n"
+"#endif\n"
+"\n"
+"/* U_xxx symbols correspond to user-defined names. There is such a symbol\n"
+" for every internal relay, variable, timer, and so on in the ladder\n"
+" program. I_xxx symbols are internally generated. */\n"
+ );
+
+ // now generate declarations for all variables
+ GenerateDeclarations(f);
+
+ fprintf(f,
+"\n"
+"\n"
+"/* Call this function once per PLC cycle. You are responsible for calling\n"
+" it at the interval that you specified in the MCU configuration when you\n"
+" generated this code. */\n"
+"void PlcCycle(void)\n"
+"{\n"
+ );
+
+ GenerateAnsiC(f);
+
+ fprintf(f, "}\n");
+ fclose(f);
+
+ char str[MAX_PATH+500];
+ sprintf(str, _("Compile successful; wrote C source code to '%s'.\r\n\r\n"
+ "This is not a complete C program. You have to provide the runtime "
+ "and all the I/O routines. See the comments in the source code for "
+ "information about how to do this."), dest);
+ CompileSuccessfulMessage(str);
+}
diff --git a/ldmicro/arduino.cpp b/ldmicro/arduino.cpp
new file mode 100644
index 0000000..e6453d0
--- /dev/null
+++ b/ldmicro/arduino.cpp
@@ -0,0 +1,1381 @@
+//-----------------------------------------------------------------------------
+// An AVR assembler, for our own internal use, plus routines to generate
+// code from the ladder logic structure, plus routines to generate the
+// runtime needed to schedule the cycles.
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "ldmicro.h"
+#include "intcode.h"
+
+// not complete; just what I need
+typedef enum ArduinoOpTag {
+ OP_VACANT,
+ OP_ADC,
+ OP_ADD,
+ OP_ASR,
+ OP_BRCC,
+ OP_BRCS,
+ OP_BREQ,
+ OP_BRGE,
+ OP_BRLO,
+ OP_BRLT,
+ OP_BRNE,
+ OP_CBR,
+ OP_CLC,
+ OP_CLR,
+ OP_COM,
+ OP_CP,
+ OP_CPC,
+ OP_DEC,
+ OP_EOR,
+ OP_ICALL,
+ OP_IJMP,
+ OP_INC,
+ OP_LDI,
+ OP_LD_X,
+ OP_MOV,
+ OP_OUT,
+ OP_RCALL,
+ OP_RET,
+ OP_RETI,
+ OP_RJMP,
+ OP_ROR,
+ OP_SEC,
+ OP_SBC,
+ OP_SBCI,
+ OP_SBR,
+ OP_SBRC,
+ OP_SBRS,
+ OP_ST_X,
+ OP_SUB,
+ OP_SUBI,
+ OP_TST,
+ OP_WDR,
+} ArduinoOp;
+
+typedef struct ArduinoInstructionTag {
+ ArduinoOp op;
+ DWORD arg1;
+ DWORD arg2;
+} ArduinoInstruction;
+
+#define MAX_PROGRAM_LEN 128*1024
+static ArduinoInstruction ArduinoProg[MAX_PROGRAM_LEN];
+static DWORD ArduinoProgWriteP;
+
+// For yet unresolved references in jumps
+static DWORD FwdAddrCount;
+
+// Fancier: can specify a forward reference to the high or low octet of a
+// 16-bit address, which is useful for indirect jumps.
+#define FWD_LO(x) ((x) | 0x20000000)
+#define FWD_HI(x) ((x) | 0x40000000)
+
+// Address to jump to when we finish one PLC cycle
+static DWORD BeginningOfCycleAddr;
+
+// Address of the multiply subroutine, and whether we will have to include it
+static DWORD MultiplyAddress;
+static BOOL MultiplyUsed;
+// and also divide
+static DWORD DivideAddress;
+static BOOL DivideUsed;
+
+// For EEPROM: we queue up characters to send in 16-bit words (corresponding
+// to the integer variables), but we can actually just program 8 bits at a
+// time, so we need to store the high byte somewhere while we wait.
+static DWORD EepromHighByte;
+static DWORD EepromHighByteWaitingAddr;
+static int EepromHighByteWaitingBit;
+
+// Some useful registers, unfortunately many of which are in different places
+// on different AVRs! I consider this a terrible design choice by Atmel.
+static DWORD REG_TIMSK1; //====Need to come back to
+//static DWORD REG_TIFR0; //====Need to come back to
+static DWORD REG_TIFR1; //====Need to come back to
+//static DWORD REG_TIFR2; //====Need to come back to
+#define REG_OCR1AH 0x89
+#define REG_OCR1AL 0x88
+#define REG_TCCR1A 0x80
+#define REG_TCCR1B 0x81
+#define REG_TCCR1C 0x82 //====Newly added. Need to check working
+#define REG_SPH 0x5e
+#define REG_SPL 0x5d
+#define REG_ADMUX 0x7C
+#define REG_ADCSRA 0x7A
+//#define REG_ADCSRB 0x7B //====Newly added. Need to check if needed
+#define REG_ADCL 0x78
+#define REG_ADCH 0x79
+
+static DWORD REG_UBRRH; //====Need to come back to
+static DWORD REG_UBRRL; //====Need to come back to
+//static DWORD REG_UCSRC; //====Need to come back to
+static DWORD REG_UCSRB; //====Need to come back to
+static DWORD REG_UCSRA; //====Need to come back to
+static DWORD REG_UDR; //====Need to come back to ---------------------------------------------------------------->3:39 PM 4/10/2015
+
+#define REG_OCR2A 0xB3
+//#define REG_OCR2B 0xB4 //====Newly added. Need to check if needed and use
+#define REG_TCCR2A 0xB0
+#define REG_TCCR2B 0xB1 //====Newly added. Need to check if needed
+
+#define REG_EEARH 0x42
+#define REG_EEARL 0x41
+#define REG_EEDR 0x40
+#define REG_EECR 0x3F
+
+
+static int IntPc;
+
+static void CompileFromIntermediate(void);
+
+//-----------------------------------------------------------------------------
+// Wipe the program and set the write pointer back to the beginning. Also
+// flush all the state of the register allocators etc.
+//-----------------------------------------------------------------------------
+static void WipeMemory(void)
+{
+ memset(ArduinoProg, 0, sizeof(ArduinoProg));
+ ArduinoProgWriteP = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Store an instruction at the next spot in program memory. Error condition
+// if this spot is already filled. We don't actually assemble to binary yet;
+// there may be references to resolve.
+//-----------------------------------------------------------------------------
+static void Instruction(ArduinoOp op, DWORD arg1, DWORD arg2)
+{
+ if(ArduinoProg[ArduinoProgWriteP].op != OP_VACANT) oops();
+
+ ArduinoProg[ArduinoProgWriteP].op = op;
+ ArduinoProg[ArduinoProgWriteP].arg1 = arg1;
+ ArduinoProg[ArduinoProgWriteP].arg2 = arg2;
+ ArduinoProgWriteP++;
+}
+
+//-----------------------------------------------------------------------------
+// Allocate a unique descriptor for a forward reference. Later that forward
+// reference gets assigned to an absolute address, and we can go back and
+// fix up the reference.
+//-----------------------------------------------------------------------------
+static DWORD AllocFwdAddr(void)
+{
+ FwdAddrCount++;
+ return 0x80000000 | FwdAddrCount;
+}
+
+//-----------------------------------------------------------------------------
+// Go back and fix up the program given that the provided forward address
+// corresponds to the next instruction to be assembled.
+//-----------------------------------------------------------------------------
+static void FwdAddrIsNow(DWORD addr)
+{
+ if(!(addr & 0x80000000)) oops();
+
+ DWORD i;
+ for(i = 0; i < ArduinoProgWriteP; i++) {
+ if(ArduinoProg[i].arg1 == addr) {
+ ArduinoProg[i].arg1 = ArduinoProgWriteP;
+ } else if(ArduinoProg[i].arg2 == FWD_LO(addr)) {
+ ArduinoProg[i].arg2 = (ArduinoProgWriteP & 0xff);
+ } else if(ArduinoProg[i].arg2 == FWD_HI(addr)) {
+ ArduinoProg[i].arg2 = ArduinoProgWriteP >> 8;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Given an opcode and its operands, assemble the 16-bit instruction for the
+// AVR. Check that the operands do not have more bits set than is meaningful;
+// it is an internal error if they do not. Needs to know what address it is
+// being assembled to so that it generate relative jumps; internal error if
+// a relative jump goes out of range.
+//-----------------------------------------------------------------------------
+static DWORD Assemble(DWORD addrAt, ArduinoOp op, DWORD arg1, DWORD arg2)
+{
+#define CHECK(v, bits) if((v) != ((v) & ((1 << (bits))-1))) oops()
+ switch(op) {
+ case OP_ASR:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (9 << 12) | (2 << 9) | (arg1 << 4) | 5;
+
+ case OP_ROR:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (9 << 12) | (2 << 9) | (arg1 << 4) | 7;
+
+ case OP_ADD:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (3 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_ADC:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (7 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_EOR:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (9 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_SUB:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (6 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_SBC:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (2 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_CP:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (5 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_CPC:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (1 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_COM:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (9 << 12) | (2 << 9) | (arg1 << 4);
+
+ case OP_SBR:
+ CHECK(arg1, 5); CHECK(arg2, 8);
+ if(!(arg1 & 0x10)) oops();
+ arg1 &= ~0x10;
+ return (6 << 12) | ((arg2 & 0xf0) << 4) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_CBR:
+ CHECK(arg1, 5); CHECK(arg2, 8);
+ if(!(arg1 & 0x10)) oops();
+ arg1 &= ~0x10;
+ arg2 = ~arg2;
+ return (7 << 12) | ((arg2 & 0xf0) << 4) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_INC:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (0x4a << 9) | (arg1 << 4) | 3;
+
+ case OP_DEC:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (0x4a << 9) | (arg1 << 4) | 10;
+
+ case OP_SUBI:
+ CHECK(arg1, 5); CHECK(arg2, 8);
+ if(!(arg1 & 0x10)) oops();
+ arg1 &= ~0x10;
+ return (5 << 12) | ((arg2 & 0xf0) << 4) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_SBCI:
+ CHECK(arg1, 5); CHECK(arg2, 8);
+ if(!(arg1 & 0x10)) oops();
+ arg1 &= ~0x10;
+ return (4 << 12) | ((arg2 & 0xf0) << 4) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_TST:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (8 << 10) | ((arg1 & 0x10) << 4) | ((arg1 & 0x10) << 5) |
+ ((arg1 & 0xf) << 4) | (arg1 & 0xf);
+
+ case OP_SEC:
+ CHECK(arg1, 0); CHECK(arg2, 0);
+ return 0x9408;
+
+ case OP_CLC:
+ CHECK(arg1, 0); CHECK(arg2, 0);
+ return 0x9488;
+
+ case OP_IJMP:
+ CHECK(arg1, 0); CHECK(arg2, 0);
+ return 0x9409;
+
+ case OP_ICALL:
+ CHECK(arg1, 0); CHECK(arg2, 0);
+ return 0x9509;
+
+ case OP_RJMP:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 2047 || ((int)arg1) < -2048) oops();
+ arg1 &= (4096-1);
+ return (12 << 12) | arg1;
+
+ case OP_RCALL:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 2047 || ((int)arg1) < -2048) oops();
+ arg1 &= (4096-1);
+ return (13 << 12) | arg1;
+
+ case OP_RETI:
+ return 0x9518;
+
+ case OP_RET:
+ return 0x9508;
+
+ case OP_SBRC:
+ CHECK(arg1, 5); CHECK(arg2, 3);
+ return (0x7e << 9) | (arg1 << 4) | arg2;
+
+ case OP_SBRS:
+ CHECK(arg1, 5); CHECK(arg2, 3);
+ return (0x7f << 9) | (arg1 << 4) | arg2;
+
+ case OP_BREQ:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 63 || ((int)arg1) < -64) oops();
+ arg1 &= (128-1);
+ return (0xf << 12) | (arg1 << 3) | 1;
+
+ case OP_BRNE:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 63 || ((int)arg1) < -64) oops();
+ arg1 &= (128-1);
+ return (0xf << 12) | (1 << 10) | (arg1 << 3) | 1;
+
+ case OP_BRLO:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 63 || ((int)arg1) < -64) oops();
+ arg1 &= (128-1);
+ return (0xf << 12) | (arg1 << 3);
+
+ case OP_BRGE:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 63 || ((int)arg1) < -64) oops();
+ arg1 &= (128-1);
+ return (0xf << 12) | (1 << 10) | (arg1 << 3) | 4;
+
+ case OP_BRLT:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 63 || ((int)arg1) < -64) oops();
+ arg1 &= (128-1);
+ return (0xf << 12) | (arg1 << 3) | 4;
+
+ case OP_BRCC:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 63 || ((int)arg1) < -64) oops();
+ arg1 &= (128-1);
+ return (0xf << 12) | (1 << 10) | (arg1 << 3);
+
+ case OP_BRCS:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 63 || ((int)arg1) < -64) oops();
+ arg1 &= (128-1);
+ return (0xf << 12) | (arg1 << 3);
+
+ case OP_MOV:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (0xb << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_LDI:
+ CHECK(arg1, 5); CHECK(arg2, 8);
+ if(!(arg1 & 0x10)) oops();
+ arg1 &= ~0x10;
+ return (0xe << 12) | ((arg2 & 0xf0) << 4) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_LD_X:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (9 << 12) | (arg1 << 4) | 12;
+
+ case OP_ST_X:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (0x49 << 9) | (arg1 << 4) | 12;
+
+ case OP_WDR:
+ CHECK(arg1, 0); CHECK(arg2, 0);
+ return 0x95a8;
+
+ default:
+ oops();
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Write an intel IHEX format description of the program assembled so far.
+// This is where we actually do the assembly to binary format.
+//-----------------------------------------------------------------------------
+static void WriteHexFile(FILE *f)
+{
+ BYTE soFar[16];
+ int soFarCount = 0;
+ DWORD soFarStart = 0;
+
+ DWORD i;
+ for(i = 0; i < ArduinoProgWriteP; i++) {
+ DWORD w = Assemble(i, ArduinoProg[i].op, ArduinoProg[i].arg1, ArduinoProg[i].arg2);
+
+ if(soFarCount == 0) soFarStart = i;
+ soFar[soFarCount++] = (BYTE)(w & 0xff);
+ soFar[soFarCount++] = (BYTE)(w >> 8);
+
+ if(soFarCount >= 0x10 || i == (ArduinoProgWriteP-1)) {
+ StartIhex(f);
+ WriteIhex(f, soFarCount);
+ WriteIhex(f, (BYTE)((soFarStart*2) >> 8));
+ WriteIhex(f, (BYTE)((soFarStart*2) & 0xff));
+ WriteIhex(f, 0x00);
+ int j;
+ for(j = 0; j < soFarCount; j++) {
+ WriteIhex(f, soFar[j]);
+ }
+ FinishIhex(f);
+ soFarCount = 0;
+ }
+ }
+
+ // end of file record
+ fprintf(f, ":00000001FF\n");
+}
+
+//-----------------------------------------------------------------------------
+// Make sure that the given address is loaded in the X register; might not
+// have to update all of it.
+//-----------------------------------------------------------------------------
+static void LoadXAddr(DWORD addr)
+{
+ Instruction(OP_LDI, 27, (addr >> 8));
+ Instruction(OP_LDI, 26, (addr & 0xff));
+}
+
+//-----------------------------------------------------------------------------
+// Generate code to write an 8-bit value to a particular register.
+//-----------------------------------------------------------------------------
+static void WriteMemory(DWORD addr, BYTE val)
+{
+ LoadXAddr(addr);
+ // load r16 with the data
+ Instruction(OP_LDI, 16, val);
+ // do the store
+ Instruction(OP_ST_X, 16, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Copy just one bit from one place to another.
+//-----------------------------------------------------------------------------
+static void CopyBit(DWORD addrDest, int bitDest, DWORD addrSrc, int bitSrc)
+{
+ LoadXAddr(addrSrc); Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrDest); Instruction(OP_LD_X, 17, 0);
+ Instruction(OP_SBRS, 16, bitSrc);
+ Instruction(OP_CBR, 17, (1 << bitDest));
+ Instruction(OP_SBRC, 16, bitSrc);
+ Instruction(OP_SBR, 17, (1 << bitDest));
+
+ Instruction(OP_ST_X, 17, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Execute the next instruction only if the specified bit of the specified
+// memory location is clear (i.e. skip if set).
+//-----------------------------------------------------------------------------
+static void IfBitClear(DWORD addr, int bit)
+{
+ LoadXAddr(addr);
+ Instruction(OP_LD_X, 16, 0);
+ Instruction(OP_SBRS, 16, bit);
+}
+
+//-----------------------------------------------------------------------------
+// Execute the next instruction only if the specified bit of the specified
+// memory location is set (i.e. skip if clear).
+//-----------------------------------------------------------------------------
+static void IfBitSet(DWORD addr, int bit)
+{
+ LoadXAddr(addr);
+ Instruction(OP_LD_X, 16, 0);
+ Instruction(OP_SBRC, 16, bit);
+}
+
+//-----------------------------------------------------------------------------
+// Set a given bit in an arbitrary (not necessarily I/O memory) location in
+// memory.
+//-----------------------------------------------------------------------------
+static void SetBit(DWORD addr, int bit)
+{
+ LoadXAddr(addr);
+ Instruction(OP_LD_X, 16, 0);
+ Instruction(OP_SBR, 16, (1 << bit));
+ Instruction(OP_ST_X, 16, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Clear a given bit in an arbitrary (not necessarily I/O memory) location in
+// memory.
+//-----------------------------------------------------------------------------
+static void ClearBit(DWORD addr, int bit)
+{
+ LoadXAddr(addr);
+ Instruction(OP_LD_X, 16, 0);
+ Instruction(OP_CBR, 16, (1 << bit));
+ Instruction(OP_ST_X, 16, 0);
+}
+
+//======================================================================================================>2:42 AM 3/29/2015>Akshay
+
+//-----------------------------------------------------------------------------
+// Configure AVR 16-bit Timer1 to do the timing for us.
+//-----------------------------------------------------------------------------
+static void ConfigureTimer1(int cycleTimeMicroseconds)
+{
+ int divisor = 1;
+ int countsPerCycle;
+ while(divisor <= 1024) {
+ int timerRate = (Prog.mcuClock / divisor); // hertz
+ double timerPeriod = 1e6 / timerRate; // timer period, us
+ countsPerCycle = ((int)(cycleTimeMicroseconds / timerPeriod)) - 1;
+
+ if(countsPerCycle < 1000) {
+ Error(_("Cycle time too fast; increase cycle time, or use faster "
+ "crystal."));
+ CompileError();
+ } else if(countsPerCycle > 0xffff) {
+ if(divisor >= 1024) {
+ Error(
+ _("Cycle time too slow; decrease cycle time, or use slower "
+ "crystal."));
+ CompileError();
+ }
+ } else {
+ break;
+ }
+
+ if(divisor == 1) divisor = 8;
+ else if(divisor == 8) divisor = 64;
+ else if(divisor == 64) divisor = 256;
+ else if(divisor == 256) divisor = 1024;
+ }
+ WriteMemory(REG_TCCR1A, 0x00); // WGM11=0, WGM10=0
+
+ int csn;
+ switch(divisor) {
+ case 1: csn = 1; break;
+ case 8: csn = 2; break;
+ case 64: csn = 3; break;
+ case 256: csn = 4; break;
+ case 1024: csn = 5; break;
+ default: oops();
+ }
+
+ WriteMemory(REG_TCCR1B, (1<<3) | csn); // WGM13=0, WGM12=1
+ WriteMemory(REG_TCCR1C, 0x00); // FOC1A=0, FOC1B=0
+
+ // `the high byte must be written before the low byte'
+ WriteMemory(REG_OCR1AH, (countsPerCycle - 1) >> 8);
+ WriteMemory(REG_OCR1AL, (countsPerCycle - 1) & 0xff);
+
+ // Okay, so many AVRs have a register called TIFR, but the meaning of
+ // the bits in that register varies from device to device...
+ WriteMemory(REG_TIMSK1, (1 << 1));
+}
+
+//-----------------------------------------------------------------------------
+// Write the basic runtime. We set up our reset vector, configure all the
+// I/O pins, then set up the timer that does the cycling. Next instruction
+// written after calling WriteRuntime should be first instruction of the
+// timer loop (i.e. the PLC logic cycle).
+//-----------------------------------------------------------------------------
+static void WriteRuntime(void)
+{
+ DWORD resetVector = AllocFwdAddr();
+
+ int i;
+ Instruction(OP_RJMP, resetVector, 0); // $0000, RESET
+ for(i = 0; i < 34; i++)
+ Instruction(OP_RETI, 0, 0);
+
+ FwdAddrIsNow(resetVector);
+
+ // set up the stack, which we use only when we jump to multiply/divide
+ // routine
+ WORD topOfMemory = (WORD)Prog.mcu->ram[0].start + Prog.mcu->ram[0].len - 1;
+ WriteMemory(REG_SPH, topOfMemory >> 8);
+ WriteMemory(REG_SPL, topOfMemory & 0xff);
+
+ // zero out the memory used for timers, internal relays, etc.
+ LoadXAddr(Prog.mcu->ram[0].start + Prog.mcu->ram[0].len);
+ Instruction(OP_LDI, 16, 0);
+ Instruction(OP_LDI, 18, (Prog.mcu->ram[0].len) & 0xff);
+ Instruction(OP_LDI, 19, (Prog.mcu->ram[0].len) >> 8);
+
+ DWORD loopZero = ArduinoProgWriteP;
+ Instruction(OP_SUBI, 26, 1);
+ Instruction(OP_SBCI, 27, 0);
+ Instruction(OP_ST_X, 16, 0);
+ Instruction(OP_SUBI, 18, 1);
+ Instruction(OP_SBCI, 19, 0);
+ Instruction(OP_TST, 18, 0);
+ Instruction(OP_BRNE, loopZero, 0);
+ Instruction(OP_TST, 19, 0);
+ Instruction(OP_BRNE, loopZero, 0);
+
+
+ // set up I/O pins
+ BYTE isInput[MAX_IO_PORTS], isOutput[MAX_IO_PORTS];
+ BuildDirectionRegisters(isInput, isOutput);
+
+ if(UartFunctionUsed()) {
+ if(Prog.baudRate == 0) {
+ Error(_("Zero baud rate not possible."));
+ return;
+ }
+
+ // bps = Fosc/(16*(X+1))
+ // bps*16*(X + 1) = Fosc
+ // X = Fosc/(bps*16)-1
+ // and round, don't truncate
+ int divisor = (Prog.mcuClock + Prog.baudRate*8)/(Prog.baudRate*16) - 1;
+
+ double actual = Prog.mcuClock/(16.0*(divisor+1));
+ double percentErr = 100*(actual - Prog.baudRate)/Prog.baudRate;
+
+ if(fabs(percentErr) > 2) {
+ ComplainAboutBaudRateError(divisor, actual, percentErr);
+ }
+ if(divisor > 4095) ComplainAboutBaudRateOverflow();
+
+ WriteMemory(REG_UBRRH, divisor >> 8);
+ WriteMemory(REG_UBRRL, divisor & 0xff);
+ WriteMemory(REG_UCSRB, (1 << 4) | (1 << 3)); // RXEN, TXEN ===>may need to change later on ucsr0b is the registername here
+
+ for(i = 0; i < Prog.mcu->pinCount; i++) {
+ if(Prog.mcu->pinInfo[i].pin == Prog.mcu->uartNeeds.txPin) {
+ McuIoPinInfo *iop = &(Prog.mcu->pinInfo[i]);
+ isOutput[iop->port - 'A'] |= (1 << iop->bit);
+ break;
+ }
+ }
+ if(i == Prog.mcu->pinCount) oops();
+ }
+
+ if(PwmFunctionUsed()) {
+ for(i = 0; i < Prog.mcu->pinCount; i++) {
+ if(Prog.mcu->pinInfo[i].pin == Prog.mcu->pwmNeedsPin) {
+ McuIoPinInfo *iop = &(Prog.mcu->pinInfo[i]);
+ isOutput[iop->port - 'A'] |= (1 << iop->bit);
+ break;
+ }
+ }
+ if(i == Prog.mcu->pinCount) oops();
+ }
+
+ for(i = 0; Prog.mcu->dirRegs[i] != 0; i++) {
+ if(Prog.mcu->dirRegs[i] == 0xff && Prog.mcu->outputRegs[i] == 0xff) {
+ // skip this one, dummy entry for MCUs with I/O ports not
+ // starting from A
+ } else {
+ WriteMemory(Prog.mcu->dirRegs[i], isOutput[i]);
+ // turn on the pull-ups, and drive the outputs low to start
+ WriteMemory(Prog.mcu->outputRegs[i], isInput[i]);
+ }
+ }
+
+
+ ConfigureTimer1(Prog.cycleTime);
+
+ // and now the generated PLC code will follow
+ BeginningOfCycleAddr = ArduinoProgWriteP;
+
+ // Okay, so many AVRs have a register called TIFR, but the meaning of
+ // the bits in that register varies from device to device...
+ int tifrBitForOCF1A;
+ tifrBitForOCF1A = 1;
+ DWORD now = ArduinoProgWriteP;
+ IfBitClear(REG_TIFR1, tifrBitForOCF1A);
+ Instruction(OP_RJMP, now, 0);
+
+ SetBit(REG_TIFR1, tifrBitForOCF1A);
+
+ Instruction(OP_WDR, 0, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Handle an IF statement. Flow continues to the first instruction generated
+// by this function if the condition is true, else it jumps to the given
+// address (which is an FwdAddress, so not yet assigned). Called with IntPc
+// on the IF statement, returns with IntPc on the END IF.
+//-----------------------------------------------------------------------------
+static void CompileIfBody(DWORD condFalse)
+{
+ IntPc++;
+ CompileFromIntermediate();
+ if(IntCode[IntPc].op == INT_ELSE) {
+ IntPc++;
+ DWORD endBlock = AllocFwdAddr();
+ Instruction(OP_RJMP, endBlock, 0);
+
+ FwdAddrIsNow(condFalse);
+ CompileFromIntermediate();
+ FwdAddrIsNow(endBlock);
+ } else {
+ FwdAddrIsNow(condFalse);
+ }
+
+ if(IntCode[IntPc].op != INT_END_IF) oops();
+}
+
+//-----------------------------------------------------------------------------
+// Call a subroutine, using either an rcall or an icall depending on what
+// the processor supports or requires.
+//-----------------------------------------------------------------------------
+static void CallSubroutine(DWORD addr)
+{
+ if(Prog.mcu->avrUseIjmp) {
+ Instruction(OP_LDI, 30, FWD_LO(addr));
+ Instruction(OP_LDI, 31, FWD_HI(addr));
+ Instruction(OP_ICALL, 0, 0);
+ } else {
+ Instruction(OP_RCALL, addr, 0);
+ }
+}
+
+//==================================================================================>>4:24 PM 3/31/2015
+
+//-----------------------------------------------------------------------------
+// Compile the intermediate code to AVR native code.
+//-----------------------------------------------------------------------------
+static void CompileFromIntermediate(void)
+{
+ DWORD addr, addr2;
+ int bit, bit2;
+ DWORD addrl, addrh;
+ DWORD addrl2, addrh2;
+
+ for(; IntPc < IntCodeLen; IntPc++) {
+ IntOp *a = &IntCode[IntPc];
+ switch(a->op) {
+ case INT_SET_BIT:
+ MemForSingleBit(a->name1, FALSE, &addr, &bit);
+ SetBit(addr, bit);
+ break;
+
+ case INT_CLEAR_BIT:
+ MemForSingleBit(a->name1, FALSE, &addr, &bit);
+ ClearBit(addr, bit);
+ break;
+
+ case INT_COPY_BIT_TO_BIT:
+ MemForSingleBit(a->name1, FALSE, &addr, &bit);
+ MemForSingleBit(a->name2, FALSE, &addr2, &bit2);
+ CopyBit(addr, bit, addr2, bit2);
+ break;
+
+ case INT_SET_VARIABLE_TO_LITERAL:
+ MemForVariable(a->name1, &addrl, &addrh);
+ WriteMemory(addrl, a->literal & 0xff);
+ WriteMemory(addrh, a->literal >> 8);
+ break;
+
+ case INT_INCREMENT_VARIABLE: {
+ MemForVariable(a->name1, &addrl, &addrh);
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_LD_X, 17, 0);
+ // increment
+ Instruction(OP_INC, 16, 0);
+ DWORD noCarry = AllocFwdAddr();
+ Instruction(OP_BRNE, noCarry, 0);
+ Instruction(OP_INC, 17, 0);
+ FwdAddrIsNow(noCarry);
+ // X is still addrh
+ Instruction(OP_ST_X, 17, 0);
+ LoadXAddr(addrl);
+ Instruction(OP_ST_X, 16, 0);
+ break;
+ }
+ case INT_IF_BIT_SET: {
+ DWORD condFalse = AllocFwdAddr();
+ MemForSingleBit(a->name1, TRUE, &addr, &bit);
+ IfBitClear(addr, bit);
+ Instruction(OP_RJMP, condFalse, 0);
+ CompileIfBody(condFalse);
+ break;
+ }
+ case INT_IF_BIT_CLEAR: {
+ DWORD condFalse = AllocFwdAddr();
+ MemForSingleBit(a->name1, TRUE, &addr, &bit);
+ IfBitSet(addr, bit);
+ Instruction(OP_RJMP, condFalse, 0);
+ CompileIfBody(condFalse);
+ break;
+ }
+ case INT_IF_VARIABLE_LES_LITERAL: {
+ DWORD notTrue = AllocFwdAddr();
+
+ MemForVariable(a->name1, &addrl, &addrh);
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_LD_X, 17, 0);
+
+ Instruction(OP_LDI, 18, (a->literal & 0xff));
+ Instruction(OP_LDI, 19, (a->literal >> 8));
+
+ Instruction(OP_CP, 16, 18);
+ Instruction(OP_CPC, 17, 19);
+ Instruction(OP_BRGE, notTrue, 0);
+
+ CompileIfBody(notTrue);
+ break;
+ }
+ case INT_IF_VARIABLE_GRT_VARIABLE:
+ case INT_IF_VARIABLE_EQUALS_VARIABLE: {
+ DWORD notTrue = AllocFwdAddr();
+
+ MemForVariable(a->name1, &addrl, &addrh);
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_LD_X, 17, 0);
+ MemForVariable(a->name2, &addrl, &addrh);
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 18, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_LD_X, 19, 0);
+
+ if(a->op == INT_IF_VARIABLE_EQUALS_VARIABLE) {
+ Instruction(OP_CP, 16, 18);
+ Instruction(OP_CPC, 17, 19);
+ Instruction(OP_BRNE, notTrue, 0);
+ } else if(a->op == INT_IF_VARIABLE_GRT_VARIABLE) {
+ DWORD isTrue = AllocFwdAddr();
+
+ // true if op1 > op2
+ // false if op1 >= op2
+ Instruction(OP_CP, 18, 16);
+ Instruction(OP_CPC, 19, 17);
+ Instruction(OP_BRGE, notTrue, 0);
+ } else oops();
+ CompileIfBody(notTrue);
+ break;
+ }
+ case INT_SET_VARIABLE_TO_VARIABLE:
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForVariable(a->name2, &addrl2, &addrh2);
+
+ LoadXAddr(addrl2);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrl);
+ Instruction(OP_ST_X, 16, 0);
+
+ LoadXAddr(addrh2);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_ST_X, 16, 0);
+ break;
+
+ case INT_SET_VARIABLE_DIVIDE:
+ // Do this one separately since the divide routine uses
+ // slightly different in/out registers and I don't feel like
+ // modifying it.
+ MemForVariable(a->name2, &addrl, &addrh);
+ MemForVariable(a->name3, &addrl2, &addrh2);
+
+ LoadXAddr(addrl2);
+ Instruction(OP_LD_X, 18, 0);
+ LoadXAddr(addrh2);
+ Instruction(OP_LD_X, 19, 0);
+
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_LD_X, 17, 0);
+
+ CallSubroutine(DivideAddress);
+ DivideUsed = TRUE;
+
+ MemForVariable(a->name1, &addrl, &addrh);
+
+ LoadXAddr(addrl);
+ Instruction(OP_ST_X, 16, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_ST_X, 17, 0);
+ break;
+
+ case INT_SET_VARIABLE_ADD:
+ case INT_SET_VARIABLE_SUBTRACT:
+ case INT_SET_VARIABLE_MULTIPLY:
+ MemForVariable(a->name2, &addrl, &addrh);
+ MemForVariable(a->name3, &addrl2, &addrh2);
+
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 18, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_LD_X, 19, 0);
+
+ LoadXAddr(addrl2);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrh2);
+ Instruction(OP_LD_X, 17, 0);
+
+ if(a->op == INT_SET_VARIABLE_ADD) {
+ Instruction(OP_ADD, 18, 16);
+ Instruction(OP_ADC, 19, 17);
+ } else if(a->op == INT_SET_VARIABLE_SUBTRACT) {
+ Instruction(OP_SUB, 18, 16);
+ Instruction(OP_SBC, 19, 17);
+ } else if(a->op == INT_SET_VARIABLE_MULTIPLY) {
+ CallSubroutine(MultiplyAddress);
+ MultiplyUsed = TRUE;
+ } else oops();
+
+ MemForVariable(a->name1, &addrl, &addrh);
+
+ LoadXAddr(addrl);
+ Instruction(OP_ST_X, 18, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_ST_X, 19, 0);
+ break;
+
+ case INT_SET_PWM: {
+ int target = atoi(a->name2);
+
+ // PWM frequency is
+ // target = xtal/(256*prescale)
+ // so not a lot of room for accurate frequency here
+
+ int prescale;
+ int bestPrescale;
+ int bestError = INT_MAX;
+ int bestFreq;
+ for(prescale = 1;;) {
+ int freq = (Prog.mcuClock + prescale*128)/(prescale*256);
+
+ int err = abs(freq - target);
+ if(err < bestError) {
+ bestError = err;
+ bestPrescale = prescale;
+ bestFreq = freq;
+ }
+
+ if(prescale == 1) {
+ prescale = 8;
+ } else if(prescale == 8) {
+ prescale = 64;
+ } else if(prescale == 64) {
+ prescale = 256;
+ } else if(prescale == 256) {
+ prescale = 1024;
+ } else {
+ break;
+ }
+ }
+
+ if(((double)bestError)/target > 0.05) {
+ Error(_("Target frequency %d Hz, closest achievable is "
+ "%d Hz (warning, >5%% error)."), target, bestFreq);
+ }
+
+ DivideUsed = TRUE; MultiplyUsed = TRUE;
+ MemForVariable(a->name1, &addrl, &addrh);
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 16, 0);
+ Instruction(OP_LDI, 17, 0);
+ Instruction(OP_LDI, 19, 0);
+ Instruction(OP_LDI, 18, 255);
+ CallSubroutine(MultiplyAddress);
+ Instruction(OP_MOV, 17, 19);
+ Instruction(OP_MOV, 16, 18);
+ Instruction(OP_LDI, 19, 0);
+ Instruction(OP_LDI, 18, 100);
+ CallSubroutine(DivideAddress);
+ LoadXAddr(REG_OCR2A);
+ Instruction(OP_ST_X, 16, 0);
+
+ // Setup only happens once
+ MemForSingleBit("$pwm_init", FALSE, &addr, &bit);
+ DWORD skip = AllocFwdAddr();
+ IfBitSet(addr, bit);
+ Instruction(OP_RJMP, skip, 0);
+ SetBit(addr, bit);
+
+ BYTE cs;
+ switch(bestPrescale) {
+ case 1: cs = 1; break;
+ case 8: cs = 2; break;
+ case 64: cs = 3; break;
+ case 256: cs = 4; break;
+ case 1024: cs = 5; break;
+ default: oops(); break;
+ }
+
+ // fast PWM mode, non-inverted operation, given prescale ==================================================12:27 PM 4/1/2015
+ WriteMemory(REG_TCCR2A, (1 << 7) | (1 << 1) | (1 << 0));
+ WriteMemory(REG_TCCR2B, cs);
+
+ FwdAddrIsNow(skip);
+
+ break;
+ }
+ case INT_EEPROM_BUSY_CHECK: {
+ MemForSingleBit(a->name1, FALSE, &addr, &bit);
+
+ DWORD isBusy = AllocFwdAddr();
+ DWORD done = AllocFwdAddr();
+ IfBitSet(REG_EECR, 1);
+ Instruction(OP_RJMP, isBusy, 0);
+
+ IfBitClear(EepromHighByteWaitingAddr, EepromHighByteWaitingBit);
+ Instruction(OP_RJMP, done, 0);
+
+ // Just increment EEARH:EEARL, to point to the high byte of
+ // whatever we just wrote the low byte for.
+ LoadXAddr(REG_EEARL);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(REG_EEARH);
+ Instruction(OP_LD_X, 17, 0);
+ Instruction(OP_INC, 16, 0);
+ DWORD noCarry = AllocFwdAddr();
+ Instruction(OP_BRNE, noCarry, 0);
+ Instruction(OP_INC, 17, 0);
+ FwdAddrIsNow(noCarry);
+ // X is still REG_EEARH
+ Instruction(OP_ST_X, 17, 0);
+ LoadXAddr(REG_EEARL);
+ Instruction(OP_ST_X, 16, 0);
+
+ LoadXAddr(EepromHighByte);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(REG_EEDR);
+ Instruction(OP_ST_X, 16, 0);
+ LoadXAddr(REG_EECR);
+ Instruction(OP_LDI, 16, 0x04);
+ Instruction(OP_ST_X, 16, 0);
+ Instruction(OP_LDI, 16, 0x06);
+ Instruction(OP_ST_X, 16, 0);
+
+ ClearBit(EepromHighByteWaitingAddr, EepromHighByteWaitingBit);
+
+ FwdAddrIsNow(isBusy);
+ SetBit(addr, bit);
+ FwdAddrIsNow(done);
+ break;
+ }
+ case INT_EEPROM_READ: {
+ MemForVariable(a->name1, &addrl, &addrh);
+ int i;
+ for(i = 0; i < 2; i++) {
+ WriteMemory(REG_EEARH, ((a->literal+i) >> 8));
+ WriteMemory(REG_EEARL, ((a->literal+i) & 0xff));
+ WriteMemory(REG_EECR, 0x01);
+ LoadXAddr(REG_EEDR);
+ Instruction(OP_LD_X, 16, 0);
+ if(i == 0) {
+ LoadXAddr(addrl);
+ } else {
+ LoadXAddr(addrh);
+ }
+ Instruction(OP_ST_X, 16, 0);
+ }
+ break;
+ }
+ case INT_EEPROM_WRITE:
+ MemForVariable(a->name1, &addrl, &addrh);
+ SetBit(EepromHighByteWaitingAddr, EepromHighByteWaitingBit);
+ LoadXAddr(addrh);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(EepromHighByte);
+ Instruction(OP_ST_X, 16, 0);
+
+ WriteMemory(REG_EEARH, (a->literal >> 8));
+ WriteMemory(REG_EEARL, (a->literal & 0xff));
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(REG_EEDR);
+ Instruction(OP_ST_X, 16, 0);
+ LoadXAddr(REG_EECR);
+ Instruction(OP_LDI, 16, 0x04);
+ Instruction(OP_ST_X, 16, 0);
+ Instruction(OP_LDI, 16, 0x06);
+ Instruction(OP_ST_X, 16, 0);
+ break;
+
+ case INT_READ_ADC: {
+ MemForVariable(a->name1, &addrl, &addrh);
+
+ WriteMemory(REG_ADMUX,
+ (0 << 6) | // AREF, internal Vref odd
+ (0 << 5) | // right-adjusted
+ MuxForAdcVariable(a->name1));
+
+ // target something around 200 kHz for the ADC clock, for
+ // 25/(200k) or 125 us conversion time, reasonable
+ int divisor = (Prog.mcuClock / 200000);
+ int j = 0;
+ for(j = 1; j <= 7; j++) {
+ if((1 << j) > divisor) break;
+ }
+
+ BYTE adcsra =
+ (1 << 7) | // ADC enabled
+ (0 << 5) | // not free running
+ (0 << 3) | // no interrupt enabled
+ j; // prescaler setup
+
+ WriteMemory(REG_ADCSRA, adcsra);
+ WriteMemory(REG_ADCSRA, (BYTE)(adcsra | (1 << 6)));
+
+ DWORD waitForFinsh = ArduinoProgWriteP;
+ IfBitSet(REG_ADCSRA, 6);
+ Instruction(OP_RJMP, waitForFinsh, 0);
+
+ LoadXAddr(REG_ADCL);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrl);
+ Instruction(OP_ST_X, 16, 0);
+
+ LoadXAddr(REG_ADCH);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_ST_X, 16, 0);
+
+ break;
+ }
+ case INT_UART_SEND: {
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForSingleBit(a->name2, TRUE, &addr, &bit);
+
+ DWORD noSend = AllocFwdAddr();
+ IfBitClear(addr, bit);
+ Instruction(OP_RJMP, noSend, 0);
+
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(REG_UDR);
+ Instruction(OP_ST_X, 16, 0);
+
+ FwdAddrIsNow(noSend);
+
+ ClearBit(addr, bit);
+ DWORD dontSet = AllocFwdAddr();
+ IfBitSet(REG_UCSRA, 5); // UDRE, is 1 when tx buffer is empty
+ Instruction(OP_RJMP, dontSet, 0);
+ SetBit(addr, bit);
+ FwdAddrIsNow(dontSet);
+
+ break;
+ }
+ case INT_UART_RECV: {
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForSingleBit(a->name2, TRUE, &addr, &bit);
+
+ ClearBit(addr, bit);
+
+ DWORD noChar = AllocFwdAddr();
+ IfBitClear(REG_UCSRA, 7);
+ Instruction(OP_RJMP, noChar, 0);
+
+ SetBit(addr, bit);
+ LoadXAddr(REG_UDR);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrl);
+ Instruction(OP_ST_X, 16, 0);
+
+ LoadXAddr(addrh);
+ Instruction(OP_LDI, 16, 0);
+ Instruction(OP_ST_X, 16, 0);
+
+ FwdAddrIsNow(noChar);
+ break;
+ }
+ case INT_END_IF:
+ case INT_ELSE:
+ return;
+
+ case INT_SIMULATE_NODE_STATE:
+ case INT_COMMENT:
+ break;
+
+ default:
+ oops();
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 16x16 signed multiply, code from Atmel app note AVR200. op1 in r17:16,
+// op2 in r19:18, result low word goes into r19:18.
+//-----------------------------------------------------------------------------
+static void MultiplyRoutine(void)
+{
+ FwdAddrIsNow(MultiplyAddress);
+
+ DWORD m16s_1;
+ DWORD m16s_2 = AllocFwdAddr();
+
+ Instruction(OP_SUB, 21, 21);
+ Instruction(OP_SUB, 20, 20);
+ Instruction(OP_LDI, 22, 16);
+ m16s_1 = ArduinoProgWriteP; Instruction(OP_BRCC, m16s_2, 0);
+ Instruction(OP_ADD, 20, 16);
+ Instruction(OP_ADC, 21, 17);
+ FwdAddrIsNow(m16s_2); Instruction(OP_SBRC, 18, 0);
+ Instruction(OP_SUB, 20, 16);
+ Instruction(OP_SBRC, 18, 0);
+ Instruction(OP_SBC, 21, 17);
+ Instruction(OP_ASR, 21, 0);
+ Instruction(OP_ROR, 20, 0);
+ Instruction(OP_ROR, 19, 0);
+ Instruction(OP_ROR, 18, 0);
+ Instruction(OP_DEC, 22, 0);
+ Instruction(OP_BRNE, m16s_1, 0);
+ Instruction(OP_RET, 0, 0);
+}
+
+//-----------------------------------------------------------------------------
+// 16/16 signed divide, code from the same app note. Dividend in r17:16,
+// divisor in r19:18, result goes in r17:16 (and remainder in r15:14).
+//-----------------------------------------------------------------------------
+static void DivideRoutine(void)
+{
+ FwdAddrIsNow(DivideAddress);
+
+ DWORD d16s_1 = AllocFwdAddr();
+ DWORD d16s_2 = AllocFwdAddr();
+ DWORD d16s_3;
+ DWORD d16s_4 = AllocFwdAddr();
+ DWORD d16s_5 = AllocFwdAddr();
+ DWORD d16s_6 = AllocFwdAddr();
+
+ Instruction(OP_MOV, 13, 17);
+ Instruction(OP_EOR, 13, 19);
+ Instruction(OP_SBRS, 17, 7);
+ Instruction(OP_RJMP, d16s_1, 0);
+ Instruction(OP_COM, 17, 0);
+ Instruction(OP_COM, 16, 0);
+ Instruction(OP_SUBI, 16, 0xff);
+ Instruction(OP_SBCI, 17, 0xff);
+ FwdAddrIsNow(d16s_1); Instruction(OP_SBRS, 19, 7);
+ Instruction(OP_RJMP, d16s_2, 0);
+ Instruction(OP_COM, 19, 0);
+ Instruction(OP_COM, 18, 0);
+ Instruction(OP_SUBI, 18, 0xff);
+ Instruction(OP_SBCI, 19, 0xff);
+ FwdAddrIsNow(d16s_2); Instruction(OP_EOR, 14, 14);
+ Instruction(OP_SUB, 15, 15);
+ Instruction(OP_LDI, 20, 17);
+
+ d16s_3 = ArduinoProgWriteP; Instruction(OP_ADC, 16, 16);
+ Instruction(OP_ADC, 17, 17);
+ Instruction(OP_DEC, 20, 0);
+ Instruction(OP_BRNE, d16s_5, 0);
+ Instruction(OP_SBRS, 13, 7);
+ Instruction(OP_RJMP, d16s_4, 0);
+ Instruction(OP_COM, 17, 0);
+ Instruction(OP_COM, 16, 0);
+ Instruction(OP_SUBI, 16, 0xff);
+ Instruction(OP_SBCI, 17, 0xff);
+ FwdAddrIsNow(d16s_4); Instruction(OP_RET, 0, 0);
+ FwdAddrIsNow(d16s_5); Instruction(OP_ADC, 14, 14);
+ Instruction(OP_ADC, 15, 15);
+ Instruction(OP_SUB, 14, 18);
+ Instruction(OP_SBC, 15, 19);
+ Instruction(OP_BRCC, d16s_6, 0);
+ Instruction(OP_ADD, 14, 18);
+ Instruction(OP_ADC, 15, 19);
+ Instruction(OP_CLC, 0, 0);
+ Instruction(OP_RJMP, d16s_3, 0);
+ FwdAddrIsNow(d16s_6); Instruction(OP_SEC, 0, 0);
+ Instruction(OP_RJMP, d16s_3, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Compile the program to REG code for the currently selected processor
+// and write it to the given file. Produce an error message if we cannot
+// write to the file, or if there is something inconsistent about the
+// program.
+//-----------------------------------------------------------------------------
+void CompileArduino(char *outFile)
+{
+ FILE *f = fopen(outFile, "w");
+ if(!f) {
+ Error(_("Couldn't open file '%s'"), outFile);
+ return;
+ }
+
+ if(setjmp(CompileErrorBuf) != 0) {
+ fclose(f);
+ return;
+ }
+
+ // Here we must set up the addresses of some registers that for some
+ // stupid reason move around from AVR to AVR.
+
+ //REG_TIMSK0 = 0x6E;
+ REG_TIMSK1 = 0x6F;
+ //REG_TIMSK2 = 0x70;
+ //REG_TIFR0 = 0x35;
+ REG_TIFR1 = 0x36;
+ //REG_TIFR2 = 0x37;
+ REG_UBRRH = 0xC5;
+ REG_UBRRL = 0xC4;
+ //REG_UCSRC = 0xC2;
+ REG_UCSRB = 0xC1;
+ REG_UCSRA = 0xC0;
+ REG_UDR = 0xC6; //---------------------------------------------------------------------------->12:19 PM 4/10/2015
+
+ WipeMemory();
+ MultiplyUsed = FALSE;
+ MultiplyAddress = AllocFwdAddr();
+ DivideUsed = FALSE;
+ DivideAddress = AllocFwdAddr();
+ AllocStart();
+
+ // Where we hold the high byte to program in EEPROM while the low byte
+ // programs.
+ EepromHighByte = AllocOctetRam();
+ AllocBitRam(&EepromHighByteWaitingAddr, &EepromHighByteWaitingBit);
+
+ WriteRuntime();
+ IntPc = 0;
+ CompileFromIntermediate();
+
+ if(Prog.mcu->avrUseIjmp) {
+ Instruction(OP_LDI, 30, (BeginningOfCycleAddr & 0xff));
+ Instruction(OP_LDI, 31, (BeginningOfCycleAddr >> 8));
+ Instruction(OP_IJMP, 0, 0);
+ } else {
+ Instruction(OP_RJMP, BeginningOfCycleAddr, 0);
+ }
+
+ MemCheckForErrorsPostCompile();
+
+ if(MultiplyUsed) MultiplyRoutine();
+ if(DivideUsed) DivideRoutine();
+
+ WriteHexFile(f);
+ fclose(f);
+
+ char str[MAX_PATH+500];
+ sprintf(str, _("Compile successful; wrote IHEX for AVR to '%s'.\r\n\r\n"
+ "Remember to set the processor configuration (fuses) correctly. "
+ "This does not happen automatically."), outFile);
+ CompileSuccessfulMessage(str);
+}
+//====================================================================================================================>4:34 PM 4/1/2015
\ No newline at end of file
diff --git a/ldmicro/avr.cpp b/ldmicro/avr.cpp
new file mode 100644
index 0000000..17c9682
--- /dev/null
+++ b/ldmicro/avr.cpp
@@ -0,0 +1,1402 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// An AVR assembler, for our own internal use, plus routines to generate
+// code from the ladder logic structure, plus routines to generate the
+// runtime needed to schedule the cycles.
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "ldmicro.h"
+#include "intcode.h"
+
+// not complete; just what I need
+typedef enum AvrOpTag {
+ OP_VACANT,
+ OP_ADC,
+ OP_ADD,
+ OP_ASR,
+ OP_BRCC,
+ OP_BRCS,
+ OP_BREQ,
+ OP_BRGE,
+ OP_BRLO,
+ OP_BRLT,
+ OP_BRNE,
+ OP_CBR,
+ OP_CLC,
+ OP_CLR,
+ OP_COM,
+ OP_CP,
+ OP_CPC,
+ OP_DEC,
+ OP_EOR,
+ OP_ICALL,
+ OP_IJMP,
+ OP_INC,
+ OP_LDI,
+ OP_LD_X,
+ OP_MOV,
+ OP_OUT,
+ OP_RCALL,
+ OP_RET,
+ OP_RETI,
+ OP_RJMP,
+ OP_ROR,
+ OP_SEC,
+ OP_SBC,
+ OP_SBCI,
+ OP_SBR,
+ OP_SBRC,
+ OP_SBRS,
+ OP_ST_X,
+ OP_SUB,
+ OP_SUBI,
+ OP_TST,
+ OP_WDR,
+} AvrOp;
+
+typedef struct AvrInstructionTag {
+ AvrOp op;
+ DWORD arg1;
+ DWORD arg2;
+} AvrInstruction;
+
+#define MAX_PROGRAM_LEN 128*1024
+static AvrInstruction AvrProg[MAX_PROGRAM_LEN];
+static DWORD AvrProgWriteP;
+
+// For yet unresolved references in jumps
+static DWORD FwdAddrCount;
+
+// Fancier: can specify a forward reference to the high or low octet of a
+// 16-bit address, which is useful for indirect jumps.
+#define FWD_LO(x) ((x) | 0x20000000)
+#define FWD_HI(x) ((x) | 0x40000000)
+
+// Address to jump to when we finish one PLC cycle
+static DWORD BeginningOfCycleAddr;
+
+// Address of the multiply subroutine, and whether we will have to include it
+static DWORD MultiplyAddress;
+static BOOL MultiplyUsed;
+// and also divide
+static DWORD DivideAddress;
+static BOOL DivideUsed;
+
+// For EEPROM: we queue up characters to send in 16-bit words (corresponding
+// to the integer variables), but we can actually just program 8 bits at a
+// time, so we need to store the high byte somewhere while we wait.
+static DWORD EepromHighByte;
+static DWORD EepromHighByteWaitingAddr;
+static int EepromHighByteWaitingBit;
+
+// Some useful registers, unfortunately many of which are in different places
+// on different AVRs! I consider this a terrible design choice by Atmel.
+static DWORD REG_TIMSK;
+static DWORD REG_TIFR;
+#define REG_OCR1AH 0x4b
+#define REG_OCR1AL 0x4a
+#define REG_TCCR1A 0x4f
+#define REG_TCCR1B 0x4e
+#define REG_SPH 0x5e
+#define REG_SPL 0x5d
+#define REG_ADMUX 0x27
+#define REG_ADCSRA 0x26
+#define REG_ADCL 0x24
+#define REG_ADCH 0x25
+
+static DWORD REG_UBRRH;
+static DWORD REG_UBRRL;
+static DWORD REG_UCSRB;
+static DWORD REG_UCSRA;
+static DWORD REG_UDR;
+
+#define REG_OCR2 0x43
+#define REG_TCCR2 0x45
+
+#define REG_EEARH 0x3f
+#define REG_EEARL 0x3e
+#define REG_EEDR 0x3d
+#define REG_EECR 0x3c
+
+
+static int IntPc;
+
+static void CompileFromIntermediate(void);
+
+//-----------------------------------------------------------------------------
+// Wipe the program and set the write pointer back to the beginning. Also
+// flush all the state of the register allocators etc.
+//-----------------------------------------------------------------------------
+static void WipeMemory(void)
+{
+ memset(AvrProg, 0, sizeof(AvrProg));
+ AvrProgWriteP = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Store an instruction at the next spot in program memory. Error condition
+// if this spot is already filled. We don't actually assemble to binary yet;
+// there may be references to resolve.
+//-----------------------------------------------------------------------------
+static void Instruction(AvrOp op, DWORD arg1, DWORD arg2)
+{
+ if(AvrProg[AvrProgWriteP].op != OP_VACANT) oops();
+
+ AvrProg[AvrProgWriteP].op = op;
+ AvrProg[AvrProgWriteP].arg1 = arg1;
+ AvrProg[AvrProgWriteP].arg2 = arg2;
+ AvrProgWriteP++;
+}
+
+//-----------------------------------------------------------------------------
+// Allocate a unique descriptor for a forward reference. Later that forward
+// reference gets assigned to an absolute address, and we can go back and
+// fix up the reference.
+//-----------------------------------------------------------------------------
+static DWORD AllocFwdAddr(void)
+{
+ FwdAddrCount++;
+ return 0x80000000 | FwdAddrCount;
+}
+
+//-----------------------------------------------------------------------------
+// Go back and fix up the program given that the provided forward address
+// corresponds to the next instruction to be assembled.
+//-----------------------------------------------------------------------------
+static void FwdAddrIsNow(DWORD addr)
+{
+ if(!(addr & 0x80000000)) oops();
+
+ DWORD i;
+ for(i = 0; i < AvrProgWriteP; i++) {
+ if(AvrProg[i].arg1 == addr) {
+ AvrProg[i].arg1 = AvrProgWriteP;
+ } else if(AvrProg[i].arg2 == FWD_LO(addr)) {
+ AvrProg[i].arg2 = (AvrProgWriteP & 0xff);
+ } else if(AvrProg[i].arg2 == FWD_HI(addr)) {
+ AvrProg[i].arg2 = AvrProgWriteP >> 8;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Given an opcode and its operands, assemble the 16-bit instruction for the
+// AVR. Check that the operands do not have more bits set than is meaningful;
+// it is an internal error if they do not. Needs to know what address it is
+// being assembled to so that it generate relative jumps; internal error if
+// a relative jump goes out of range.
+//-----------------------------------------------------------------------------
+static DWORD Assemble(DWORD addrAt, AvrOp op, DWORD arg1, DWORD arg2)
+{
+#define CHECK(v, bits) if((v) != ((v) & ((1 << (bits))-1))) oops()
+ switch(op) {
+ case OP_ASR:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (9 << 12) | (2 << 9) | (arg1 << 4) | 5;
+
+ case OP_ROR:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (9 << 12) | (2 << 9) | (arg1 << 4) | 7;
+
+ case OP_ADD:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (3 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_ADC:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (7 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_EOR:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (9 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_SUB:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (6 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_SBC:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (2 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_CP:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (5 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_CPC:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (1 << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_COM:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (9 << 12) | (2 << 9) | (arg1 << 4);
+
+ case OP_SBR:
+ CHECK(arg1, 5); CHECK(arg2, 8);
+ if(!(arg1 & 0x10)) oops();
+ arg1 &= ~0x10;
+ return (6 << 12) | ((arg2 & 0xf0) << 4) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_CBR:
+ CHECK(arg1, 5); CHECK(arg2, 8);
+ if(!(arg1 & 0x10)) oops();
+ arg1 &= ~0x10;
+ arg2 = ~arg2;
+ return (7 << 12) | ((arg2 & 0xf0) << 4) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_INC:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (0x4a << 9) | (arg1 << 4) | 3;
+
+ case OP_DEC:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (0x4a << 9) | (arg1 << 4) | 10;
+
+ case OP_SUBI:
+ CHECK(arg1, 5); CHECK(arg2, 8);
+ if(!(arg1 & 0x10)) oops();
+ arg1 &= ~0x10;
+ return (5 << 12) | ((arg2 & 0xf0) << 4) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_SBCI:
+ CHECK(arg1, 5); CHECK(arg2, 8);
+ if(!(arg1 & 0x10)) oops();
+ arg1 &= ~0x10;
+ return (4 << 12) | ((arg2 & 0xf0) << 4) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_TST:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (8 << 10) | ((arg1 & 0x10) << 4) | ((arg1 & 0x10) << 5) |
+ ((arg1 & 0xf) << 4) | (arg1 & 0xf);
+
+ case OP_SEC:
+ CHECK(arg1, 0); CHECK(arg2, 0);
+ return 0x9408;
+
+ case OP_CLC:
+ CHECK(arg1, 0); CHECK(arg2, 0);
+ return 0x9488;
+
+ case OP_IJMP:
+ CHECK(arg1, 0); CHECK(arg2, 0);
+ return 0x9409;
+
+ case OP_ICALL:
+ CHECK(arg1, 0); CHECK(arg2, 0);
+ return 0x9509;
+
+ case OP_RJMP:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 2047 || ((int)arg1) < -2048) oops();
+ arg1 &= (4096-1);
+ return (12 << 12) | arg1;
+
+ case OP_RCALL:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 2047 || ((int)arg1) < -2048) oops();
+ arg1 &= (4096-1);
+ return (13 << 12) | arg1;
+
+ case OP_RETI:
+ return 0x9518;
+
+ case OP_RET:
+ return 0x9508;
+
+ case OP_SBRC:
+ CHECK(arg1, 5); CHECK(arg2, 3);
+ return (0x7e << 9) | (arg1 << 4) | arg2;
+
+ case OP_SBRS:
+ CHECK(arg1, 5); CHECK(arg2, 3);
+ return (0x7f << 9) | (arg1 << 4) | arg2;
+
+ case OP_BREQ:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 63 || ((int)arg1) < -64) oops();
+ arg1 &= (128-1);
+ return (0xf << 12) | (arg1 << 3) | 1;
+
+ case OP_BRNE:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 63 || ((int)arg1) < -64) oops();
+ arg1 &= (128-1);
+ return (0xf << 12) | (1 << 10) | (arg1 << 3) | 1;
+
+ case OP_BRLO:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 63 || ((int)arg1) < -64) oops();
+ arg1 &= (128-1);
+ return (0xf << 12) | (arg1 << 3);
+
+ case OP_BRGE:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 63 || ((int)arg1) < -64) oops();
+ arg1 &= (128-1);
+ return (0xf << 12) | (1 << 10) | (arg1 << 3) | 4;
+
+ case OP_BRLT:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 63 || ((int)arg1) < -64) oops();
+ arg1 &= (128-1);
+ return (0xf << 12) | (arg1 << 3) | 4;
+
+ case OP_BRCC:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 63 || ((int)arg1) < -64) oops();
+ arg1 &= (128-1);
+ return (0xf << 12) | (1 << 10) | (arg1 << 3);
+
+ case OP_BRCS:
+ CHECK(arg2, 0);
+ arg1 = arg1 - addrAt - 1;
+ if(((int)arg1) > 63 || ((int)arg1) < -64) oops();
+ arg1 &= (128-1);
+ return (0xf << 12) | (arg1 << 3);
+
+ case OP_MOV:
+ CHECK(arg1, 5); CHECK(arg2, 5);
+ return (0xb << 10) | ((arg2 & 0x10) << 5) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_LDI:
+ CHECK(arg1, 5); CHECK(arg2, 8);
+ if(!(arg1 & 0x10)) oops();
+ arg1 &= ~0x10;
+ return (0xe << 12) | ((arg2 & 0xf0) << 4) | (arg1 << 4) |
+ (arg2 & 0x0f);
+
+ case OP_LD_X:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (9 << 12) | (arg1 << 4) | 12;
+
+ case OP_ST_X:
+ CHECK(arg1, 5); CHECK(arg2, 0);
+ return (0x49 << 9) | (arg1 << 4) | 12;
+
+ case OP_WDR:
+ CHECK(arg1, 0); CHECK(arg2, 0);
+ return 0x95a8;
+
+ default:
+ oops();
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Write an intel IHEX format description of the program assembled so far.
+// This is where we actually do the assembly to binary format.
+//-----------------------------------------------------------------------------
+static void WriteHexFile(FILE *f)
+{
+ BYTE soFar[16];
+ int soFarCount = 0;
+ DWORD soFarStart = 0;
+
+ DWORD i;
+ for(i = 0; i < AvrProgWriteP; i++) {
+ DWORD w = Assemble(i, AvrProg[i].op, AvrProg[i].arg1, AvrProg[i].arg2);
+
+ if(soFarCount == 0) soFarStart = i;
+ soFar[soFarCount++] = (BYTE)(w & 0xff);
+ soFar[soFarCount++] = (BYTE)(w >> 8);
+
+ if(soFarCount >= 0x10 || i == (AvrProgWriteP-1)) {
+ StartIhex(f);
+ WriteIhex(f, soFarCount);
+ WriteIhex(f, (BYTE)((soFarStart*2) >> 8));
+ WriteIhex(f, (BYTE)((soFarStart*2) & 0xff));
+ WriteIhex(f, 0x00);
+ int j;
+ for(j = 0; j < soFarCount; j++) {
+ WriteIhex(f, soFar[j]);
+ }
+ FinishIhex(f);
+ soFarCount = 0;
+ }
+ }
+
+ // end of file record
+ fprintf(f, ":00000001FF\n");
+}
+
+//-----------------------------------------------------------------------------
+// Make sure that the given address is loaded in the X register; might not
+// have to update all of it.
+//-----------------------------------------------------------------------------
+static void LoadXAddr(DWORD addr)
+{
+ Instruction(OP_LDI, 27, (addr >> 8));
+ Instruction(OP_LDI, 26, (addr & 0xff));
+}
+
+//-----------------------------------------------------------------------------
+// Generate code to write an 8-bit value to a particular register.
+//-----------------------------------------------------------------------------
+static void WriteMemory(DWORD addr, BYTE val)
+{
+ LoadXAddr(addr);
+ // load r16 with the data
+ Instruction(OP_LDI, 16, val);
+ // do the store
+ Instruction(OP_ST_X, 16, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Copy just one bit from one place to another.
+//-----------------------------------------------------------------------------
+static void CopyBit(DWORD addrDest, int bitDest, DWORD addrSrc, int bitSrc)
+{
+ LoadXAddr(addrSrc); Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrDest); Instruction(OP_LD_X, 17, 0);
+ Instruction(OP_SBRS, 16, bitSrc);
+ Instruction(OP_CBR, 17, (1 << bitDest));
+ Instruction(OP_SBRC, 16, bitSrc);
+ Instruction(OP_SBR, 17, (1 << bitDest));
+
+ Instruction(OP_ST_X, 17, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Execute the next instruction only if the specified bit of the specified
+// memory location is clear (i.e. skip if set).
+//-----------------------------------------------------------------------------
+static void IfBitClear(DWORD addr, int bit)
+{
+ LoadXAddr(addr);
+ Instruction(OP_LD_X, 16, 0);
+ Instruction(OP_SBRS, 16, bit);
+}
+
+//-----------------------------------------------------------------------------
+// Execute the next instruction only if the specified bit of the specified
+// memory location is set (i.e. skip if clear).
+//-----------------------------------------------------------------------------
+static void IfBitSet(DWORD addr, int bit)
+{
+ LoadXAddr(addr);
+ Instruction(OP_LD_X, 16, 0);
+ Instruction(OP_SBRC, 16, bit);
+}
+
+//-----------------------------------------------------------------------------
+// Set a given bit in an arbitrary (not necessarily I/O memory) location in
+// memory.
+//-----------------------------------------------------------------------------
+static void SetBit(DWORD addr, int bit)
+{
+ LoadXAddr(addr);
+ Instruction(OP_LD_X, 16, 0);
+ Instruction(OP_SBR, 16, (1 << bit));
+ Instruction(OP_ST_X, 16, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Clear a given bit in an arbitrary (not necessarily I/O memory) location in
+// memory.
+//-----------------------------------------------------------------------------
+static void ClearBit(DWORD addr, int bit)
+{
+ LoadXAddr(addr);
+ Instruction(OP_LD_X, 16, 0);
+ Instruction(OP_CBR, 16, (1 << bit));
+ Instruction(OP_ST_X, 16, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Configure AVR 16-bit Timer1 to do the timing for us.
+//-----------------------------------------------------------------------------
+static void ConfigureTimer1(int cycleTimeMicroseconds)
+{
+ int divisor = 1;
+ int countsPerCycle;
+ while(divisor <= 1024) {
+ int timerRate = (Prog.mcuClock / divisor); // hertz
+ double timerPeriod = 1e6 / timerRate; // timer period, us
+ countsPerCycle = ((int)(cycleTimeMicroseconds / timerPeriod)) - 1;
+
+ if(countsPerCycle < 1000) {
+ Error(_("Cycle time too fast; increase cycle time, or use faster "
+ "crystal."));
+ CompileError();
+ } else if(countsPerCycle > 0xffff) {
+ if(divisor >= 1024) {
+ Error(
+ _("Cycle time too slow; decrease cycle time, or use slower "
+ "crystal."));
+ CompileError();
+ }
+ } else {
+ break;
+ }
+
+ if(divisor == 1) divisor = 8;
+ else if(divisor == 8) divisor = 64;
+ else if(divisor == 64) divisor = 256;
+ else if(divisor == 256) divisor = 1024;
+ }
+ WriteMemory(REG_TCCR1A, 0x00); // WGM11=0, WGM10=0
+
+ int csn;
+ switch(divisor) {
+ case 1: csn = 1; break;
+ case 8: csn = 2; break;
+ case 64: csn = 3; break;
+ case 256: csn = 4; break;
+ case 1024: csn = 5; break;
+ default: oops();
+ }
+
+ WriteMemory(REG_TCCR1B, (1<<3) | csn); // WGM13=0, WGM12=1
+
+ // `the high byte must be written before the low byte'
+ WriteMemory(REG_OCR1AH, (countsPerCycle - 1) >> 8);
+ WriteMemory(REG_OCR1AL, (countsPerCycle - 1) & 0xff);
+
+ // Okay, so many AVRs have a register called TIFR, but the meaning of
+ // the bits in that register varies from device to device...
+ if(strcmp(Prog.mcu->mcuName, "Atmel AVR ATmega162 40-PDIP")==0) {
+ WriteMemory(REG_TIMSK, (1 << 6));
+ } else {
+ WriteMemory(REG_TIMSK, (1 << 4));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Write the basic runtime. We set up our reset vector, configure all the
+// I/O pins, then set up the timer that does the cycling. Next instruction
+// written after calling WriteRuntime should be first instruction of the
+// timer loop (i.e. the PLC logic cycle).
+//-----------------------------------------------------------------------------
+static void WriteRuntime(void)
+{
+ DWORD resetVector = AllocFwdAddr();
+
+ int i;
+ Instruction(OP_RJMP, resetVector, 0); // $0000, RESET
+ for(i = 0; i < 34; i++)
+ Instruction(OP_RETI, 0, 0);
+
+ FwdAddrIsNow(resetVector);
+
+ // set up the stack, which we use only when we jump to multiply/divide
+ // routine
+ WORD topOfMemory = (WORD)Prog.mcu->ram[0].start + Prog.mcu->ram[0].len - 1;
+ WriteMemory(REG_SPH, topOfMemory >> 8);
+ WriteMemory(REG_SPL, topOfMemory & 0xff);
+
+ // zero out the memory used for timers, internal relays, etc.
+ LoadXAddr(Prog.mcu->ram[0].start + Prog.mcu->ram[0].len);
+ Instruction(OP_LDI, 16, 0);
+ Instruction(OP_LDI, 18, (Prog.mcu->ram[0].len) & 0xff);
+ Instruction(OP_LDI, 19, (Prog.mcu->ram[0].len) >> 8);
+
+ DWORD loopZero = AvrProgWriteP;
+ Instruction(OP_SUBI, 26, 1);
+ Instruction(OP_SBCI, 27, 0);
+ Instruction(OP_ST_X, 16, 0);
+ Instruction(OP_SUBI, 18, 1);
+ Instruction(OP_SBCI, 19, 0);
+ Instruction(OP_TST, 18, 0);
+ Instruction(OP_BRNE, loopZero, 0);
+ Instruction(OP_TST, 19, 0);
+ Instruction(OP_BRNE, loopZero, 0);
+
+
+ // set up I/O pins
+ BYTE isInput[MAX_IO_PORTS], isOutput[MAX_IO_PORTS];
+ BuildDirectionRegisters(isInput, isOutput);
+
+ if(UartFunctionUsed()) {
+ if(Prog.baudRate == 0) {
+ Error(_("Zero baud rate not possible."));
+ return;
+ }
+
+ // bps = Fosc/(16*(X+1))
+ // bps*16*(X + 1) = Fosc
+ // X = Fosc/(bps*16)-1
+ // and round, don't truncate
+ int divisor = (Prog.mcuClock + Prog.baudRate*8)/(Prog.baudRate*16) - 1;
+
+ double actual = Prog.mcuClock/(16.0*(divisor+1));
+ double percentErr = 100*(actual - Prog.baudRate)/Prog.baudRate;
+
+ if(fabs(percentErr) > 2) {
+ ComplainAboutBaudRateError(divisor, actual, percentErr);
+ }
+ if(divisor > 4095) ComplainAboutBaudRateOverflow();
+
+ WriteMemory(REG_UBRRH, divisor >> 8);
+ WriteMemory(REG_UBRRL, divisor & 0xff);
+ WriteMemory(REG_UCSRB, (1 << 4) | (1 << 3)); // RXEN, TXEN
+
+ for(i = 0; i < Prog.mcu->pinCount; i++) {
+ if(Prog.mcu->pinInfo[i].pin == Prog.mcu->uartNeeds.txPin) {
+ McuIoPinInfo *iop = &(Prog.mcu->pinInfo[i]);
+ isOutput[iop->port - 'A'] |= (1 << iop->bit);
+ break;
+ }
+ }
+ if(i == Prog.mcu->pinCount) oops();
+ }
+
+ if(PwmFunctionUsed()) {
+ for(i = 0; i < Prog.mcu->pinCount; i++) {
+ if(Prog.mcu->pinInfo[i].pin == Prog.mcu->pwmNeedsPin) {
+ McuIoPinInfo *iop = &(Prog.mcu->pinInfo[i]);
+ isOutput[iop->port - 'A'] |= (1 << iop->bit);
+ break;
+ }
+ }
+ if(i == Prog.mcu->pinCount) oops();
+ }
+
+ for(i = 0; Prog.mcu->dirRegs[i] != 0; i++) {
+ if(Prog.mcu->dirRegs[i] == 0xff && Prog.mcu->outputRegs[i] == 0xff) {
+ // skip this one, dummy entry for MCUs with I/O ports not
+ // starting from A
+ } else {
+ WriteMemory(Prog.mcu->dirRegs[i], isOutput[i]);
+ // turn on the pull-ups, and drive the outputs low to start
+ WriteMemory(Prog.mcu->outputRegs[i], isInput[i]);
+ }
+ }
+
+
+ ConfigureTimer1(Prog.cycleTime);
+
+ // and now the generated PLC code will follow
+ BeginningOfCycleAddr = AvrProgWriteP;
+
+ // Okay, so many AVRs have a register called TIFR, but the meaning of
+ // the bits in that register varies from device to device...
+ int tifrBitForOCF1A;
+ if(strcmp(Prog.mcu->mcuName, "Atmel AVR ATmega162 40-PDIP")==0) {
+ tifrBitForOCF1A = 6;
+ } else {
+ tifrBitForOCF1A = 4;
+ }
+
+ DWORD now = AvrProgWriteP;
+ IfBitClear(REG_TIFR, tifrBitForOCF1A);
+ Instruction(OP_RJMP, now, 0);
+
+ SetBit(REG_TIFR, tifrBitForOCF1A);
+
+ Instruction(OP_WDR, 0, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Handle an IF statement. Flow continues to the first instruction generated
+// by this function if the condition is true, else it jumps to the given
+// address (which is an FwdAddress, so not yet assigned). Called with IntPc
+// on the IF statement, returns with IntPc on the END IF.
+//-----------------------------------------------------------------------------
+static void CompileIfBody(DWORD condFalse)
+{
+ IntPc++;
+ CompileFromIntermediate();
+ if(IntCode[IntPc].op == INT_ELSE) {
+ IntPc++;
+ DWORD endBlock = AllocFwdAddr();
+ Instruction(OP_RJMP, endBlock, 0);
+
+ FwdAddrIsNow(condFalse);
+ CompileFromIntermediate();
+ FwdAddrIsNow(endBlock);
+ } else {
+ FwdAddrIsNow(condFalse);
+ }
+
+ if(IntCode[IntPc].op != INT_END_IF) oops();
+}
+
+//-----------------------------------------------------------------------------
+// Call a subroutine, using either an rcall or an icall depending on what
+// the processor supports or requires.
+//-----------------------------------------------------------------------------
+static void CallSubroutine(DWORD addr)
+{
+ if(Prog.mcu->avrUseIjmp) {
+ Instruction(OP_LDI, 30, FWD_LO(addr));
+ Instruction(OP_LDI, 31, FWD_HI(addr));
+ Instruction(OP_ICALL, 0, 0);
+ } else {
+ Instruction(OP_RCALL, addr, 0);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Compile the intermediate code to AVR native code.
+//-----------------------------------------------------------------------------
+static void CompileFromIntermediate(void)
+{
+ DWORD addr, addr2;
+ int bit, bit2;
+ DWORD addrl, addrh;
+ DWORD addrl2, addrh2;
+
+ for(; IntPc < IntCodeLen; IntPc++) {
+ IntOp *a = &IntCode[IntPc];
+ switch(a->op) {
+ case INT_SET_BIT:
+ MemForSingleBit(a->name1, FALSE, &addr, &bit);
+ SetBit(addr, bit);
+ break;
+
+ case INT_CLEAR_BIT:
+ MemForSingleBit(a->name1, FALSE, &addr, &bit);
+ ClearBit(addr, bit);
+ break;
+
+ case INT_COPY_BIT_TO_BIT:
+ MemForSingleBit(a->name1, FALSE, &addr, &bit);
+ MemForSingleBit(a->name2, FALSE, &addr2, &bit2);
+ CopyBit(addr, bit, addr2, bit2);
+ break;
+
+ case INT_SET_VARIABLE_TO_LITERAL:
+ MemForVariable(a->name1, &addrl, &addrh);
+ WriteMemory(addrl, a->literal & 0xff);
+ WriteMemory(addrh, a->literal >> 8);
+ break;
+
+ case INT_INCREMENT_VARIABLE: {
+ MemForVariable(a->name1, &addrl, &addrh);
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_LD_X, 17, 0);
+ // increment
+ Instruction(OP_INC, 16, 0);
+ DWORD noCarry = AllocFwdAddr();
+ Instruction(OP_BRNE, noCarry, 0);
+ Instruction(OP_INC, 17, 0);
+ FwdAddrIsNow(noCarry);
+ // X is still addrh
+ Instruction(OP_ST_X, 17, 0);
+ LoadXAddr(addrl);
+ Instruction(OP_ST_X, 16, 0);
+ break;
+ }
+ case INT_IF_BIT_SET: {
+ DWORD condFalse = AllocFwdAddr();
+ MemForSingleBit(a->name1, TRUE, &addr, &bit);
+ IfBitClear(addr, bit);
+ Instruction(OP_RJMP, condFalse, 0);
+ CompileIfBody(condFalse);
+ break;
+ }
+ case INT_IF_BIT_CLEAR: {
+ DWORD condFalse = AllocFwdAddr();
+ MemForSingleBit(a->name1, TRUE, &addr, &bit);
+ IfBitSet(addr, bit);
+ Instruction(OP_RJMP, condFalse, 0);
+ CompileIfBody(condFalse);
+ break;
+ }
+ case INT_IF_VARIABLE_LES_LITERAL: {
+ DWORD notTrue = AllocFwdAddr();
+
+ MemForVariable(a->name1, &addrl, &addrh);
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_LD_X, 17, 0);
+
+ Instruction(OP_LDI, 18, (a->literal & 0xff));
+ Instruction(OP_LDI, 19, (a->literal >> 8));
+
+ Instruction(OP_CP, 16, 18);
+ Instruction(OP_CPC, 17, 19);
+ Instruction(OP_BRGE, notTrue, 0);
+
+ CompileIfBody(notTrue);
+ break;
+ }
+ case INT_IF_VARIABLE_GRT_VARIABLE:
+ case INT_IF_VARIABLE_EQUALS_VARIABLE: {
+ DWORD notTrue = AllocFwdAddr();
+
+ MemForVariable(a->name1, &addrl, &addrh);
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_LD_X, 17, 0);
+ MemForVariable(a->name2, &addrl, &addrh);
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 18, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_LD_X, 19, 0);
+
+ if(a->op == INT_IF_VARIABLE_EQUALS_VARIABLE) {
+ Instruction(OP_CP, 16, 18);
+ Instruction(OP_CPC, 17, 19);
+ Instruction(OP_BRNE, notTrue, 0);
+ } else if(a->op == INT_IF_VARIABLE_GRT_VARIABLE) {
+ DWORD isTrue = AllocFwdAddr();
+
+ // true if op1 > op2
+ // false if op1 >= op2
+ Instruction(OP_CP, 18, 16);
+ Instruction(OP_CPC, 19, 17);
+ Instruction(OP_BRGE, notTrue, 0);
+ } else oops();
+ CompileIfBody(notTrue);
+ break;
+ }
+ case INT_SET_VARIABLE_TO_VARIABLE:
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForVariable(a->name2, &addrl2, &addrh2);
+
+ LoadXAddr(addrl2);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrl);
+ Instruction(OP_ST_X, 16, 0);
+
+ LoadXAddr(addrh2);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_ST_X, 16, 0);
+ break;
+
+ case INT_SET_VARIABLE_DIVIDE:
+ // Do this one separately since the divide routine uses
+ // slightly different in/out registers and I don't feel like
+ // modifying it.
+ MemForVariable(a->name2, &addrl, &addrh);
+ MemForVariable(a->name3, &addrl2, &addrh2);
+
+ LoadXAddr(addrl2);
+ Instruction(OP_LD_X, 18, 0);
+ LoadXAddr(addrh2);
+ Instruction(OP_LD_X, 19, 0);
+
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_LD_X, 17, 0);
+
+ CallSubroutine(DivideAddress);
+ DivideUsed = TRUE;
+
+ MemForVariable(a->name1, &addrl, &addrh);
+
+ LoadXAddr(addrl);
+ Instruction(OP_ST_X, 16, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_ST_X, 17, 0);
+ break;
+
+ case INT_SET_VARIABLE_ADD:
+ case INT_SET_VARIABLE_SUBTRACT:
+ case INT_SET_VARIABLE_MULTIPLY:
+ MemForVariable(a->name2, &addrl, &addrh);
+ MemForVariable(a->name3, &addrl2, &addrh2);
+
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 18, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_LD_X, 19, 0);
+
+ LoadXAddr(addrl2);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrh2);
+ Instruction(OP_LD_X, 17, 0);
+
+ if(a->op == INT_SET_VARIABLE_ADD) {
+ Instruction(OP_ADD, 18, 16);
+ Instruction(OP_ADC, 19, 17);
+ } else if(a->op == INT_SET_VARIABLE_SUBTRACT) {
+ Instruction(OP_SUB, 18, 16);
+ Instruction(OP_SBC, 19, 17);
+ } else if(a->op == INT_SET_VARIABLE_MULTIPLY) {
+ CallSubroutine(MultiplyAddress);
+ MultiplyUsed = TRUE;
+ } else oops();
+
+ MemForVariable(a->name1, &addrl, &addrh);
+
+ LoadXAddr(addrl);
+ Instruction(OP_ST_X, 18, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_ST_X, 19, 0);
+ break;
+
+ case INT_SET_PWM: {
+ int target = atoi(a->name2);
+
+ // PWM frequency is
+ // target = xtal/(256*prescale)
+ // so not a lot of room for accurate frequency here
+
+ int prescale;
+ int bestPrescale;
+ int bestError = INT_MAX;
+ int bestFreq;
+ for(prescale = 1;;) {
+ int freq = (Prog.mcuClock + prescale*128)/(prescale*256);
+
+ int err = abs(freq - target);
+ if(err < bestError) {
+ bestError = err;
+ bestPrescale = prescale;
+ bestFreq = freq;
+ }
+
+ if(prescale == 1) {
+ prescale = 8;
+ } else if(prescale == 8) {
+ prescale = 64;
+ } else if(prescale == 64) {
+ prescale = 256;
+ } else if(prescale == 256) {
+ prescale = 1024;
+ } else {
+ break;
+ }
+ }
+
+ if(((double)bestError)/target > 0.05) {
+ Error(_("Target frequency %d Hz, closest achievable is "
+ "%d Hz (warning, >5%% error)."), target, bestFreq);
+ }
+
+ DivideUsed = TRUE; MultiplyUsed = TRUE;
+ MemForVariable(a->name1, &addrl, &addrh);
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 16, 0);
+ Instruction(OP_LDI, 17, 0);
+ Instruction(OP_LDI, 19, 0);
+ Instruction(OP_LDI, 18, 255);
+ CallSubroutine(MultiplyAddress);
+ Instruction(OP_MOV, 17, 19);
+ Instruction(OP_MOV, 16, 18);
+ Instruction(OP_LDI, 19, 0);
+ Instruction(OP_LDI, 18, 100);
+ CallSubroutine(DivideAddress);
+ LoadXAddr(REG_OCR2);
+ Instruction(OP_ST_X, 16, 0);
+
+ // Setup only happens once
+ MemForSingleBit("$pwm_init", FALSE, &addr, &bit);
+ DWORD skip = AllocFwdAddr();
+ IfBitSet(addr, bit);
+ Instruction(OP_RJMP, skip, 0);
+ SetBit(addr, bit);
+
+ BYTE cs;
+ switch(bestPrescale) {
+ case 1: cs = 1; break;
+ case 8: cs = 2; break;
+ case 64: cs = 3; break;
+ case 256: cs = 4; break;
+ case 1024: cs = 5; break;
+ default: oops(); break;
+ }
+
+ // fast PWM mode, non-inverted operation, given prescale
+ WriteMemory(REG_TCCR2, (1 << 6) | (1 << 3) | (1 << 5) | cs);
+
+ FwdAddrIsNow(skip);
+
+ break;
+ }
+ case INT_EEPROM_BUSY_CHECK: {
+ MemForSingleBit(a->name1, FALSE, &addr, &bit);
+
+ DWORD isBusy = AllocFwdAddr();
+ DWORD done = AllocFwdAddr();
+ IfBitSet(REG_EECR, 1);
+ Instruction(OP_RJMP, isBusy, 0);
+
+ IfBitClear(EepromHighByteWaitingAddr, EepromHighByteWaitingBit);
+ Instruction(OP_RJMP, done, 0);
+
+ // Just increment EEARH:EEARL, to point to the high byte of
+ // whatever we just wrote the low byte for.
+ LoadXAddr(REG_EEARL);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(REG_EEARH);
+ Instruction(OP_LD_X, 17, 0);
+ Instruction(OP_INC, 16, 0);
+ DWORD noCarry = AllocFwdAddr();
+ Instruction(OP_BRNE, noCarry, 0);
+ Instruction(OP_INC, 17, 0);
+ FwdAddrIsNow(noCarry);
+ // X is still REG_EEARH
+ Instruction(OP_ST_X, 17, 0);
+ LoadXAddr(REG_EEARL);
+ Instruction(OP_ST_X, 16, 0);
+
+ LoadXAddr(EepromHighByte);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(REG_EEDR);
+ Instruction(OP_ST_X, 16, 0);
+ LoadXAddr(REG_EECR);
+ Instruction(OP_LDI, 16, 0x04);
+ Instruction(OP_ST_X, 16, 0);
+ Instruction(OP_LDI, 16, 0x06);
+ Instruction(OP_ST_X, 16, 0);
+
+ ClearBit(EepromHighByteWaitingAddr, EepromHighByteWaitingBit);
+
+ FwdAddrIsNow(isBusy);
+ SetBit(addr, bit);
+ FwdAddrIsNow(done);
+ break;
+ }
+ case INT_EEPROM_READ: {
+ MemForVariable(a->name1, &addrl, &addrh);
+ int i;
+ for(i = 0; i < 2; i++) {
+ WriteMemory(REG_EEARH, ((a->literal+i) >> 8));
+ WriteMemory(REG_EEARL, ((a->literal+i) & 0xff));
+ WriteMemory(REG_EECR, 0x01);
+ LoadXAddr(REG_EEDR);
+ Instruction(OP_LD_X, 16, 0);
+ if(i == 0) {
+ LoadXAddr(addrl);
+ } else {
+ LoadXAddr(addrh);
+ }
+ Instruction(OP_ST_X, 16, 0);
+ }
+ break;
+ }
+ case INT_EEPROM_WRITE:
+ MemForVariable(a->name1, &addrl, &addrh);
+ SetBit(EepromHighByteWaitingAddr, EepromHighByteWaitingBit);
+ LoadXAddr(addrh);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(EepromHighByte);
+ Instruction(OP_ST_X, 16, 0);
+
+ WriteMemory(REG_EEARH, (a->literal >> 8));
+ WriteMemory(REG_EEARL, (a->literal & 0xff));
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(REG_EEDR);
+ Instruction(OP_ST_X, 16, 0);
+ LoadXAddr(REG_EECR);
+ Instruction(OP_LDI, 16, 0x04);
+ Instruction(OP_ST_X, 16, 0);
+ Instruction(OP_LDI, 16, 0x06);
+ Instruction(OP_ST_X, 16, 0);
+ break;
+
+ case INT_READ_ADC: {
+ MemForVariable(a->name1, &addrl, &addrh);
+
+ WriteMemory(REG_ADMUX,
+ (0 << 6) | // AREF, internal Vref odd
+ (0 << 5) | // right-adjusted
+ MuxForAdcVariable(a->name1));
+
+ // target something around 200 kHz for the ADC clock, for
+ // 25/(200k) or 125 us conversion time, reasonable
+ int divisor = (Prog.mcuClock / 200000);
+ int j = 0;
+ for(j = 1; j <= 7; j++) {
+ if((1 << j) > divisor) break;
+ }
+
+ BYTE adcsra =
+ (1 << 7) | // ADC enabled
+ (0 << 5) | // not free running
+ (0 << 3) | // no interrupt enabled
+ j; // prescaler setup
+
+ WriteMemory(REG_ADCSRA, adcsra);
+ WriteMemory(REG_ADCSRA, (BYTE)(adcsra | (1 << 6)));
+
+ DWORD waitForFinsh = AvrProgWriteP;
+ IfBitSet(REG_ADCSRA, 6);
+ Instruction(OP_RJMP, waitForFinsh, 0);
+
+ LoadXAddr(REG_ADCL);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrl);
+ Instruction(OP_ST_X, 16, 0);
+
+ LoadXAddr(REG_ADCH);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrh);
+ Instruction(OP_ST_X, 16, 0);
+
+ break;
+ }
+ case INT_UART_SEND: {
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForSingleBit(a->name2, TRUE, &addr, &bit);
+
+ DWORD noSend = AllocFwdAddr();
+ IfBitClear(addr, bit);
+ Instruction(OP_RJMP, noSend, 0);
+
+ LoadXAddr(addrl);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(REG_UDR);
+ Instruction(OP_ST_X, 16, 0);
+
+ FwdAddrIsNow(noSend);
+
+ ClearBit(addr, bit);
+ DWORD dontSet = AllocFwdAddr();
+ IfBitSet(REG_UCSRA, 5); // UDRE, is 1 when tx buffer is empty
+ Instruction(OP_RJMP, dontSet, 0);
+ SetBit(addr, bit);
+ FwdAddrIsNow(dontSet);
+
+ break;
+ }
+ case INT_UART_RECV: {
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForSingleBit(a->name2, TRUE, &addr, &bit);
+
+ ClearBit(addr, bit);
+
+ DWORD noChar = AllocFwdAddr();
+ IfBitClear(REG_UCSRA, 7);
+ Instruction(OP_RJMP, noChar, 0);
+
+ SetBit(addr, bit);
+ LoadXAddr(REG_UDR);
+ Instruction(OP_LD_X, 16, 0);
+ LoadXAddr(addrl);
+ Instruction(OP_ST_X, 16, 0);
+
+ LoadXAddr(addrh);
+ Instruction(OP_LDI, 16, 0);
+ Instruction(OP_ST_X, 16, 0);
+
+ FwdAddrIsNow(noChar);
+ break;
+ }
+ case INT_END_IF:
+ case INT_ELSE:
+ return;
+
+ case INT_SIMULATE_NODE_STATE:
+ case INT_COMMENT:
+ break;
+
+ default:
+ oops();
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 16x16 signed multiply, code from Atmel app note AVR200. op1 in r17:16,
+// op2 in r19:18, result low word goes into r19:18.
+//-----------------------------------------------------------------------------
+static void MultiplyRoutine(void)
+{
+ FwdAddrIsNow(MultiplyAddress);
+
+ DWORD m16s_1;
+ DWORD m16s_2 = AllocFwdAddr();
+
+ Instruction(OP_SUB, 21, 21);
+ Instruction(OP_SUB, 20, 20);
+ Instruction(OP_LDI, 22, 16);
+ m16s_1 = AvrProgWriteP; Instruction(OP_BRCC, m16s_2, 0);
+ Instruction(OP_ADD, 20, 16);
+ Instruction(OP_ADC, 21, 17);
+ FwdAddrIsNow(m16s_2); Instruction(OP_SBRC, 18, 0);
+ Instruction(OP_SUB, 20, 16);
+ Instruction(OP_SBRC, 18, 0);
+ Instruction(OP_SBC, 21, 17);
+ Instruction(OP_ASR, 21, 0);
+ Instruction(OP_ROR, 20, 0);
+ Instruction(OP_ROR, 19, 0);
+ Instruction(OP_ROR, 18, 0);
+ Instruction(OP_DEC, 22, 0);
+ Instruction(OP_BRNE, m16s_1, 0);
+ Instruction(OP_RET, 0, 0);
+}
+
+//-----------------------------------------------------------------------------
+// 16/16 signed divide, code from the same app note. Dividend in r17:16,
+// divisor in r19:18, result goes in r17:16 (and remainder in r15:14).
+//-----------------------------------------------------------------------------
+static void DivideRoutine(void)
+{
+ FwdAddrIsNow(DivideAddress);
+
+ DWORD d16s_1 = AllocFwdAddr();
+ DWORD d16s_2 = AllocFwdAddr();
+ DWORD d16s_3;
+ DWORD d16s_4 = AllocFwdAddr();
+ DWORD d16s_5 = AllocFwdAddr();
+ DWORD d16s_6 = AllocFwdAddr();
+
+ Instruction(OP_MOV, 13, 17);
+ Instruction(OP_EOR, 13, 19);
+ Instruction(OP_SBRS, 17, 7);
+ Instruction(OP_RJMP, d16s_1, 0);
+ Instruction(OP_COM, 17, 0);
+ Instruction(OP_COM, 16, 0);
+ Instruction(OP_SUBI, 16, 0xff);
+ Instruction(OP_SBCI, 17, 0xff);
+ FwdAddrIsNow(d16s_1); Instruction(OP_SBRS, 19, 7);
+ Instruction(OP_RJMP, d16s_2, 0);
+ Instruction(OP_COM, 19, 0);
+ Instruction(OP_COM, 18, 0);
+ Instruction(OP_SUBI, 18, 0xff);
+ Instruction(OP_SBCI, 19, 0xff);
+ FwdAddrIsNow(d16s_2); Instruction(OP_EOR, 14, 14);
+ Instruction(OP_SUB, 15, 15);
+ Instruction(OP_LDI, 20, 17);
+
+ d16s_3 = AvrProgWriteP; Instruction(OP_ADC, 16, 16);
+ Instruction(OP_ADC, 17, 17);
+ Instruction(OP_DEC, 20, 0);
+ Instruction(OP_BRNE, d16s_5, 0);
+ Instruction(OP_SBRS, 13, 7);
+ Instruction(OP_RJMP, d16s_4, 0);
+ Instruction(OP_COM, 17, 0);
+ Instruction(OP_COM, 16, 0);
+ Instruction(OP_SUBI, 16, 0xff);
+ Instruction(OP_SBCI, 17, 0xff);
+ FwdAddrIsNow(d16s_4); Instruction(OP_RET, 0, 0);
+ FwdAddrIsNow(d16s_5); Instruction(OP_ADC, 14, 14);
+ Instruction(OP_ADC, 15, 15);
+ Instruction(OP_SUB, 14, 18);
+ Instruction(OP_SBC, 15, 19);
+ Instruction(OP_BRCC, d16s_6, 0);
+ Instruction(OP_ADD, 14, 18);
+ Instruction(OP_ADC, 15, 19);
+ Instruction(OP_CLC, 0, 0);
+ Instruction(OP_RJMP, d16s_3, 0);
+ FwdAddrIsNow(d16s_6); Instruction(OP_SEC, 0, 0);
+ Instruction(OP_RJMP, d16s_3, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Compile the program to REG code for the currently selected processor
+// and write it to the given file. Produce an error message if we cannot
+// write to the file, or if there is something inconsistent about the
+// program.
+//-----------------------------------------------------------------------------
+void CompileAvr(char *outFile)
+{
+ FILE *f = fopen(outFile, "w");
+ if(!f) {
+ Error(_("Couldn't open file '%s'"), outFile);
+ return;
+ }
+
+ if(setjmp(CompileErrorBuf) != 0) {
+ fclose(f);
+ return;
+ }
+
+ // Here we must set up the addresses of some registers that for some
+ // stupid reason move around from AVR to AVR.
+ if(strcmp(Prog.mcu->mcuName, "Atmel AVR ATmega16 40-PDIP")==0 ||
+ strcmp(Prog.mcu->mcuName, "Atmel AVR ATmega32 40-PDIP")==0 ||
+ strcmp(Prog.mcu->mcuName, "Atmel AVR ATmega162 40-PDIP")==0 ||
+ strcmp(Prog.mcu->mcuName, "Atmel AVR ATmega8 28-PDIP")==0)
+ {
+ REG_TIMSK = 0x59;
+ REG_TIFR = 0x58;
+ REG_UBRRH = 0x40;
+ REG_UBRRL = 0x29;
+ REG_UCSRB = 0x2a;
+ REG_UCSRA = 0x2b;
+ REG_UDR = 0x2c;
+ } else {
+ REG_TIMSK = 0x57;
+ REG_TIFR = 0x56;
+ REG_UBRRH = 0x98;
+ REG_UBRRL = 0x99;
+ REG_UCSRB = 0x9a;
+ REG_UCSRA = 0x9b;
+ REG_UDR = 0x9c;
+ }
+
+ WipeMemory();
+ MultiplyUsed = FALSE;
+ MultiplyAddress = AllocFwdAddr();
+ DivideUsed = FALSE;
+ DivideAddress = AllocFwdAddr();
+ AllocStart();
+
+ // Where we hold the high byte to program in EEPROM while the low byte
+ // programs.
+ EepromHighByte = AllocOctetRam();
+ AllocBitRam(&EepromHighByteWaitingAddr, &EepromHighByteWaitingBit);
+
+ WriteRuntime();
+ IntPc = 0;
+ CompileFromIntermediate();
+
+ if(Prog.mcu->avrUseIjmp) {
+ Instruction(OP_LDI, 30, (BeginningOfCycleAddr & 0xff));
+ Instruction(OP_LDI, 31, (BeginningOfCycleAddr >> 8));
+ Instruction(OP_IJMP, 0, 0);
+ } else {
+ Instruction(OP_RJMP, BeginningOfCycleAddr, 0);
+ }
+
+ MemCheckForErrorsPostCompile();
+
+ if(MultiplyUsed) MultiplyRoutine();
+ if(DivideUsed) DivideRoutine();
+
+ WriteHexFile(f);
+ fclose(f);
+
+ char str[MAX_PATH+500];
+ sprintf(str, _("Compile successful; wrote IHEX for AVR to '%s'.\r\n\r\n"
+ "Remember to set the processor configuration (fuses) correctly. "
+ "This does not happen automatically."), outFile);
+ CompileSuccessfulMessage(str);
+}
diff --git a/ldmicro/circuit.cpp b/ldmicro/circuit.cpp
new file mode 100644
index 0000000..2237f0e
--- /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 .
+//------
+//
+// Routines for modifying the circuit: add a particular element at a
+// particular point, delete the selected element, etc.
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+
+#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;
+}
diff --git a/ldmicro/coildialog.cpp b/ldmicro/coildialog.cpp
new file mode 100644
index 0000000..96772e0
--- /dev/null
+++ b/ldmicro/coildialog.cpp
@@ -0,0 +1,210 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Dialog for setting the properties of a relay coils: negated or not,
+// plus the name, plus set-only or reset-only
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+static HWND CoilDialog;
+
+static HWND SourceInternalRelayRadio;
+static HWND SourceMcuPinRadio;
+static HWND NegatedRadio;
+static HWND NormalRadio;
+static HWND SetOnlyRadio;
+static HWND ResetOnlyRadio;
+static HWND NameTextbox;
+
+static LONG_PTR PrevNameProc;
+
+//-----------------------------------------------------------------------------
+// Don't allow any characters other than A-Za-z0-9_ in the name.
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK MyNameProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam)
+{
+ if(msg == WM_CHAR) {
+ if(!(isalpha(wParam) || isdigit(wParam) || wParam == '_' ||
+ wParam == '\b'))
+ {
+ return 0;
+ }
+ }
+
+ return CallWindowProc((WNDPROC)PrevNameProc, hwnd, msg, wParam, lParam);
+}
+
+static void MakeControls(void)
+{
+ HWND grouper = CreateWindowEx(0, WC_BUTTON, _("Type"),
+ WS_CHILD | BS_GROUPBOX | WS_VISIBLE | WS_TABSTOP,
+ 7, 3, 120, 105, CoilDialog, NULL, Instance, NULL);
+ NiceFont(grouper);
+
+ NormalRadio = CreateWindowEx(0, WC_BUTTON, _("( ) Normal"),
+ WS_CHILD | BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE | WS_GROUP,
+ 16, 21, 100, 20, CoilDialog, NULL, Instance, NULL);
+ NiceFont(NormalRadio);
+
+ NegatedRadio = CreateWindowEx(0, WC_BUTTON, _("(/) Negated"),
+ WS_CHILD | BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE,
+ 16, 41, 100, 20, CoilDialog, NULL, Instance, NULL);
+ NiceFont(NegatedRadio);
+
+ SetOnlyRadio = CreateWindowEx(0, WC_BUTTON, _("(S) Set-Only"),
+ WS_CHILD | BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE,
+ 16, 61, 100, 20, CoilDialog, NULL, Instance, NULL);
+ NiceFont(SetOnlyRadio);
+
+ ResetOnlyRadio = CreateWindowEx(0, WC_BUTTON, _("(R) Reset-Only"),
+ WS_CHILD | BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE,
+ 16, 81, 105, 20, CoilDialog, NULL, Instance, NULL);
+ NiceFont(ResetOnlyRadio);
+
+ HWND grouper2 = CreateWindowEx(0, WC_BUTTON, _("Source"),
+ WS_CHILD | BS_GROUPBOX | WS_VISIBLE,
+ 140, 3, 120, 65, CoilDialog, NULL, Instance, NULL);
+ NiceFont(grouper2);
+
+ SourceInternalRelayRadio = CreateWindowEx(0, WC_BUTTON, _("Internal Relay"),
+ WS_CHILD | BS_AUTORADIOBUTTON | WS_VISIBLE | WS_GROUP | WS_TABSTOP,
+ 149, 21, 100, 20, CoilDialog, NULL, Instance, NULL);
+ NiceFont(SourceInternalRelayRadio);
+
+ SourceMcuPinRadio = CreateWindowEx(0, WC_BUTTON, _("Pin on MCU"),
+ WS_CHILD | BS_AUTORADIOBUTTON | WS_VISIBLE | WS_TABSTOP,
+ 149, 41, 100, 20, CoilDialog, NULL, Instance, NULL);
+ NiceFont(SourceMcuPinRadio);
+
+ HWND textLabel = CreateWindowEx(0, WC_STATIC, _("Name:"),
+ WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SS_RIGHT,
+ 135, 80, 50, 21, CoilDialog, NULL, Instance, NULL);
+ NiceFont(textLabel);
+
+ NameTextbox = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "",
+ WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 190, 80, 155, 21, CoilDialog, NULL, Instance, NULL);
+ FixedFont(NameTextbox);
+
+ OkButton = CreateWindowEx(0, WC_BUTTON, _("OK"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_DEFPUSHBUTTON,
+ 276, 10, 70, 23, CoilDialog, NULL, Instance, NULL);
+ NiceFont(OkButton);
+
+ CancelButton = CreateWindowEx(0, WC_BUTTON, _("Cancel"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 276, 40, 70, 23, CoilDialog, NULL, Instance, NULL);
+ NiceFont(CancelButton);
+
+ PrevNameProc = SetWindowLongPtr(NameTextbox, GWLP_WNDPROC,
+ (LONG_PTR)MyNameProc);
+}
+
+void ShowCoilDialog(BOOL *negated, BOOL *setOnly, BOOL *resetOnly, char *name)
+{
+ CoilDialog = CreateWindowClient(0, "LDmicroDialog",
+ _("Coil"), WS_OVERLAPPED | WS_SYSMENU,
+ 100, 100, 359, 115, NULL, NULL, Instance, NULL);
+ RECT r;
+ GetClientRect(CoilDialog, &r);
+
+ MakeControls();
+
+ if(name[0] == 'R') {
+ SendMessage(SourceInternalRelayRadio, BM_SETCHECK, BST_CHECKED, 0);
+ } else {
+ SendMessage(SourceMcuPinRadio, BM_SETCHECK, BST_CHECKED, 0);
+ }
+ SendMessage(NameTextbox, WM_SETTEXT, 0, (LPARAM)(name + 1));
+ if(*negated) {
+ SendMessage(NegatedRadio, BM_SETCHECK, BST_CHECKED, 0);
+ } else if(*setOnly) {
+ SendMessage(SetOnlyRadio, BM_SETCHECK, BST_CHECKED, 0);
+ } else if(*resetOnly) {
+ SendMessage(ResetOnlyRadio, BM_SETCHECK, BST_CHECKED, 0);
+ } else {
+ SendMessage(NormalRadio, BM_SETCHECK, BST_CHECKED, 0);
+ }
+
+ EnableWindow(MainWindow, FALSE);
+ ShowWindow(CoilDialog, TRUE);
+ SetFocus(NameTextbox);
+ SendMessage(NameTextbox, EM_SETSEL, 0, -1);
+
+ MSG msg;
+ DWORD ret;
+ DialogDone = FALSE;
+ DialogCancel = FALSE;
+ while((ret = GetMessage(&msg, NULL, 0, 0)) && !DialogDone) {
+ if(msg.message == WM_KEYDOWN) {
+ if(msg.wParam == VK_RETURN) {
+ DialogDone = TRUE;
+ break;
+ } else if(msg.wParam == VK_ESCAPE) {
+ DialogDone = TRUE;
+ DialogCancel = TRUE;
+ break;
+ }
+ }
+
+ if(IsDialogMessage(CoilDialog, &msg)) continue;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ if(!DialogCancel) {
+ if(SendMessage(SourceInternalRelayRadio, BM_GETSTATE, 0, 0)
+ & BST_CHECKED)
+ {
+ name[0] = 'R';
+ } else {
+ name[0] = 'Y';
+ }
+ SendMessage(NameTextbox, WM_GETTEXT, (WPARAM)16, (LPARAM)(name+1));
+
+ if(SendMessage(NormalRadio, BM_GETSTATE, 0, 0) & BST_CHECKED) {
+ *negated = FALSE;
+ *setOnly = FALSE;
+ *resetOnly = FALSE;
+ } else if(SendMessage(NegatedRadio, BM_GETSTATE, 0, 0) & BST_CHECKED) {
+ *negated = TRUE;
+ *setOnly = FALSE;
+ *resetOnly = FALSE;
+ } else if(SendMessage(SetOnlyRadio, BM_GETSTATE, 0, 0) & BST_CHECKED) {
+ *negated = FALSE;
+ *setOnly = TRUE;
+ *resetOnly = FALSE;
+ } else if(SendMessage(ResetOnlyRadio, BM_GETSTATE, 0, 0) & BST_CHECKED)
+ {
+ *negated = FALSE;
+ *setOnly = FALSE;
+ *resetOnly = TRUE;
+ }
+ }
+
+ EnableWindow(MainWindow, TRUE);
+ DestroyWindow(CoilDialog);
+ return;
+}
diff --git a/ldmicro/commentdialog.cpp b/ldmicro/commentdialog.cpp
new file mode 100644
index 0000000..beff989
--- /dev/null
+++ b/ldmicro/commentdialog.cpp
@@ -0,0 +1,97 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Dialog to enter the text of a comment; make it long and skinny to
+// encourage people to write it the way it will look on the diagram.
+// Jonathan Westhues, Jun 2005
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+static HWND CommentDialog;
+
+static HWND CommentTextbox;
+
+static void MakeControls(void)
+{
+ CommentTextbox = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "",
+ WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE |
+ ES_MULTILINE | ES_WANTRETURN,
+ 7, 10, 600, 38, CommentDialog, NULL, Instance, NULL);
+ FixedFont(CommentTextbox);
+
+ OkButton = CreateWindowEx(0, WC_BUTTON, _("OK"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_DEFPUSHBUTTON,
+ 620, 6, 70, 23, CommentDialog, NULL, Instance, NULL);
+ NiceFont(OkButton);
+
+ CancelButton = CreateWindowEx(0, WC_BUTTON, _("Cancel"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 620, 36, 70, 23, CommentDialog, NULL, Instance, NULL);
+ NiceFont(CancelButton);
+}
+
+void ShowCommentDialog(char *comment)
+{
+ CommentDialog = CreateWindowClient(0, "LDmicroDialog",
+ _("Comment"), WS_OVERLAPPED | WS_SYSMENU,
+ 100, 100, 700, 65, NULL, NULL, Instance, NULL);
+
+ MakeControls();
+
+ SendMessage(CommentTextbox, WM_SETTEXT, 0, (LPARAM)comment);
+
+ EnableWindow(MainWindow, FALSE);
+ ShowWindow(CommentDialog, TRUE);
+ SetFocus(CommentTextbox);
+ SendMessage(CommentTextbox, EM_SETSEL, 0, -1);
+
+ MSG msg;
+ DWORD ret;
+ DialogDone = FALSE;
+ DialogCancel = FALSE;
+ while((ret = GetMessage(&msg, NULL, 0, 0)) && !DialogDone) {
+ if(msg.message == WM_KEYDOWN) {
+ if(msg.wParam == VK_TAB && GetFocus() == CommentTextbox) {
+ SetFocus(OkButton);
+ continue;
+ } else if(msg.wParam == VK_ESCAPE) {
+ DialogDone = TRUE;
+ DialogCancel = TRUE;
+ break;
+ }
+ }
+
+ if(IsDialogMessage(CommentDialog, &msg)) continue;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ if(!DialogCancel) {
+ SendMessage(CommentTextbox, WM_GETTEXT, (WPARAM)(MAX_COMMENT_LEN-1),
+ (LPARAM)comment);
+ }
+
+ EnableWindow(MainWindow, TRUE);
+ DestroyWindow(CommentDialog);
+ return;
+}
diff --git a/ldmicro/compilecommon.cpp b/ldmicro/compilecommon.cpp
new file mode 100644
index 0000000..cc3238e
--- /dev/null
+++ b/ldmicro/compilecommon.cpp
@@ -0,0 +1,373 @@
+//-----------------------------------------------------------------------------
+// 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."));
+}
diff --git a/ldmicro/confdialog.cpp b/ldmicro/confdialog.cpp
new file mode 100644
index 0000000..b0bfbcd
--- /dev/null
+++ b/ldmicro/confdialog.cpp
@@ -0,0 +1,249 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Dialog for setting the overall PLC parameters. Mostly this relates to
+// timing; to set up the timers we need to know the desired cycle time,
+// which is configurable, plus the MCU clock (i.e. crystal frequency).
+// Jonathan Westhues, Nov 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+static HWND ConfDialog;
+
+static HWND CrystalTextbox;
+static HWND CycleTextbox;
+static HWND BaudTextbox;
+
+static LONG_PTR PrevCrystalProc;
+static LONG_PTR PrevCycleProc;
+static LONG_PTR PrevBaudProc;
+
+//-----------------------------------------------------------------------------
+// Don't allow any characters other than 0-9. in the text boxes.
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK MyNumberProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam)
+{
+ if(msg == WM_CHAR) {
+ if(!(isdigit(wParam) || wParam == '.' || wParam == '\b')) {
+ return 0;
+ }
+ }
+
+ LONG_PTR t;
+ if(hwnd == CrystalTextbox)
+ t = PrevCrystalProc;
+ else if(hwnd == CycleTextbox)
+ t = PrevCycleProc;
+ else if(hwnd == BaudTextbox)
+ t = PrevBaudProc;
+ else
+ oops();
+
+ return CallWindowProc((WNDPROC)t, hwnd, msg, wParam, lParam);
+}
+
+static void MakeControls(void)
+{
+ HWND textLabel = CreateWindowEx(0, WC_STATIC, _("Cycle Time (ms):"),
+ WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SS_RIGHT,
+ 5, 13, 145, 21, ConfDialog, NULL, Instance, NULL);
+ NiceFont(textLabel);
+
+ CycleTextbox = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "",
+ WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 155, 12, 85, 21, ConfDialog, NULL, Instance, NULL);
+ NiceFont(CycleTextbox);
+
+ HWND textLabel2 = CreateWindowEx(0, WC_STATIC,
+ _("Crystal Frequency (MHz):"),
+ WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SS_RIGHT,
+ 0, 43, 150, 21, ConfDialog, NULL, Instance, NULL);
+ NiceFont(textLabel2);
+
+ CrystalTextbox = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "",
+ WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 155, 42, 85, 21, ConfDialog, NULL, Instance, NULL);
+ NiceFont(CrystalTextbox);
+
+ HWND textLabel3 = CreateWindowEx(0, WC_STATIC, _("UART Baud Rate (bps):"),
+ WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SS_RIGHT,
+ 5, 73, 145, 21, ConfDialog, NULL, Instance, NULL);
+ NiceFont(textLabel3);
+
+ BaudTextbox = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "",
+ WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 155, 72, 85, 21, ConfDialog, NULL, Instance, NULL);
+ NiceFont(BaudTextbox);
+
+ if(!UartFunctionUsed()) {
+ EnableWindow(BaudTextbox, FALSE);
+ EnableWindow(textLabel3, FALSE);
+ }
+
+ if(Prog.mcu && (Prog.mcu->whichIsa == ISA_ANSIC ||
+ Prog.mcu->whichIsa == ISA_INTERPRETED))
+ {
+ EnableWindow(CrystalTextbox, FALSE);
+ EnableWindow(textLabel2, FALSE);
+ }
+
+ OkButton = CreateWindowEx(0, WC_BUTTON, _("OK"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_DEFPUSHBUTTON,
+ 258, 11, 70, 23, ConfDialog, NULL, Instance, NULL);
+ NiceFont(OkButton);
+
+ CancelButton = CreateWindowEx(0, WC_BUTTON, _("Cancel"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 258, 41, 70, 23, ConfDialog, NULL, Instance, NULL);
+ NiceFont(CancelButton);
+
+ char explanation[1024] = "";
+
+ if(UartFunctionUsed()) {
+ if(Prog.mcu && Prog.mcu->uartNeeds.rxPin != 0) {
+ sprintf(explanation,
+ _("Serial (UART) will use pins %d and %d.\r\n\r\n"),
+ Prog.mcu->uartNeeds.rxPin, Prog.mcu->uartNeeds.txPin);
+ } else {
+ strcpy(explanation,
+ _("Please select a micro with a UART.\r\n\r\n"));
+ }
+ } else {
+ strcpy(explanation, _("No serial instructions (UART Send/UART Receive) "
+ "are in use; add one to program before setting baud rate.\r\n\r\n")
+ );
+ }
+
+ strcat(explanation,
+ _("The cycle time for the 'PLC' runtime generated by LDmicro is user-"
+ "configurable. Very short cycle times may not be achievable due "
+ "to processor speed constraints, and very long cycle times may not "
+ "be achievable due to hardware overflows. Cycle times between 10 ms "
+ "and 100 ms will usually be practical.\r\n\r\n"
+ "The compiler must know what speed crystal you are using with the "
+ "micro to convert between timing in clock cycles and timing in "
+ "seconds. A 4 MHz to 20 MHz crystal is typical; check the speed "
+ "grade of the part you are using to determine the maximum allowable "
+ "clock speed before choosing a crystal."));
+
+ HWND textLabel4 = CreateWindowEx(0, WC_STATIC, explanation,
+ WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 11, 104, 310, 400, ConfDialog, NULL, Instance, NULL);
+ NiceFont(textLabel4);
+
+ // Measure the explanation string, so that we know how to size our window
+ RECT tr, cr;
+ HDC hdc = CreateCompatibleDC(NULL);
+ SelectObject(hdc, MyNiceFont);
+ SetRect(&tr, 0, 0, 310, 400);
+ DrawText(hdc, explanation, -1, &tr, DT_CALCRECT |
+ DT_LEFT | DT_TOP | DT_WORDBREAK);
+ DeleteDC(hdc);
+ int h = 104 + tr.bottom + 10;
+ SetWindowPos(ConfDialog, NULL, 0, 0, 344, h, SWP_NOMOVE);
+ // h is the desired client height, but SetWindowPos includes title bar;
+ // so fix it up by hand
+ GetClientRect(ConfDialog, &cr);
+ int nh = h + (h - (cr.bottom - cr.top));
+ SetWindowPos(ConfDialog, NULL, 0, 0, 344, nh, SWP_NOMOVE);
+
+
+ PrevCycleProc = SetWindowLongPtr(CycleTextbox, GWLP_WNDPROC,
+ (LONG_PTR)MyNumberProc);
+
+ PrevCrystalProc = SetWindowLongPtr(CrystalTextbox, GWLP_WNDPROC,
+ (LONG_PTR)MyNumberProc);
+
+ PrevBaudProc = SetWindowLongPtr(BaudTextbox, GWLP_WNDPROC,
+ (LONG_PTR)MyNumberProc);
+}
+
+void ShowConfDialog(void)
+{
+ // The window's height will be resized later, to fit the explanation text.
+ ConfDialog = CreateWindowClient(0, "LDmicroDialog", _("PLC Configuration"),
+ WS_OVERLAPPED | WS_SYSMENU,
+ 100, 100, 0, 0, NULL, NULL, Instance, NULL);
+
+ MakeControls();
+
+ char buf[16];
+ sprintf(buf, "%.1f", (Prog.cycleTime / 1000.0));
+ SendMessage(CycleTextbox, WM_SETTEXT, 0, (LPARAM)buf);
+
+ sprintf(buf, "%.6f", Prog.mcuClock / 1e6);
+ SendMessage(CrystalTextbox, WM_SETTEXT, 0, (LPARAM)buf);
+
+ sprintf(buf, "%d", Prog.baudRate);
+ SendMessage(BaudTextbox, WM_SETTEXT, 0, (LPARAM)buf);
+
+ EnableWindow(MainWindow, FALSE);
+ ShowWindow(ConfDialog, TRUE);
+ SetFocus(CycleTextbox);
+
+ MSG msg;
+ DWORD ret;
+ DialogDone = FALSE;
+ DialogCancel = FALSE;
+ while((ret = GetMessage(&msg, NULL, 0, 0)) && !DialogDone) {
+ if(msg.message == WM_KEYDOWN) {
+ if(msg.wParam == VK_RETURN) {
+ DialogDone = TRUE;
+ break;
+ } else if(msg.wParam == VK_ESCAPE) {
+ DialogDone = TRUE;
+ DialogCancel = TRUE;
+ break;
+ }
+ }
+
+ if(IsDialogMessage(ConfDialog, &msg)) continue;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ if(!DialogCancel) {
+ char buf[16];
+ SendMessage(CycleTextbox, WM_GETTEXT, (WPARAM)sizeof(buf),
+ (LPARAM)(buf));
+ Prog.cycleTime = (int)(1000*atof(buf) + 0.5);
+ if(Prog.cycleTime == 0) {
+ Error(_("Zero cycle time not valid; resetting to 10 ms."));
+ Prog.cycleTime = 10000;
+ }
+
+ SendMessage(CrystalTextbox, WM_GETTEXT, (WPARAM)sizeof(buf),
+ (LPARAM)(buf));
+ Prog.mcuClock = (int)(1e6*atof(buf) + 0.5);
+
+ SendMessage(BaudTextbox, WM_GETTEXT, (WPARAM)sizeof(buf),
+ (LPARAM)(buf));
+ Prog.baudRate = atoi(buf);
+ }
+
+ EnableWindow(MainWindow, TRUE);
+ DestroyWindow(ConfDialog);
+ return;
+}
diff --git a/ldmicro/contactsdialog.cpp b/ldmicro/contactsdialog.cpp
new file mode 100644
index 0000000..e5611b8
--- /dev/null
+++ b/ldmicro/contactsdialog.cpp
@@ -0,0 +1,177 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Dialog for setting the properties of a set of contacts: negated or not,
+// plus the name
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+static HWND ContactsDialog;
+
+static HWND NegatedCheckbox;
+static HWND SourceInternalRelayRadio;
+static HWND SourceInputPinRadio;
+static HWND SourceOutputPinRadio;
+static HWND NameTextbox;
+
+static LONG_PTR PrevNameProc;
+
+//-----------------------------------------------------------------------------
+// Don't allow any characters other than A-Za-z0-9_ in the name.
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK MyNameProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam)
+{
+ if(msg == WM_CHAR) {
+ if(!(isalpha(wParam) || isdigit(wParam) || wParam == '_' ||
+ wParam == '\b'))
+ {
+ return 0;
+ }
+ }
+
+ return CallWindowProc((WNDPROC)PrevNameProc, hwnd, msg, wParam, lParam);
+}
+
+static void MakeControls(void)
+{
+ HWND grouper = CreateWindowEx(0, WC_BUTTON, _("Source"),
+ WS_CHILD | BS_GROUPBOX | WS_VISIBLE,
+ 7, 3, 120, 85, ContactsDialog, NULL, Instance, NULL);
+ NiceFont(grouper);
+
+ SourceInternalRelayRadio = CreateWindowEx(0, WC_BUTTON, _("Internal Relay"),
+ WS_CHILD | BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE,
+ 16, 21, 100, 20, ContactsDialog, NULL, Instance, NULL);
+ NiceFont(SourceInternalRelayRadio);
+
+ SourceInputPinRadio = CreateWindowEx(0, WC_BUTTON, _("Input pin"),
+ WS_CHILD | BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE,
+ 16, 41, 100, 20, ContactsDialog, NULL, Instance, NULL);
+ NiceFont(SourceInputPinRadio);
+
+ SourceOutputPinRadio = CreateWindowEx(0, WC_BUTTON, _("Output pin"),
+ WS_CHILD | BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE,
+ 16, 61, 100, 20, ContactsDialog, NULL, Instance, NULL);
+ NiceFont(SourceOutputPinRadio);
+
+ HWND textLabel = CreateWindowEx(0, WC_STATIC, _("Name:"),
+ WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SS_RIGHT,
+ 135, 16, 50, 21, ContactsDialog, NULL, Instance, NULL);
+ NiceFont(textLabel);
+
+ NameTextbox = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "",
+ WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 190, 16, 115, 21, ContactsDialog, NULL, Instance, NULL);
+ FixedFont(NameTextbox);
+
+ NegatedCheckbox = CreateWindowEx(0, WC_BUTTON, _("|/| Negated"),
+ WS_CHILD | BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 146, 44, 160, 20, ContactsDialog, NULL, Instance, NULL);
+ NiceFont(NegatedCheckbox);
+
+ OkButton = CreateWindowEx(0, WC_BUTTON, _("OK"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_DEFPUSHBUTTON,
+ 321, 10, 70, 23, ContactsDialog, NULL, Instance, NULL);
+ NiceFont(OkButton);
+
+ CancelButton = CreateWindowEx(0, WC_BUTTON, _("Cancel"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 321, 40, 70, 23, ContactsDialog, NULL, Instance, NULL);
+ NiceFont(CancelButton);
+
+ PrevNameProc = SetWindowLongPtr(NameTextbox, GWLP_WNDPROC,
+ (LONG_PTR)MyNameProc);
+}
+
+void ShowContactsDialog(BOOL *negated, char *name)
+{
+ ContactsDialog = CreateWindowClient(0, "LDmicroDialog",
+ _("Contacts"), WS_OVERLAPPED | WS_SYSMENU,
+ 100, 100, 404, 95, NULL, NULL, Instance, NULL);
+
+ MakeControls();
+
+ if(name[0] == 'R') {
+ SendMessage(SourceInternalRelayRadio, BM_SETCHECK, BST_CHECKED, 0);
+ } else if(name[0] == 'Y') {
+ SendMessage(SourceOutputPinRadio, BM_SETCHECK, BST_CHECKED, 0);
+ } else {
+ SendMessage(SourceInputPinRadio, BM_SETCHECK, BST_CHECKED, 0);
+ }
+ if(*negated) {
+ SendMessage(NegatedCheckbox, BM_SETCHECK, BST_CHECKED, 0);
+ }
+ SendMessage(NameTextbox, WM_SETTEXT, 0, (LPARAM)(name + 1));
+
+ EnableWindow(MainWindow, FALSE);
+ ShowWindow(ContactsDialog, TRUE);
+ SetFocus(NameTextbox);
+ SendMessage(NameTextbox, EM_SETSEL, 0, -1);
+
+ MSG msg;
+ DWORD ret;
+ DialogDone = FALSE;
+ DialogCancel = FALSE;
+ while((ret = GetMessage(&msg, NULL, 0, 0)) && !DialogDone) {
+ if(msg.message == WM_KEYDOWN) {
+ if(msg.wParam == VK_RETURN) {
+ DialogDone = TRUE;
+ break;
+ } else if(msg.wParam == VK_ESCAPE) {
+ DialogDone = TRUE;
+ DialogCancel = TRUE;
+ break;
+ }
+ }
+
+ if(IsDialogMessage(ContactsDialog, &msg)) continue;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ if(!DialogCancel) {
+ if(SendMessage(NegatedCheckbox, BM_GETSTATE, 0, 0) & BST_CHECKED) {
+ *negated = TRUE;
+ } else {
+ *negated = FALSE;
+ }
+ if(SendMessage(SourceInternalRelayRadio, BM_GETSTATE, 0, 0)
+ & BST_CHECKED)
+ {
+ name[0] = 'R';
+ } else if(SendMessage(SourceInputPinRadio, BM_GETSTATE, 0, 0)
+ & BST_CHECKED)
+ {
+ name[0] = 'X';
+ } else {
+ name[0] = 'Y';
+ }
+ SendMessage(NameTextbox, WM_GETTEXT, (WPARAM)16, (LPARAM)(name+1));
+ }
+
+ EnableWindow(MainWindow, TRUE);
+ DestroyWindow(ContactsDialog);
+ return;
+}
diff --git a/ldmicro/draw.cpp b/ldmicro/draw.cpp
new file mode 100644
index 0000000..2e34622
--- /dev/null
+++ b/ldmicro/draw.cpp
@@ -0,0 +1,1066 @@
+//-----------------------------------------------------------------------------
+// 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 for drawing the ladder diagram as a schematic on screen. This
+// includes the stuff to figure out where we should draw each leaf (coil,
+// contact, timer, ...) element on screen and how we should connect them up
+// with wires.
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+// Number of drawing columns (leaf element units) available. We want to
+// know this so that we can right-justify the coils.
+int ColsAvailable;
+
+// Set when we draw the selected element in the program. If there is no
+// element selected then we ought to put the cursor at the top left of
+// the screen.
+BOOL SelectionActive;
+
+// Is the element currently being drawn highlighted because it is selected?
+// If so we must not do further syntax highlighting.
+BOOL ThisHighlighted;
+
+#define TOO_LONG _("!!!too long!!!")
+
+#define DM_BOUNDS(gx, gy) { \
+ if((gx) >= DISPLAY_MATRIX_X_SIZE || (gx) < 0) oops(); \
+ if((gy) >= DISPLAY_MATRIX_Y_SIZE || (gy) < 0) oops(); \
+ }
+
+//-----------------------------------------------------------------------------
+// The display code is the only part of the program that knows how wide a
+// rung will be when it's displayed; so this is the only convenient place to
+// warn the user and undo their changes if they created something too wide.
+// This is not very clean.
+//-----------------------------------------------------------------------------
+static BOOL CheckBoundsUndoIfFails(int gx, int gy)
+{
+ if(gx >= DISPLAY_MATRIX_X_SIZE || gx < 0 ||
+ gy >= DISPLAY_MATRIX_Y_SIZE || gy < 0)
+ {
+ if(CanUndo()) {
+ UndoUndo();
+ Error(_("Too many elements in subcircuit!"));
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Determine the width, in leaf element units, of a particular subcircuit.
+// The width of a leaf is 1, the width of a series circuit is the sum of
+// of the widths of its members, and the width of a parallel circuit is
+// the maximum of the widths of its members.
+//-----------------------------------------------------------------------------
+static int CountWidthOfElement(int which, void *elem, int soFar)
+{
+ switch(which) {
+ case ELEM_PLACEHOLDER:
+ case ELEM_OPEN:
+ case ELEM_SHORT:
+ case ELEM_CONTACTS:
+ case ELEM_TON:
+ case ELEM_TOF:
+ case ELEM_RTO:
+ case ELEM_CTU:
+ case ELEM_CTD:
+ 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:
+ case ELEM_UART_RECV:
+ case ELEM_UART_SEND:
+ return 1;
+
+ case ELEM_FORMATTED_STRING:
+ return 2;
+
+ case ELEM_COMMENT: {
+ if(soFar != 0) oops();
+
+ ElemLeaf *l = (ElemLeaf *)elem;
+ char tbuf[MAX_COMMENT_LEN];
+
+ strcpy(tbuf, l->d.comment.str);
+ char *b = strchr(tbuf, '\n');
+
+ int len;
+ if(b) {
+ *b = '\0';
+ len = max(strlen(tbuf)-1, strlen(b+1));
+ } else {
+ len = strlen(tbuf);
+ }
+ // round up, and allow space for lead-in
+ len = (len + 7 + (POS_WIDTH-1)) / POS_WIDTH;
+ return max(ColsAvailable, len);
+ }
+ case ELEM_CTC:
+ case ELEM_RES:
+ case ELEM_COIL:
+ case ELEM_MOVE:
+ case ELEM_SHIFT_REGISTER:
+ case ELEM_LOOK_UP_TABLE:
+ case ELEM_PIECEWISE_LINEAR:
+ case ELEM_MASTER_RELAY:
+ case ELEM_READ_ADC:
+ case ELEM_SET_PWM:
+ case ELEM_PERSIST:
+ if(ColsAvailable - soFar > 1) {
+ return ColsAvailable - soFar;
+ } else {
+ return 1;
+ }
+
+ case ELEM_ADD:
+ case ELEM_SUB:
+ case ELEM_MUL:
+ case ELEM_DIV:
+ if(ColsAvailable - soFar > 2) {
+ return ColsAvailable - soFar;
+ } else {
+ return 2;
+ }
+
+ case ELEM_SERIES_SUBCKT: {
+ // total of the width of the members
+ int total = 0;
+ int i;
+ ElemSubcktSeries *s = (ElemSubcktSeries *)elem;
+ for(i = 0; i < s->count; i++) {
+ total += CountWidthOfElement(s->contents[i].which,
+ s->contents[i].d.any, total+soFar);
+ }
+ return total;
+ }
+
+ case ELEM_PARALLEL_SUBCKT: {
+ // greatest of the width of the members
+ int max = 0;
+ int i;
+ ElemSubcktParallel *p = (ElemSubcktParallel *)elem;
+ for(i = 0; i < p->count; i++) {
+ int w = CountWidthOfElement(p->contents[i].which,
+ p->contents[i].d.any, soFar);
+ if(w > max) {
+ max = w;
+ }
+ }
+ return max;
+ }
+
+ default:
+ oops();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Determine the height, in leaf element units, of a particular subcircuit.
+// The height of a leaf is 1, the height of a parallel circuit is the sum of
+// of the heights of its members, and the height of a series circuit is the
+// maximum of the heights of its members. (This is the dual of the width
+// case.)
+//-----------------------------------------------------------------------------
+int CountHeightOfElement(int which, void *elem)
+{
+ switch(which) {
+ CASE_LEAF
+ return 1;
+
+ case ELEM_PARALLEL_SUBCKT: {
+ // total of the height of the members
+ int total = 0;
+ int i;
+ ElemSubcktParallel *s = (ElemSubcktParallel *)elem;
+ for(i = 0; i < s->count; i++) {
+ total += CountHeightOfElement(s->contents[i].which,
+ s->contents[i].d.any);
+ }
+ return total;
+ }
+
+ case ELEM_SERIES_SUBCKT: {
+ // greatest of the height of the members
+ int max = 0;
+ int i;
+ ElemSubcktSeries *s = (ElemSubcktSeries *)elem;
+ for(i = 0; i < s->count; i++) {
+ int w = CountHeightOfElement(s->contents[i].which,
+ s->contents[i].d.any);
+ if(w > max) {
+ max = w;
+ }
+ }
+ return max;
+ }
+
+ default:
+ oops();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Determine the width, in leaf element units, of the widest row of the PLC
+// program (i.e. loop over all the rungs and find the widest).
+//-----------------------------------------------------------------------------
+int ProgCountWidestRow(void)
+{
+ int i;
+ int max = 0;
+ int colsTemp = ColsAvailable;
+ ColsAvailable = 0;
+ for(i = 0; i < Prog.numRungs; i++) {
+ int w = CountWidthOfElement(ELEM_SERIES_SUBCKT, Prog.rungs[i], 0);
+ if(w > max) {
+ max = w;
+ }
+ }
+ ColsAvailable = colsTemp;
+ return max;
+}
+
+//-----------------------------------------------------------------------------
+// Draw a vertical wire one leaf element unit high up from (cx, cy), where cx
+// and cy are in charcter units.
+//-----------------------------------------------------------------------------
+static void VerticalWire(int cx, int cy)
+{
+ int j;
+ for(j = 1; j < POS_HEIGHT; j++) {
+ DrawChars(cx, cy + (POS_HEIGHT/2 - j), "|");
+ }
+ DrawChars(cx, cy + (POS_HEIGHT/2), "+");
+ DrawChars(cx, cy + (POS_HEIGHT/2 - POS_HEIGHT), "+");
+}
+
+//-----------------------------------------------------------------------------
+// Convenience functions for making the text colors pretty, for DrawElement.
+//-----------------------------------------------------------------------------
+static void NormText(void)
+{
+ SetTextColor(Hdc, InSimulationMode ? HighlightColours.simOff :
+ HighlightColours.def);
+ SelectObject(Hdc, FixedWidthFont);
+}
+static void EmphText(void)
+{
+ SetTextColor(Hdc, InSimulationMode ? HighlightColours.simOn :
+ HighlightColours.selected);
+ SelectObject(Hdc, FixedWidthFontBold);
+}
+static void NameText(void)
+{
+ if(!InSimulationMode && !ThisHighlighted) {
+ SetTextColor(Hdc, HighlightColours.name);
+ }
+}
+static void BodyText(void)
+{
+ if(!InSimulationMode && !ThisHighlighted) {
+ SetTextColor(Hdc, HighlightColours.def);
+ }
+}
+static void PoweredText(BOOL powered)
+{
+ if(InSimulationMode) {
+ if(powered)
+ EmphText();
+ else
+ NormText();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Count the length of a string, in characters. Nonstandard because the
+// string may contain special characters to indicate formatting (syntax
+// highlighting).
+//-----------------------------------------------------------------------------
+static int FormattedStrlen(char *str)
+{
+ int l = 0;
+ while(*str) {
+ if(*str > 10) {
+ l++;
+ }
+ str++;
+ }
+ return l;
+}
+
+//-----------------------------------------------------------------------------
+// Draw a string, centred in the space of a single position, with spaces on
+// the left and right. Draws on the upper line of the position.
+//-----------------------------------------------------------------------------
+static void CenterWithSpaces(int cx, int cy, char *str, BOOL powered,
+ BOOL isName)
+{
+ int extra = POS_WIDTH - FormattedStrlen(str);
+ PoweredText(powered);
+ if(isName) NameText();
+ DrawChars(cx + (extra/2), cy + (POS_HEIGHT/2) - 1, str);
+ if(isName) BodyText();
+}
+
+//-----------------------------------------------------------------------------
+// Like CenterWithWires, but for an arbitrary width position (e.g. for ADD
+// and SUB, which are double-width).
+//-----------------------------------------------------------------------------
+static void CenterWithWiresWidth(int cx, int cy, char *str, BOOL before,
+ BOOL after, int totalWidth)
+{
+ int extra = totalWidth - FormattedStrlen(str);
+
+ PoweredText(after);
+ DrawChars(cx + (extra/2), cy + (POS_HEIGHT/2), str);
+
+ PoweredText(before);
+ int i;
+ for(i = 0; i < (extra/2); i++) {
+ DrawChars(cx + i, cy + (POS_HEIGHT/2), "-");
+ }
+ PoweredText(after);
+ for(i = FormattedStrlen(str)+(extra/2); i < totalWidth; i++) {
+ DrawChars(cx + i, cy + (POS_HEIGHT/2), "-");
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Draw a string, centred in the space of a single position, with en dashes on
+// the left and right coloured according to the powered state. Draws on the
+// middle line.
+//-----------------------------------------------------------------------------
+static void CenterWithWires(int cx, int cy, char *str, BOOL before, BOOL after)
+{
+ CenterWithWiresWidth(cx, cy, str, before, after, POS_WIDTH);
+}
+
+//-----------------------------------------------------------------------------
+// Draw an end of line element (coil, RES, MOV, etc.). Special things about
+// an end of line element: we must right-justify it.
+//-----------------------------------------------------------------------------
+static BOOL DrawEndOfLine(int which, ElemLeaf *leaf, int *cx, int *cy,
+ BOOL poweredBefore)
+{
+ int cx0 = *cx, cy0 = *cy;
+
+ BOOL poweredAfter = leaf->poweredAfter;
+
+ int thisWidth;
+ switch(which) {
+ case ELEM_ADD:
+ case ELEM_SUB:
+ case ELEM_MUL:
+ case ELEM_DIV:
+ thisWidth = 2;
+ break;
+
+ default:
+ thisWidth = 1;
+ break;
+ }
+
+ NormText();
+ PoweredText(poweredBefore);
+ while(*cx < (ColsAvailable-thisWidth)*POS_WIDTH) {
+ int gx = *cx/POS_WIDTH;
+ int gy = *cy/POS_HEIGHT;
+
+ if(CheckBoundsUndoIfFails(gx, gy)) return FALSE;
+
+ if(gx >= DISPLAY_MATRIX_X_SIZE) oops();
+ DM_BOUNDS(gx, gy);
+ DisplayMatrix[gx][gy] = PADDING_IN_DISPLAY_MATRIX;
+ DisplayMatrixWhich[gx][gy] = ELEM_PADDING;
+
+ int i;
+ for(i = 0; i < POS_WIDTH; i++) {
+ DrawChars(*cx + i, *cy + (POS_HEIGHT/2), "-");
+ }
+ *cx += POS_WIDTH;
+ cx0 += POS_WIDTH;
+ }
+
+ if(leaf == Selected && !InSimulationMode) {
+ EmphText();
+ ThisHighlighted = TRUE;
+ } else {
+ ThisHighlighted = FALSE;
+ }
+
+ switch(which) {
+ case ELEM_CTC: {
+ char buf[256];
+ ElemCounter *c = &leaf->d.counter;
+ sprintf(buf, "{\x01""CTC\x02 0:%d}", c->max);
+
+ CenterWithSpaces(*cx, *cy, c->name, poweredAfter, TRUE);
+ CenterWithWires(*cx, *cy, buf, poweredBefore, poweredAfter);
+ break;
+ }
+ case ELEM_RES: {
+ ElemReset *r = &leaf->d.reset;
+ CenterWithSpaces(*cx, *cy, r->name, poweredAfter, TRUE);
+ CenterWithWires(*cx, *cy, "{RES}", poweredBefore, poweredAfter);
+ break;
+ }
+ case ELEM_READ_ADC: {
+ ElemReadAdc *r = &leaf->d.readAdc;
+ CenterWithSpaces(*cx, *cy, r->name, poweredAfter, TRUE);
+ CenterWithWires(*cx, *cy, "{READ ADC}", poweredBefore,
+ poweredAfter);
+ break;
+ }
+ case ELEM_SET_PWM: {
+ ElemSetPwm *s = &leaf->d.setPwm;
+ CenterWithSpaces(*cx, *cy, s->name, poweredAfter, TRUE);
+ char l[50];
+ if(s->targetFreq >= 100000) {
+ sprintf(l, "{PWM %d kHz}", (s->targetFreq+500)/1000);
+ } else if(s->targetFreq >= 10000) {
+ sprintf(l, "{PWM %.1f kHz}", s->targetFreq/1000.0);
+ } else if(s->targetFreq >= 1000) {
+ sprintf(l, "{PWM %.2f kHz}", s->targetFreq/1000.0);
+ } else {
+ sprintf(l, "{PWM %d Hz}", s->targetFreq);
+ }
+ CenterWithWires(*cx, *cy, l, poweredBefore,
+ poweredAfter);
+ break;
+ }
+ case ELEM_PERSIST:
+ CenterWithSpaces(*cx, *cy, leaf->d.persist.var, poweredAfter, TRUE);
+ CenterWithWires(*cx, *cy, "{PERSIST}", poweredBefore, poweredAfter);
+ break;
+
+ case ELEM_MOVE: {
+ char top[256];
+ char bot[256];
+ ElemMove *m = &leaf->d.move;
+
+ if((strlen(m->dest) > (POS_WIDTH - 9)) ||
+ (strlen(m->src) > (POS_WIDTH - 9)))
+ {
+ CenterWithWires(*cx, *cy, TOO_LONG, poweredBefore,
+ poweredAfter);
+ break;
+ }
+
+ strcpy(top, "{ }");
+ memcpy(top+1, m->dest, strlen(m->dest));
+ top[strlen(m->dest) + 3] = ':';
+ top[strlen(m->dest) + 4] = '=';
+
+ strcpy(bot, "{ \x01MOV\x02}");
+ memcpy(bot+2, m->src, strlen(m->src));
+
+ CenterWithSpaces(*cx, *cy, top, poweredAfter, FALSE);
+ CenterWithWires(*cx, *cy, bot, poweredBefore, poweredAfter);
+ break;
+ }
+ case ELEM_MASTER_RELAY:
+ CenterWithWires(*cx, *cy, "{MASTER RLY}", poweredBefore,
+ poweredAfter);
+ break;
+
+ case ELEM_SHIFT_REGISTER: {
+ char bot[MAX_NAME_LEN+20];
+ memset(bot, ' ', sizeof(bot));
+ bot[0] = '{';
+ sprintf(bot+2, "%s0..%d", leaf->d.shiftRegister.name,
+ leaf->d.shiftRegister.stages-1);
+ bot[strlen(bot)] = ' ';
+ bot[13] = '}';
+ bot[14] = '\0';
+ CenterWithSpaces(*cx, *cy, "{\x01SHIFT REG\x02 }",
+ poweredAfter, FALSE);
+ CenterWithWires(*cx, *cy, bot, poweredBefore, poweredAfter);
+ break;
+ }
+ case ELEM_PIECEWISE_LINEAR:
+ case ELEM_LOOK_UP_TABLE: {
+ char top[MAX_NAME_LEN+20], bot[MAX_NAME_LEN+20];
+ char *dest, *index, *str;
+ if(which == ELEM_PIECEWISE_LINEAR) {
+ dest = leaf->d.piecewiseLinear.dest;
+ index = leaf->d.piecewiseLinear.index;
+ str = "PWL";
+ } else {
+ dest = leaf->d.lookUpTable.dest;
+ index = leaf->d.lookUpTable.index;
+ str = "LUT";
+ }
+ memset(top, ' ', sizeof(top));
+ top[0] = '{';
+ sprintf(top+2, "%s :=", dest);
+ top[strlen(top)] = ' ';
+ top[13] = '}';
+ top[14] = '\0';
+ CenterWithSpaces(*cx, *cy, top, poweredAfter, FALSE);
+ memset(bot, ' ', sizeof(bot));
+ bot[0] = '{';
+ sprintf(bot+2, " %s[%s]", str, index);
+ bot[strlen(bot)] = ' ';
+ bot[13] = '}';
+ bot[14] = '\0';
+ CenterWithWires(*cx, *cy, bot, poweredBefore, poweredAfter);
+ break;
+ }
+ case ELEM_COIL: {
+ char buf[4];
+ ElemCoil *c = &leaf->d.coil;
+
+ buf[0] = '(';
+ if(c->negated) {
+ buf[1] = '/';
+ } else if(c->setOnly) {
+ buf[1] = 'S';
+ } else if(c->resetOnly) {
+ buf[1] = 'R';
+ } else {
+ buf[1] = ' ';
+ }
+ buf[2] = ')';
+ buf[3] = '\0';
+
+ CenterWithSpaces(*cx, *cy, c->name, poweredAfter, TRUE);
+ CenterWithWires(*cx, *cy, buf, poweredBefore, poweredAfter);
+ break;
+ }
+ case ELEM_DIV:
+ case ELEM_MUL:
+ case ELEM_SUB:
+ case ELEM_ADD: {
+ char top[POS_WIDTH*2-3+2];
+ char bot[POS_WIDTH*2-3];
+
+ memset(top, ' ', sizeof(top)-1);
+ top[0] = '{';
+
+ memset(bot, ' ', sizeof(bot)-1);
+ bot[0] = '{';
+
+ int lt = 1;
+ if(which == ELEM_ADD) {
+ memcpy(top+lt, "\x01""ADD\x02", 5);
+ } else if(which == ELEM_SUB) {
+ memcpy(top+lt, "\x01SUB\x02", 5);
+ } else if(which == ELEM_MUL) {
+ memcpy(top+lt, "\x01MUL\x02", 5);
+ } else if(which == ELEM_DIV) {
+ memcpy(top+lt, "\x01""DIV\x02", 5);
+ } else oops();
+
+ lt += 7;
+ memcpy(top+lt, leaf->d.math.dest, strlen(leaf->d.math.dest));
+ lt += strlen(leaf->d.math.dest) + 2;
+ top[lt++] = ':';
+ top[lt++] = '=';
+
+ int lb = 2;
+ memcpy(bot+lb, leaf->d.math.op1, strlen(leaf->d.math.op1));
+ lb += strlen(leaf->d.math.op1) + 1;
+ if(which == ELEM_ADD) {
+ bot[lb++] = '+';
+ } else if(which == ELEM_SUB) {
+ bot[lb++] = '-';
+ } else if(which == ELEM_MUL) {
+ bot[lb++] = '*';
+ } else if(which == ELEM_DIV) {
+ bot[lb++] = '/';
+ } else oops();
+ lb++;
+ memcpy(bot+lb, leaf->d.math.op2, strlen(leaf->d.math.op2));
+ lb += strlen(leaf->d.math.op2);
+
+ int l = max(lb, lt - 2);
+ top[l+2] = '}'; top[l+3] = '\0';
+ bot[l] = '}'; bot[l+1] = '\0';
+
+ int extra = 2*POS_WIDTH - FormattedStrlen(top);
+ PoweredText(poweredAfter);
+ DrawChars(*cx + (extra/2), *cy + (POS_HEIGHT/2) - 1, top);
+ CenterWithWiresWidth(*cx, *cy, bot, poweredBefore, poweredAfter,
+ 2*POS_WIDTH);
+
+ *cx += POS_WIDTH;
+
+ break;
+ }
+ default:
+ oops();
+ break;
+ }
+
+ *cx += POS_WIDTH;
+
+ return poweredAfter;
+}
+
+//-----------------------------------------------------------------------------
+// Draw a leaf element. Special things about a leaf: no need to recurse
+// further, and we must put it into the display matrix.
+//-----------------------------------------------------------------------------
+static BOOL DrawLeaf(int which, ElemLeaf *leaf, int *cx, int *cy,
+ BOOL poweredBefore)
+{
+ int cx0 = *cx, cy0 = *cy;
+ BOOL poweredAfter = leaf->poweredAfter;
+
+ switch(which) {
+ case ELEM_COMMENT: {
+ char tbuf[MAX_COMMENT_LEN];
+ char tlbuf[MAX_COMMENT_LEN+8];
+
+ strcpy(tbuf, leaf->d.comment.str);
+ char *b = strchr(tbuf, '\n');
+
+ if(b) {
+ if(b[-1] == '\r') b[-1] = '\0';
+ *b = '\0';
+ sprintf(tlbuf, "\x03 ; %s\x02", tbuf);
+ DrawChars(*cx, *cy + (POS_HEIGHT/2) - 1, tlbuf);
+ sprintf(tlbuf, "\x03 ; %s\x02", b+1);
+ DrawChars(*cx, *cy + (POS_HEIGHT/2), tlbuf);
+ } else {
+ sprintf(tlbuf, "\x03 ; %s\x02", tbuf);
+ DrawChars(*cx, *cy + (POS_HEIGHT/2) - 1, tlbuf);
+ }
+
+ *cx += ColsAvailable*POS_WIDTH;
+ break;
+ }
+ case ELEM_PLACEHOLDER: {
+ NormText();
+ CenterWithWiresWidth(*cx, *cy, "--", FALSE, FALSE, 2);
+ *cx += POS_WIDTH;
+ break;
+ }
+ case ELEM_CONTACTS: {
+ char buf[4];
+ ElemContacts *c = &leaf->d.contacts;
+
+ buf[0] = ']';
+ buf[1] = c->negated ? '/' : ' ';
+ buf[2] = '[';
+ buf[3] = '\0';
+
+ CenterWithSpaces(*cx, *cy, c->name, poweredAfter, TRUE);
+ CenterWithWires(*cx, *cy, buf, poweredBefore, poweredAfter);
+
+ *cx += POS_WIDTH;
+ break;
+ }
+ {
+ char *s;
+ case ELEM_EQU:
+ s = "=="; goto cmp;
+ case ELEM_NEQ:
+ s = "/="; goto cmp;
+ case ELEM_GRT:
+ s = ">"; goto cmp;
+ case ELEM_GEQ:
+ s = ">="; goto cmp;
+ case ELEM_LES:
+ s = "<"; goto cmp;
+ case ELEM_LEQ:
+ s = "<="; goto cmp;
+cmp:
+ char s1[POS_WIDTH+10], s2[POS_WIDTH+10];
+ int l1, l2, lmax;
+
+ l1 = 2 + 1 + strlen(s) + strlen(leaf->d.cmp.op1);
+ l2 = 2 + 1 + strlen(leaf->d.cmp.op2);
+ lmax = max(l1, l2);
+
+ if(lmax < POS_WIDTH) {
+ memset(s1, ' ', sizeof(s1));
+ s1[0] = '[';
+ s1[lmax-1] = ']';
+ s1[lmax] = '\0';
+ strcpy(s2, s1);
+ memcpy(s1+1, leaf->d.cmp.op1, strlen(leaf->d.cmp.op1));
+ memcpy(s1+strlen(leaf->d.cmp.op1)+2, s, strlen(s));
+ memcpy(s2+2, leaf->d.cmp.op2, strlen(leaf->d.cmp.op2));
+ } else {
+ strcpy(s1, "");
+ strcpy(s2, TOO_LONG);
+ }
+
+ CenterWithSpaces(*cx, *cy, s1, poweredAfter, FALSE);
+ CenterWithWires(*cx, *cy, s2, poweredBefore, poweredAfter);
+
+ *cx += POS_WIDTH;
+ break;
+ }
+ case ELEM_OPEN:
+ CenterWithWires(*cx, *cy, "+ +", poweredBefore, poweredAfter);
+ *cx += POS_WIDTH;
+ break;
+
+ case ELEM_SHORT:
+ CenterWithWires(*cx, *cy, "+------+", poweredBefore, poweredAfter);
+ *cx += POS_WIDTH;
+ break;
+
+ case ELEM_ONE_SHOT_RISING:
+ case ELEM_ONE_SHOT_FALLING: {
+ char *s1, *s2;
+ if(which == ELEM_ONE_SHOT_RISING) {
+ s1 = " _ ";
+ s2 = "[\x01OSR\x02_/ ]";
+ } else if(which == ELEM_ONE_SHOT_FALLING) {
+ s1 = " _ ";
+ s2 = "[\x01OSF\x02 \\_]";
+ } else oops();
+
+ CenterWithSpaces(*cx, *cy, s1, poweredAfter, FALSE);
+ CenterWithWires(*cx, *cy, s2, poweredBefore, poweredAfter);
+
+ *cx += POS_WIDTH;
+ break;
+ }
+ case ELEM_CTU:
+ case ELEM_CTD: {
+ char *s;
+ if(which == ELEM_CTU)
+ s = "\x01""CTU\x02";
+ else if(which == ELEM_CTD)
+ s = "\x01""CTD\x02";
+ else oops();
+
+ char buf[256];
+ ElemCounter *c = &leaf->d.counter;
+ sprintf(buf, "[%s >=%d]", s, c->max);
+
+ CenterWithSpaces(*cx, *cy, c->name, poweredAfter, TRUE);
+ CenterWithWires(*cx, *cy, buf, poweredBefore, poweredAfter);
+
+ *cx += POS_WIDTH;
+ break;
+ }
+ case ELEM_RTO:
+ case ELEM_TON:
+ case ELEM_TOF: {
+ char *s;
+ if(which == ELEM_TON)
+ s = "\x01TON\x02";
+ else if(which == ELEM_TOF)
+ s = "\x01TOF\x02";
+ else if(which == ELEM_RTO)
+ s = "\x01RTO\x02";
+ else oops();
+
+ char buf[256];
+ ElemTimer *t = &leaf->d.timer;
+ if(t->delay >= 1000*1000) {
+ sprintf(buf, "[%s %.3f s]", s, t->delay/1000000.0);
+ } else if(t->delay >= 100*1000) {
+ sprintf(buf, "[%s %.1f ms]", s, t->delay/1000.0);
+ } else {
+ sprintf(buf, "[%s %.2f ms]", s, t->delay/1000.0);
+ }
+
+ CenterWithSpaces(*cx, *cy, t->name, poweredAfter, TRUE);
+ CenterWithWires(*cx, *cy, buf, poweredBefore, poweredAfter);
+
+ *cx += POS_WIDTH;
+ break;
+ }
+ case ELEM_FORMATTED_STRING: {
+ // Careful, string could be longer than fits in our space.
+ char str[POS_WIDTH*2];
+ memset(str, 0, sizeof(str));
+ char *srcStr = leaf->d.fmtdStr.string;
+ memcpy(str, srcStr, min(strlen(srcStr), POS_WIDTH*2 - 7));
+
+ char bot[100];
+ sprintf(bot, "{\"%s\"}", str);
+
+ int extra = 2*POS_WIDTH - strlen(leaf->d.fmtdStr.var);
+ PoweredText(poweredAfter);
+ NameText();
+ DrawChars(*cx + (extra/2), *cy + (POS_HEIGHT/2) - 1,
+ leaf->d.fmtdStr.var);
+ BodyText();
+
+ CenterWithWiresWidth(*cx, *cy, bot, poweredBefore, poweredAfter,
+ 2*POS_WIDTH);
+ *cx += 2*POS_WIDTH;
+ break;
+ }
+ case ELEM_UART_RECV:
+ case ELEM_UART_SEND:
+ CenterWithWires(*cx, *cy,
+ (which == ELEM_UART_RECV) ? "{UART RECV}" : "{UART SEND}",
+ poweredBefore, poweredAfter);
+ CenterWithSpaces(*cx, *cy, leaf->d.uart.name, poweredAfter, TRUE);
+ *cx += POS_WIDTH;
+ break;
+
+ default:
+ poweredAfter = DrawEndOfLine(which, leaf, cx, cy, poweredBefore);
+ break;
+ }
+
+ // And now we can enter the element into the display matrix so that the
+ // UI routines know what element is at position (gx, gy) when the user
+ // clicks there, and so that we know where to put the cursor if this
+ // element is selected.
+
+ // Don't use original cx0, as an end of line element might be further
+ // along than that.
+ cx0 = *cx - POS_WIDTH;
+
+ int gx = cx0/POS_WIDTH;
+ int gy = cy0/POS_HEIGHT;
+ if(CheckBoundsUndoIfFails(gx, gy)) return FALSE;
+ DM_BOUNDS(gx, gy);
+
+ DisplayMatrix[gx][gy] = leaf;
+ DisplayMatrixWhich[gx][gy] = which;
+
+ int xadj = 0;
+ switch(which) {
+ case ELEM_ADD:
+ case ELEM_SUB:
+ case ELEM_MUL:
+ case ELEM_DIV:
+ case ELEM_FORMATTED_STRING:
+ DM_BOUNDS(gx-1, gy);
+ DisplayMatrix[gx-1][gy] = leaf;
+ DisplayMatrixWhich[gx-1][gy] = which;
+ xadj = POS_WIDTH*FONT_WIDTH;
+ break;
+ }
+
+ if(which == ELEM_COMMENT) {
+ int i;
+ for(i = 0; i < ColsAvailable; i++) {
+ DisplayMatrix[i][gy] = leaf;
+ DisplayMatrixWhich[i][gy] = ELEM_COMMENT;
+ }
+ xadj = (ColsAvailable-1)*POS_WIDTH*FONT_WIDTH;
+ }
+
+ int x0 = X_PADDING + cx0*FONT_WIDTH;
+ int y0 = Y_PADDING + cy0*FONT_HEIGHT;
+
+ if(leaf->selectedState != SELECTED_NONE && leaf == Selected) {
+ SelectionActive = TRUE;
+ }
+ switch(leaf->selectedState) {
+ case SELECTED_LEFT:
+ Cursor.left = x0 + FONT_WIDTH - 4 - xadj;
+ Cursor.top = y0 - FONT_HEIGHT/2;
+ Cursor.width = 2;
+ Cursor.height = POS_HEIGHT*FONT_HEIGHT;
+ break;
+
+ case SELECTED_RIGHT:
+ Cursor.left = x0 + (POS_WIDTH-1)*FONT_WIDTH - 5;
+ Cursor.top = y0 - FONT_HEIGHT/2;
+ Cursor.width = 2;
+ Cursor.height = POS_HEIGHT*FONT_HEIGHT;
+ break;
+
+ case SELECTED_ABOVE:
+ Cursor.left = x0 + FONT_WIDTH/2 - xadj;
+ Cursor.top = y0 - 2;
+ Cursor.width = (POS_WIDTH-2)*FONT_WIDTH + xadj;
+ Cursor.height = 2;
+ break;
+
+ case SELECTED_BELOW:
+ Cursor.left = x0 + FONT_WIDTH/2 - xadj;
+ Cursor.top = y0 + (POS_HEIGHT-1)*FONT_HEIGHT +
+ FONT_HEIGHT/2 - 2;
+ Cursor.width = (POS_WIDTH-2)*(FONT_WIDTH) + xadj;
+ Cursor.height = 2;
+ break;
+
+ default:
+ break;
+ }
+
+ return poweredAfter;
+}
+
+//-----------------------------------------------------------------------------
+// Draw a particular subcircuit with its top left corner at *cx and *cy (in
+// characters). If it is a leaf element then just print it and return; else
+// loop over the elements of the subcircuit and call ourselves recursively.
+// At the end updates *cx and *cy.
+//
+// In simulation mode, returns TRUE the circuit is energized after the given
+// element, else FALSE. This is needed to colour all the wires correctly,
+// since the colouring indicates whether a wire is energized.
+//-----------------------------------------------------------------------------
+BOOL DrawElement(int which, void *elem, int *cx, int *cy, BOOL poweredBefore)
+{
+ BOOL poweredAfter;
+
+ int cx0 = *cx, cy0 = *cy;
+ ElemLeaf *leaf = (ElemLeaf *)elem;
+
+ SetBkColor(Hdc, InSimulationMode ? HighlightColours.simBg :
+ HighlightColours.bg);
+ NormText();
+
+ if(elem == Selected && !InSimulationMode) {
+ EmphText();
+ ThisHighlighted = TRUE;
+ } else {
+ ThisHighlighted = FALSE;
+ }
+
+ switch(which) {
+ case ELEM_SERIES_SUBCKT: {
+ int i;
+ ElemSubcktSeries *s = (ElemSubcktSeries *)elem;
+ poweredAfter = poweredBefore;
+ for(i = 0; i < s->count; i++) {
+ poweredAfter = DrawElement(s->contents[i].which,
+ s->contents[i].d.any, cx, cy, poweredAfter);
+ }
+ break;
+ }
+ case ELEM_PARALLEL_SUBCKT: {
+ int i;
+ ElemSubcktParallel *p = (ElemSubcktParallel *)elem;
+ int widthMax = CountWidthOfElement(which, elem, (*cx)/POS_WIDTH);
+ int heightMax = CountHeightOfElement(which, elem);
+
+ poweredAfter = FALSE;
+
+ int lowestPowered = -1;
+ int downBy = 0;
+ for(i = 0; i < p->count; i++) {
+ BOOL poweredThis;
+
+ poweredThis = DrawElement(p->contents[i].which,
+ p->contents[i].d.any, cx, cy, poweredBefore);
+
+ if(InSimulationMode) {
+ if(poweredThis) poweredAfter = TRUE;
+ PoweredText(poweredThis);
+ }
+
+ while((*cx - cx0) < widthMax*POS_WIDTH) {
+ int gx = *cx/POS_WIDTH;
+ int gy = *cy/POS_HEIGHT;
+
+ if(CheckBoundsUndoIfFails(gx, gy)) return FALSE;
+
+ DM_BOUNDS(gx, gy);
+ DisplayMatrix[gx][gy] = PADDING_IN_DISPLAY_MATRIX;
+ DisplayMatrixWhich[gx][gy] = ELEM_PADDING;
+
+ char buf[256];
+ int j;
+ for(j = 0; j < POS_WIDTH; j++) {
+ buf[j] = '-';
+ }
+ buf[j] = '\0';
+ DrawChars(*cx, *cy + (POS_HEIGHT/2), buf);
+ *cx += POS_WIDTH;
+ }
+
+ *cx = cx0;
+ int justDrewHeight = CountHeightOfElement(p->contents[i].which,
+ p->contents[i].d.any);
+ *cy += POS_HEIGHT*justDrewHeight;
+
+ downBy += justDrewHeight;
+ if(poweredThis) {
+ lowestPowered = downBy - 1;
+ }
+ }
+ *cx = cx0 + POS_WIDTH*widthMax;
+ *cy = cy0;
+
+ int j;
+ BOOL needWire;
+
+ if(*cx/POS_WIDTH != ColsAvailable) {
+ needWire = FALSE;
+ for(j = heightMax - 1; j >= 1; j--) {
+ if(j <= lowestPowered) PoweredText(poweredAfter);
+ if(DisplayMatrix[*cx/POS_WIDTH - 1][*cy/POS_HEIGHT + j]) {
+ needWire = TRUE;
+ }
+ if(needWire) VerticalWire(*cx - 1, *cy + j*POS_HEIGHT);
+ }
+ // stupid special case
+ if(lowestPowered == 0 && InSimulationMode) {
+ EmphText();
+ DrawChars(*cx - 1, *cy + (POS_HEIGHT/2), "+");
+ }
+ }
+
+ PoweredText(poweredBefore);
+ needWire = FALSE;
+ for(j = heightMax - 1; j >= 1; j--) {
+ if(DisplayMatrix[cx0/POS_WIDTH][*cy/POS_HEIGHT + j]) {
+ needWire = TRUE;
+ }
+ if(needWire) VerticalWire(cx0 - 1, *cy + j*POS_HEIGHT);
+ }
+
+ break;
+ }
+ default:
+ poweredAfter = DrawLeaf(which, leaf, cx, cy, poweredBefore);
+ break;
+ }
+
+
+ NormText();
+ return poweredAfter;
+}
+
+//-----------------------------------------------------------------------------
+// Draw the rung that signals the end of the program. Kind of useless but
+// do it anyways, for convention.
+//-----------------------------------------------------------------------------
+void DrawEndRung(int cx, int cy)
+{
+ int i;
+ char *str = "[END]";
+ int lead = (POS_WIDTH - strlen(str))/2;
+ ThisHighlighted = TRUE;
+ for(i = 0; i < lead; i++) {
+ DrawChars(cx + i, cy + (POS_HEIGHT/2), "-");
+ }
+ DrawChars(cx + i, cy + (POS_HEIGHT/2), str);
+ i += strlen(str);
+ for(; i < ColsAvailable*POS_WIDTH; i++) {
+ DrawChars(cx + i, cy + (POS_HEIGHT/2), "-");
+ }
+}
diff --git a/ldmicro/draw_outputdev.cpp b/ldmicro/draw_outputdev.cpp
new file mode 100644
index 0000000..a688550
--- /dev/null
+++ b/ldmicro/draw_outputdev.cpp
@@ -0,0 +1,607 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// The two `output devices' for the drawing code: either the export as text
+// stuff to write to a file, or all the routines concerned with drawing to
+// the screen.
+// Jonathan Westhues, Dec 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+void (*DrawChars)(int, int, char *);
+
+// After an undo all the memory addresses change but make an effort to put
+// the cursor roughly where it should be.
+int SelectedGxAfterNextPaint = -1;
+int SelectedGyAfterNextPaint = -1;
+
+// After pushing a rung up or down the position of that rung in the table
+// changes, but the cursor should stay where it was.
+BOOL ScrollSelectedIntoViewAfterNextPaint;
+
+// Buffer that we write to when exporting (drawing) diagram to a text file.
+// Dynamically allocated so that we're at least slightly efficient.
+static char **ExportBuffer;
+
+// The fonts that we will use to draw the ladder diagram: fixed width, one
+// normal-weight, one bold.
+HFONT FixedWidthFont;
+HFONT FixedWidthFontBold;
+
+// Different colour brushes for right and left buses in simulation, but same
+// colour for both in edit mode; also for the backgrounds in simulation and
+// edit modes.
+static HBRUSH BusRightBus;
+static HBRUSH BusLeftBrush;
+static HBRUSH BusBrush;
+static HBRUSH BgBrush;
+static HBRUSH SimBgBrush;
+
+// Parameters that determine our offset if we are scrolled
+int ScrollXOffset;
+int ScrollYOffset;
+int ScrollXOffsetMax;
+int ScrollYOffsetMax;
+
+// Is the cursor currently drawn? We XOR it so this gets toggled.
+static BOOL CursorDrawn;
+
+// Colours with which to do syntax highlighting, configurable
+SyntaxHighlightingColours HighlightColours;
+
+#define X_RIGHT_PADDING 30
+
+//-----------------------------------------------------------------------------
+// Blink the cursor on the schematic; called by a Windows timer. We XOR
+// draw it so just draw the same rectangle every time to show/erase the
+// cursor. Cursor may be in one of four places in the selected leaf (top,
+// bottom, left, right) but we don't care; just go from the coordinates
+// computed when we drew the schematic in the paint procedure.
+//-----------------------------------------------------------------------------
+void CALLBACK BlinkCursor(HWND hwnd, UINT msg, UINT_PTR id, DWORD time)
+{
+ if(GetFocus() != MainWindow && !CursorDrawn) return;
+ if(Cursor.left == 0) return;
+
+ PlcCursor c;
+ memcpy(&c, &Cursor, sizeof(c));
+
+ c.top -= ScrollYOffset*POS_HEIGHT*FONT_HEIGHT;
+ c.left -= ScrollXOffset;
+
+ if(c.top >= IoListTop) return;
+
+ if(c.top + c.height >= IoListTop) {
+ c.height = IoListTop - c.top - 3;
+ }
+
+ Hdc = GetDC(MainWindow);
+ SelectObject(Hdc, GetStockObject(WHITE_BRUSH));
+ PatBlt(Hdc, c.left, c.top, c.width, c.height, PATINVERT);
+ CursorDrawn = !CursorDrawn;
+ ReleaseDC(MainWindow, Hdc);
+}
+
+//-----------------------------------------------------------------------------
+// Output a string to the screen at a particular location, in character-
+// sized units.
+//-----------------------------------------------------------------------------
+static void DrawCharsToScreen(int cx, int cy, char *str)
+{
+ cy -= ScrollYOffset*POS_HEIGHT;
+ if(cy < -2) return;
+ if(cy*FONT_HEIGHT + Y_PADDING > IoListTop) return;
+
+ COLORREF prev;
+ BOOL firstTime = TRUE;
+ BOOL inNumber = FALSE;
+ BOOL inComment = FALSE;
+ int inBrace = 0;
+ for(; *str; str++, cx++) {
+ int x = cx*FONT_WIDTH + X_PADDING;
+ int y = cy*FONT_HEIGHT + Y_PADDING;
+
+ BOOL hiOk = !(InSimulationMode || ThisHighlighted);
+
+ if(strchr("{}[]", *str) && hiOk && !inComment) {
+ if(*str == '{' || *str == '[') inBrace++;
+ if(inBrace == 1) {
+ prev = GetTextColor(Hdc);
+ SetTextColor(Hdc, HighlightColours.punct);
+ TextOut(Hdc, x, y, str, 1);
+ SetTextColor(Hdc, prev);
+ } else {
+ TextOut(Hdc, x, y, str, 1);
+ }
+ if(*str == ']' || *str == '}') inBrace--;
+ } else if((
+ (isdigit(*str) && (firstTime || isspace(str[-1])
+ || str[-1] == ':' || str[-1] == '[')) ||
+ (*str == '-' && isdigit(str[1]))) && hiOk && !inComment)
+ {
+ prev = GetTextColor(Hdc);
+ SetTextColor(Hdc, HighlightColours.lit);
+ TextOut(Hdc, x, y, str, 1);
+ SetTextColor(Hdc, prev);
+ inNumber = TRUE;
+ } else if(*str == '\x01') {
+ cx--;
+ if(hiOk) {
+ prev = GetTextColor(Hdc);
+ SetTextColor(Hdc, HighlightColours.op);
+ }
+ } else if(*str == '\x02') {
+ cx--;
+ if(hiOk) {
+ SetTextColor(Hdc, prev);
+ inComment = FALSE;
+ }
+ } else if(*str == '\x03') {
+ cx--;
+ if(hiOk) {
+ prev = GetTextColor(Hdc);
+ SetTextColor(Hdc, HighlightColours.comment);
+ inComment = TRUE;
+ }
+ } else if(inNumber) {
+ if(isdigit(*str) || *str == '.') {
+ prev = GetTextColor(Hdc);
+ SetTextColor(Hdc, HighlightColours.lit);
+ TextOut(Hdc, x, y, str, 1);
+ SetTextColor(Hdc, prev);
+ } else {
+ TextOut(Hdc, x, y, str, 1);
+ inNumber = FALSE;
+ }
+ } else {
+ TextOut(Hdc, x, y, str, 1);
+ }
+
+ firstTime = FALSE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Total number of columns that we can display in the given amount of
+// window area. Need to leave some slop on the right for the scrollbar, of
+// course.
+//-----------------------------------------------------------------------------
+int ScreenColsAvailable(void)
+{
+ RECT r;
+ GetClientRect(MainWindow, &r);
+
+ return (r.right - (X_PADDING + X_RIGHT_PADDING)) / (POS_WIDTH*FONT_WIDTH);
+}
+
+//-----------------------------------------------------------------------------
+// Total number of columns that we can display in the given amount of
+// window area. Need to leave some slop on the right for the scrollbar, of
+// course, and extra slop at the bottom for the horiz scrollbar if it is
+// shown.
+//-----------------------------------------------------------------------------
+int ScreenRowsAvailable(void)
+{
+ int adj;
+ if(ScrollXOffsetMax == 0) {
+ adj = 0;
+ } else {
+ adj = 18;
+ }
+ return (IoListTop - Y_PADDING - adj) / (POS_HEIGHT*FONT_HEIGHT);
+}
+
+//-----------------------------------------------------------------------------
+// Paint the ladder logic program to the screen. Also figure out where the
+// cursor should go and fill in coordinates for BlinkCursor. Not allowed to
+// draw deeper than IoListTop, as we would run in to the I/O listbox.
+//-----------------------------------------------------------------------------
+void PaintWindow(void)
+{
+ static HBITMAP BackBitmap;
+ static HDC BackDc;
+ static int BitmapWidth;
+
+ ok();
+
+ RECT r;
+ GetClientRect(MainWindow, &r);
+ int bw = r.right;
+ int bh = IoListTop;
+
+ HDC paintDc;
+ if(!BackDc) {
+ HWND desktop = GetDesktopWindow();
+ RECT dk;
+ GetClientRect(desktop, &dk);
+
+ BitmapWidth = max(2000, dk.right + 300);
+ BackBitmap = CreateCompatibleBitmap(Hdc, BitmapWidth, dk.bottom + 300);
+ BackDc = CreateCompatibleDC(Hdc);
+ SelectObject(BackDc, BackBitmap);
+ }
+ paintDc = Hdc;
+ Hdc = BackDc;
+
+ RECT fi;
+ fi.left = 0; fi.top = 0;
+ fi.right = BitmapWidth; fi.bottom = bh;
+ FillRect(Hdc, &fi, InSimulationMode ? SimBgBrush : BgBrush);
+
+ // now figure out how we should draw the ladder logic
+ ColsAvailable = ProgCountWidestRow();
+ if(ColsAvailable < ScreenColsAvailable()) {
+ ColsAvailable = ScreenColsAvailable();
+ }
+ memset(DisplayMatrix, 0, sizeof(DisplayMatrix));
+ SelectionActive = FALSE;
+ memset(&Cursor, 0, sizeof(Cursor));
+
+ DrawChars = DrawCharsToScreen;
+
+ int i;
+ int cy = 0;
+ int rowsAvailable = ScreenRowsAvailable();
+ for(i = 0; i < Prog.numRungs; i++) {
+ int thisHeight = POS_HEIGHT*CountHeightOfElement(ELEM_SERIES_SUBCKT,
+ Prog.rungs[i]);
+
+ // For speed, there is no need to draw everything all the time, but
+ // we still must draw a bit above and below so that the DisplayMatrix
+ // is filled in enough to make it possible to reselect using the
+ // cursor keys.
+ if(((cy + thisHeight) >= (ScrollYOffset - 8)*POS_HEIGHT) &&
+ (cy < (ScrollYOffset + rowsAvailable + 8)*POS_HEIGHT))
+ {
+ SetBkColor(Hdc, InSimulationMode ? HighlightColours.simBg :
+ HighlightColours.bg);
+ SetTextColor(Hdc, InSimulationMode ? HighlightColours.simRungNum :
+ HighlightColours.rungNum);
+ SelectObject(Hdc, FixedWidthFont);
+ int rung = i + 1;
+ int y = Y_PADDING + FONT_HEIGHT*cy;
+ int yp = y + FONT_HEIGHT*(POS_HEIGHT/2) -
+ POS_HEIGHT*FONT_HEIGHT*ScrollYOffset;
+
+ if(rung < 10) {
+ char r[1] = { rung + '0' };
+ TextOut(Hdc, 8 + FONT_WIDTH, yp, r, 1);
+ } else {
+ char r[2] = { (rung / 10) + '0', (rung % 10) + '0' };
+ TextOut(Hdc, 8, yp, r, 2);
+ }
+
+ int cx = 0;
+ DrawElement(ELEM_SERIES_SUBCKT, Prog.rungs[i], &cx, &cy,
+ Prog.rungPowered[i]);
+ }
+
+ cy += thisHeight;
+ cy += POS_HEIGHT;
+ }
+ cy -= 2;
+ DrawEndRung(0, cy);
+
+ if(SelectedGxAfterNextPaint >= 0) {
+ MoveCursorNear(SelectedGxAfterNextPaint, SelectedGyAfterNextPaint);
+ InvalidateRect(MainWindow, NULL, FALSE);
+ SelectedGxAfterNextPaint = -1;
+ SelectedGyAfterNextPaint = -1;
+ } else if(ScrollSelectedIntoViewAfterNextPaint && Selected) {
+ SelectElement(-1, -1, Selected->selectedState);
+ ScrollSelectedIntoViewAfterNextPaint = FALSE;
+ InvalidateRect(MainWindow, NULL, FALSE);
+ } else {
+ if(!SelectionActive) {
+ if(Prog.numRungs > 0) {
+ if(MoveCursorTopLeft()) {
+ InvalidateRect(MainWindow, NULL, FALSE);
+ }
+ }
+ }
+ }
+
+ // draw the `buses' at either side of the screen
+ r.left = X_PADDING - FONT_WIDTH;
+ r.top = 0;
+ r.right = r.left + 4;
+ r.bottom = IoListTop;
+ FillRect(Hdc, &r, InSimulationMode ? BusLeftBrush : BusBrush);
+
+ r.left += POS_WIDTH*FONT_WIDTH*ColsAvailable + 2;
+ r.right += POS_WIDTH*FONT_WIDTH*ColsAvailable + 2;
+ FillRect(Hdc, &r, InSimulationMode ? BusRightBus : BusBrush);
+
+ CursorDrawn = FALSE;
+
+ BitBlt(paintDc, 0, 0, bw, bh, BackDc, ScrollXOffset, 0, SRCCOPY);
+
+ if(InSimulationMode) {
+ KillTimer(MainWindow, TIMER_BLINK_CURSOR);
+ } else {
+ KillTimer(MainWindow, TIMER_BLINK_CURSOR);
+ BlinkCursor(NULL, 0, NULL, 0);
+ SetTimer(MainWindow, TIMER_BLINK_CURSOR, 800, BlinkCursor);
+ }
+
+ Hdc = paintDc;
+ ok();
+}
+
+//-----------------------------------------------------------------------------
+// Set up the syntax highlighting colours, according to the currently selected
+// scheme.
+//-----------------------------------------------------------------------------
+static void SetSyntaxHighlightingColours(void)
+{
+ static const SyntaxHighlightingColours Schemes[] = {
+ {
+ RGB(0, 0, 0), // bg
+ RGB(255, 255, 225), // def
+ RGB(255, 110, 90), // selected
+ RGB(255, 150, 90), // op
+ RGB(255, 255, 100), // punct
+ RGB(255, 160, 160), // lit
+ RGB(120, 255, 130), // name
+ RGB(130, 130, 130), // rungNum
+ RGB(130, 130, 245), // comment
+
+ RGB(255, 255, 255), // bus
+
+ RGB(0, 0, 0), // simBg
+ RGB(130, 130, 130), // simRungNum
+ RGB(100, 130, 130), // simOff
+ RGB(255, 150, 150), // simOn
+
+ RGB(255, 150, 150), // simBusLeft
+ RGB(150, 150, 255), // simBusRight
+ },
+ };
+
+ memcpy(&HighlightColours, &Schemes[0], sizeof(Schemes[0]));
+}
+
+//-----------------------------------------------------------------------------
+// Set up the stuff we'll need to draw our schematic diagram. Fonts, brushes,
+// pens, etc.
+//-----------------------------------------------------------------------------
+void InitForDrawing(void)
+{
+ SetSyntaxHighlightingColours();
+
+ FixedWidthFont = CreateFont(
+ FONT_HEIGHT, FONT_WIDTH,
+ 0, 0,
+ FW_REGULAR,
+ FALSE,
+ FALSE,
+ FALSE,
+ ANSI_CHARSET,
+ OUT_DEFAULT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ DEFAULT_QUALITY,
+ FF_DONTCARE,
+ "Lucida Console");
+
+ FixedWidthFontBold = CreateFont(
+ FONT_HEIGHT, FONT_WIDTH,
+ 0, 0,
+ FW_REGULAR, // the bold text renders funny under Vista
+ FALSE,
+ FALSE,
+ FALSE,
+ ANSI_CHARSET,
+ OUT_DEFAULT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ DEFAULT_QUALITY,
+ FF_DONTCARE,
+ "Lucida Console");
+
+ LOGBRUSH lb;
+ lb.lbStyle = BS_SOLID;
+ lb.lbColor = HighlightColours.simBusRight;
+ BusRightBus = CreateBrushIndirect(&lb);
+
+ lb.lbColor = HighlightColours.simBusLeft;
+ BusLeftBrush = CreateBrushIndirect(&lb);
+
+ lb.lbColor = HighlightColours.bus;
+ BusBrush = CreateBrushIndirect(&lb);
+
+ lb.lbColor = HighlightColours.bg;
+ BgBrush = CreateBrushIndirect(&lb);
+
+ lb.lbColor = HighlightColours.simBg;
+ SimBgBrush = CreateBrushIndirect(&lb);
+}
+
+//-----------------------------------------------------------------------------
+// DrawChars function, for drawing to the export buffer instead of to the
+// screen.
+//-----------------------------------------------------------------------------
+static void DrawCharsToExportBuffer(int cx, int cy, char *str)
+{
+ while(*str) {
+ if(*str >= 10) {
+ ExportBuffer[cy][cx] = *str;
+ cx++;
+ }
+ str++;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Export a text drawing of the ladder logic program to a file.
+//-----------------------------------------------------------------------------
+void ExportDrawingAsText(char *file)
+{
+ int maxWidth = ProgCountWidestRow();
+ ColsAvailable = maxWidth;
+
+ int totalHeight = 0;
+ int i;
+ for(i = 0; i < Prog.numRungs; i++) {
+ totalHeight += CountHeightOfElement(ELEM_SERIES_SUBCKT, Prog.rungs[i]);
+ totalHeight += 1;
+ }
+ totalHeight *= POS_HEIGHT;
+ totalHeight += 3;
+
+ ExportBuffer = (char **)CheckMalloc(totalHeight * sizeof(char *));
+
+ int l = maxWidth*POS_WIDTH + 8;
+ for(i = 0; i < totalHeight; i++) {
+ ExportBuffer[i] = (char *)CheckMalloc(l);
+ memset(ExportBuffer[i], ' ', l-1);
+ ExportBuffer[i][4] = '|';
+ ExportBuffer[i][3] = '|';
+ ExportBuffer[i][l-3] = '|';
+ ExportBuffer[i][l-2] = '|';
+ ExportBuffer[i][l-1] = '\0';
+ }
+
+ DrawChars = DrawCharsToExportBuffer;
+
+ int cy = 1;
+ for(i = 0; i < Prog.numRungs; i++) {
+ int cx = 5;
+ DrawElement(ELEM_SERIES_SUBCKT, Prog.rungs[i], &cx, &cy,
+ Prog.rungPowered[i]);
+
+ if((i + 1) < 10) {
+ ExportBuffer[cy+1][1] = '0' + (i + 1);
+ } else {
+ ExportBuffer[cy+1][1] = '0' + ((i + 1) % 10);
+ ExportBuffer[cy+1][0] = '0' + ((i + 1) / 10);
+ }
+
+ cy += POS_HEIGHT*CountHeightOfElement(ELEM_SERIES_SUBCKT,
+ Prog.rungs[i]);
+ cy += POS_HEIGHT;
+ }
+ cy -= 2;
+ DrawEndRung(5, cy);
+
+ FILE *f = fopen(file, "w");
+ if(!f) {
+ Error(_("Couldn't open '%s'\n"), f);
+ return;
+ }
+
+ fprintf(f, "LDmicro export text\n");
+
+ if(Prog.mcu) {
+ fprintf(f, "for '%s', %.6f MHz crystal, %.1f ms cycle time\n\n",
+ Prog.mcu->mcuName, Prog.mcuClock/1e6, Prog.cycleTime/1e3);
+ } else {
+ fprintf(f, "no MCU assigned, %.6f MHz crystal, %.1f ms cycle time\n\n",
+ Prog.mcuClock/1e6, Prog.cycleTime/1e3);
+ }
+
+ fprintf(f, "\nLADDER DIAGRAM:\n\n");
+
+ for(i = 0; i < totalHeight; i++) {
+ ExportBuffer[i][4] = '|';
+ fprintf(f, "%s\n", ExportBuffer[i]);
+ CheckFree(ExportBuffer[i]);
+ }
+ CheckFree(ExportBuffer);
+ ExportBuffer = NULL;
+
+ fprintf(f, _("\n\nI/O ASSIGNMENT:\n\n"));
+
+ fprintf(f, _(" Name | Type | Pin\n"));
+ fprintf(f, " ----------------------------+--------------------+------\n");
+ for(i = 0; i < Prog.io.count; i++) {
+ char b[1024];
+ memset(b, '\0', sizeof(b));
+
+ PlcProgramSingleIo *io = &Prog.io.assignment[i];
+ char *type = IoTypeToString(io->type);
+ char pin[MAX_NAME_LEN];
+
+ PinNumberForIo(pin, io);
+
+ sprintf(b, " | | %s\n",
+ pin);
+
+ memcpy(b+2, io->name, strlen(io->name));
+ memcpy(b+31, type, strlen(type));
+ fprintf(f, "%s", b);
+ }
+
+ fclose(f);
+
+ // we may have trashed the grid tables a bit; a repaint will fix that
+ InvalidateRect(MainWindow, NULL, FALSE);
+}
+
+//-----------------------------------------------------------------------------
+// Determine the settings of the vertical and (if needed) horizontal
+// scrollbars used to scroll our view of the program.
+//-----------------------------------------------------------------------------
+void SetUpScrollbars(BOOL *horizShown, SCROLLINFO *horiz, SCROLLINFO *vert)
+{
+ int totalHeight = 0;
+ int i;
+ for(i = 0; i < Prog.numRungs; i++) {
+ totalHeight += CountHeightOfElement(ELEM_SERIES_SUBCKT, Prog.rungs[i]);
+ totalHeight++;
+ }
+ totalHeight += 1; // for the end rung
+
+ int totalWidth = ProgCountWidestRow();
+
+ if(totalWidth <= ScreenColsAvailable()) {
+ *horizShown = FALSE;
+ ScrollXOffset = 0;
+ ScrollXOffsetMax = 0;
+ } else {
+ *horizShown = TRUE;
+ memset(horiz, 0, sizeof(*horiz));
+ horiz->cbSize = sizeof(*horiz);
+ horiz->fMask = SIF_DISABLENOSCROLL | SIF_ALL;
+ horiz->nMin = 0;
+ horiz->nMax = X_PADDING + totalWidth*POS_WIDTH*FONT_WIDTH;
+ RECT r;
+ GetClientRect(MainWindow, &r);
+ horiz->nPage = r.right - X_PADDING;
+ horiz->nPos = ScrollXOffset;
+
+ ScrollXOffsetMax = horiz->nMax - horiz->nPage + 1;
+ if(ScrollXOffset > ScrollXOffsetMax) ScrollXOffset = ScrollXOffsetMax;
+ if(ScrollXOffset < 0) ScrollXOffset = 0;
+ }
+
+ vert->cbSize = sizeof(*vert);
+ vert->fMask = SIF_DISABLENOSCROLL | SIF_ALL;
+ vert->nMin = 0;
+ vert->nMax = totalHeight - 1;
+ vert->nPos = ScrollYOffset;
+ vert->nPage = ScreenRowsAvailable();
+
+ ScrollYOffsetMax = vert->nMax - vert->nPage + 1;
+
+ if(ScrollYOffset > ScrollYOffsetMax) ScrollYOffset = ScrollYOffsetMax;
+ if(ScrollYOffset < 0) ScrollYOffset = 0;
+}
diff --git a/ldmicro/helpdialog.cpp b/ldmicro/helpdialog.cpp
new file mode 100644
index 0000000..f5c28e1
--- /dev/null
+++ b/ldmicro/helpdialog.cpp
@@ -0,0 +1,308 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Window to show our internal help. Roll my own using a rich edit control so
+// that I don't have to distribute a separate Windows Help or HTML file; the
+// manual is compiled in to the exe. Do pretty syntax highlighting style
+// colours.
+// Jonathan Westhues, Dec 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+extern char *HelpText[];
+extern char *HelpTextDe[];
+extern char *HelpTextFr[];
+extern char *HelpTextTr[];
+
+static char *AboutText[] = {
+"",
+"ABOUT LDMICRO",
+"=============",
+"",
+"LDmicro is a ladder logic editor, simulator and compiler for 8-bit",
+"microcontrollers. It can generate native code for Atmel AVR and Microchip",
+"PIC16 CPUs from a ladder diagram.",
+"",
+"This program 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.",
+"",
+"This program 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 this program. If not, see .",
+"",
+"The source code for LDmicro is available at",
+"",
+" http://cq.cx/ladder.pl",
+"",
+"Copyright 2005-2010 Jonathan Westhues",
+"Release 2.2, built " __TIME__ " " __DATE__ ".",
+"",
+"Email: user jwesthues, at host cq.cx",
+"",
+NULL
+};
+
+static char **Text[] = {
+#if defined(LDLANG_EN) || \
+ defined(LDLANG_ES) || \
+ defined(LDLANG_IT) || \
+ defined(LDLANG_PT)
+ HelpText,
+#elif defined(LDLANG_DE)
+ HelpTextDe,
+#elif defined(LDLANG_FR)
+ HelpTextFr,
+#elif defined(LDLANG_TR)
+ HelpTextTr,
+#else
+# error "Bad language"
+#endif
+
+ // Let's always keep the about text in English.
+ AboutText
+};
+
+static HWND HelpDialog[2];
+static HWND RichEdit[2];
+
+static BOOL HelpWindowOpen[2];
+
+static int TitleHeight;
+
+#define RICH_EDIT_HEIGHT(h) \
+ ((((h) - 3 + (FONT_HEIGHT/2)) / FONT_HEIGHT) * FONT_HEIGHT)
+
+static void SizeRichEdit(int a)
+{
+ RECT r;
+ GetClientRect(HelpDialog[a], &r);
+
+ SetWindowPos(RichEdit[a], HWND_TOP, 6, 3, r.right - 6,
+ RICH_EDIT_HEIGHT(r.bottom), 0);
+}
+
+static BOOL Resizing(RECT *r, int wParam)
+{
+ BOOL touched = FALSE;
+ if(r->right - r->left < 650) {
+ int diff = 650 - (r->right - r->left);
+ if(wParam == WMSZ_RIGHT || wParam == WMSZ_TOPRIGHT ||
+ wParam == WMSZ_BOTTOMRIGHT)
+ {
+ r->right += diff;
+ } else {
+ r->left -= diff;
+ }
+ touched = TRUE;
+ }
+
+ if(!(wParam == WMSZ_LEFT || wParam == WMSZ_RIGHT)) {
+ int h = r->bottom - r->top - TitleHeight - 5;
+ if(RICH_EDIT_HEIGHT(h) != h) {
+ int diff = h - RICH_EDIT_HEIGHT(h);
+ if(wParam == WMSZ_TOP || wParam == WMSZ_TOPRIGHT ||
+ wParam == WMSZ_TOPLEFT)
+ {
+ r->top += diff;
+ } else {
+ r->bottom -= diff;
+ }
+ touched = TRUE;
+ }
+ }
+
+ return !touched;
+}
+
+static void MakeControls(int a)
+{
+ HMODULE re = LoadLibrary("RichEd20.dll");
+ if(!re) oops();
+
+ RichEdit[a] = CreateWindowEx(0, RICHEDIT_CLASS,
+ "", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | ES_READONLY |
+ ES_MULTILINE | WS_VSCROLL,
+ 0, 0, 100, 100, HelpDialog[a], NULL, Instance, NULL);
+
+ SendMessage(RichEdit[a], WM_SETFONT, (WPARAM)FixedWidthFont, TRUE);
+ SendMessage(RichEdit[a], EM_SETBKGNDCOLOR, (WPARAM)0, RGB(0, 0, 0));
+
+ SizeRichEdit(a);
+
+ int i;
+ BOOL nextSubHead = FALSE;
+ for(i = 0; Text[a][i]; i++) {
+ char *s = Text[a][i];
+
+ CHARFORMAT cf;
+ cf.cbSize = sizeof(cf);
+ cf.dwMask = CFM_BOLD | CFM_COLOR;
+ cf.dwEffects = 0;
+ if((s[0] == '=') ||
+ (Text[a][i+1] && Text[a][i+1][0] == '='))
+ {
+ cf.crTextColor = RGB(255, 255, 110);
+ } else if(s[3] == '|' && s[4] == '|') {
+ cf.crTextColor = RGB(255, 110, 255);
+ } else if(s[0] == '>' || nextSubHead) {
+ // Need to make a copy because the strings we are passed aren't
+ // mutable.
+ char copy[1024];
+ if(strlen(s) >= sizeof(copy)) oops();
+ strcpy(copy, s);
+
+ int j;
+ for(j = 1; copy[j]; j++) {
+ if(copy[j] == ' ' && copy[j-1] == ' ')
+ break;
+ }
+ BOOL justHeading = (copy[j] == '\0');
+ copy[j] = '\0';
+ cf.crTextColor = RGB(110, 255, 110);
+ SendMessage(RichEdit[a], EM_SETCHARFORMAT, SCF_SELECTION,
+ (LPARAM)&cf);
+ SendMessage(RichEdit[a], EM_REPLACESEL, (WPARAM)FALSE,
+ (LPARAM)copy);
+ SendMessage(RichEdit[a], EM_SETSEL, (WPARAM)-1, (LPARAM)-1);
+
+ // Special case if there's nothing except title on the line
+ if(!justHeading) {
+ copy[j] = ' ';
+ }
+ s += j;
+ cf.crTextColor = RGB(255, 110, 255);
+ nextSubHead = !nextSubHead;
+ } else {
+ cf.crTextColor = RGB(255, 255, 255);
+ }
+
+ SendMessage(RichEdit[a], EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+ SendMessage(RichEdit[a], EM_REPLACESEL, (WPARAM)FALSE, (LPARAM)s);
+ SendMessage(RichEdit[a], EM_SETSEL, (WPARAM)-1, (LPARAM)-1);
+
+ if(Text[a][i+1]) {
+ SendMessage(RichEdit[a], EM_REPLACESEL, FALSE, (LPARAM)"\r\n");
+ SendMessage(RichEdit[a], EM_SETSEL, (WPARAM)-1, (LPARAM)-1);
+ }
+ }
+
+ SendMessage(RichEdit[a], EM_SETSEL, (WPARAM)0, (LPARAM)0);
+}
+
+//-----------------------------------------------------------------------------
+// Window proc for the help dialog.
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK HelpProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam)
+{
+ int a = (hwnd == HelpDialog[0] ? 0 : 1);
+ switch (msg) {
+ case WM_SIZING: {
+ RECT *r = (RECT *)lParam;
+ return Resizing(r, wParam);
+ break;
+ }
+ case WM_SIZE:
+ SizeRichEdit(a);
+ break;
+
+ case WM_ACTIVATE:
+ case WM_KEYDOWN:
+ SetFocus(RichEdit[a]);
+ break;
+
+ case WM_DESTROY:
+ case WM_CLOSE:
+ HelpWindowOpen[a] = FALSE;
+ // fall through
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Create the class for the help window.
+//-----------------------------------------------------------------------------
+static void MakeClass(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)HelpProc;
+ wc.hInstance = Instance;
+ wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ wc.lpszClassName = "LDmicroHelp";
+ wc.lpszMenuName = NULL;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hIcon = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
+ IMAGE_ICON, 32, 32, 0);
+ wc.hIconSm = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
+ IMAGE_ICON, 16, 16, 0);
+
+ RegisterClassEx(&wc);
+}
+
+void ShowHelpDialog(BOOL about)
+{
+ int a = about ? 1 : 0;
+ if(HelpWindowOpen[a]) {
+ SetForegroundWindow(HelpDialog[a]);
+ return;
+ }
+
+ MakeClass();
+
+ char *s = about ? "About LDmicro" : "LDmicro Help";
+ HelpDialog[a] = CreateWindowEx(0, "LDmicroHelp", s,
+ WS_OVERLAPPED | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX |
+ WS_SIZEBOX,
+ 100, 100, 650, 300+10*FONT_HEIGHT, NULL, NULL, Instance, NULL);
+ MakeControls(a);
+
+ ShowWindow(HelpDialog[a], TRUE);
+ SetFocus(RichEdit[a]);
+
+ HelpWindowOpen[a] = TRUE;
+
+ RECT r;
+ GetClientRect(HelpDialog[a], &r);
+ TitleHeight = 300 - r.bottom;
+
+ GetWindowRect(HelpDialog[a], &r);
+ Resizing(&r, WMSZ_TOP);
+ SetWindowPos(HelpDialog[a], HWND_TOP, r.left, r.top, r.right - r.left,
+ r.bottom - r.top, 0);
+}
diff --git a/ldmicro/intcode.cpp b/ldmicro/intcode.cpp
new file mode 100644
index 0000000..2ef460e
--- /dev/null
+++ b/ldmicro/intcode.cpp
@@ -0,0 +1,1179 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// 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
+#include
+#include
+#include
+
+#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;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Report an error if a constant doesn't fit in 16 bits.
+//-----------------------------------------------------------------------------
+void CheckConstantInRange(int v)
+{
+ if(v < -32768 || v > 32767) {
+ Error(_("Constant %d out of range: -32768 to 32767 inclusive."), v);
+ CompileError();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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);
+ }
+
+ CheckConstantInRange(val);
+
+ 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,
+ stateInOut);
+ }
+ Comment("] finish series");
+ break;
+ }
+ case ELEM_PARALLEL_SUBCKT: {
+ char parThis[MAX_NAME_LEN];
+ GenSymParThis(parThis);
+
+ char parOut[MAX_NAME_LEN];
+ GenSymParOut(parOut);
+
+ Comment("start parallel [");
+
+ Op(INT_CLEAR_BIT, parOut);
+
+ ElemSubcktParallel *p = (ElemSubcktParallel *)any;
+ int i;
+ for(i = 0; i < p->count; i++) {
+ Op(INT_COPY_BIT_TO_BIT, parThis, stateInOut);
+
+ IntCodeFromCircuit(p->contents[i].which, p->contents[i].d.any,
+ parThis);
+
+ Op(INT_IF_BIT_SET, parThis);
+ Op(INT_SET_BIT, parOut);
+ Op(INT_END_IF);
+ }
+ Op(INT_COPY_BIT_TO_BIT, stateInOut, parOut);
+ Comment("] finish parallel");
+
+ break;
+ }
+ case ELEM_CONTACTS: {
+ if(l->d.contacts.negated) {
+ Op(INT_IF_BIT_SET, l->d.contacts.name);
+ } else {
+ Op(INT_IF_BIT_CLEAR, l->d.contacts.name);
+ }
+ Op(INT_CLEAR_BIT, stateInOut);
+ Op(INT_END_IF);
+ break;
+ }
+ case ELEM_COIL: {
+ if(l->d.coil.negated) {
+ Op(INT_IF_BIT_SET, stateInOut);
+ Op(INT_CLEAR_BIT, l->d.contacts.name);
+ Op(INT_ELSE);
+ Op(INT_SET_BIT, l->d.contacts.name);
+ Op(INT_END_IF);
+ } else if(l->d.coil.setOnly) {
+ Op(INT_IF_BIT_SET, stateInOut);
+ Op(INT_SET_BIT, l->d.contacts.name);
+ Op(INT_END_IF);
+ } else if(l->d.coil.resetOnly) {
+ Op(INT_IF_BIT_SET, stateInOut);
+ Op(INT_CLEAR_BIT, l->d.contacts.name);
+ Op(INT_END_IF);
+ } else {
+ Op(INT_COPY_BIT_TO_BIT, l->d.contacts.name, stateInOut);
+ }
+ break;
+ }
+ case ELEM_RTO: {
+ int period = TimerPeriod(l);
+
+ Op(INT_IF_VARIABLE_LES_LITERAL, l->d.timer.name, period);
+
+ Op(INT_IF_BIT_SET, stateInOut);
+ Op(INT_INCREMENT_VARIABLE, l->d.timer.name);
+ Op(INT_END_IF);
+ Op(INT_CLEAR_BIT, stateInOut);
+
+ Op(INT_ELSE);
+
+ Op(INT_SET_BIT, stateInOut);
+
+ Op(INT_END_IF);
+
+ break;
+ }
+ case ELEM_RES:
+ Op(INT_IF_BIT_SET, stateInOut);
+ Op(INT_SET_VARIABLE_TO_LITERAL, l->d.reset.name);
+ Op(INT_END_IF);
+ break;
+
+ case ELEM_TON: {
+ int period = TimerPeriod(l);
+
+ Op(INT_IF_BIT_SET, stateInOut);
+
+ Op(INT_IF_VARIABLE_LES_LITERAL, l->d.timer.name, period);
+
+ Op(INT_INCREMENT_VARIABLE, l->d.timer.name);
+ Op(INT_CLEAR_BIT, stateInOut);
+ Op(INT_END_IF);
+
+ Op(INT_ELSE);
+
+ Op(INT_SET_VARIABLE_TO_LITERAL, l->d.timer.name);
+
+ Op(INT_END_IF);
+
+ break;
+ }
+ case ELEM_TOF: {
+ int period = TimerPeriod(l);
+
+ // All variables start at zero by default, so by default the
+ // TOF timer would start out with its output forced HIGH, until
+ // it finishes counting up. This does not seem to be what
+ // people expect, so add a special case to fix that up.
+ char antiGlitchName[MAX_NAME_LEN];
+ sprintf(antiGlitchName, "$%s_antiglitch", l->d.timer.name);
+ Op(INT_IF_BIT_CLEAR, antiGlitchName);
+ Op(INT_SET_VARIABLE_TO_LITERAL, l->d.timer.name, period);
+ Op(INT_END_IF);
+ Op(INT_SET_BIT, antiGlitchName);
+
+ Op(INT_IF_BIT_CLEAR, stateInOut);
+
+ Op(INT_IF_VARIABLE_LES_LITERAL, l->d.timer.name, period);
+
+ Op(INT_INCREMENT_VARIABLE, l->d.timer.name);
+ Op(INT_SET_BIT, stateInOut);
+ Op(INT_END_IF);
+
+ Op(INT_ELSE);
+
+ Op(INT_SET_VARIABLE_TO_LITERAL, l->d.timer.name);
+
+ Op(INT_END_IF);
+ break;
+ }
+ case ELEM_CTU: {
+ CheckConstantInRange(l->d.counter.max);
+ char storeName[MAX_NAME_LEN];
+ GenSymOneShot(storeName);
+
+ Op(INT_IF_BIT_SET, stateInOut);
+ Op(INT_IF_BIT_CLEAR, storeName);
+ Op(INT_INCREMENT_VARIABLE, l->d.counter.name);
+ Op(INT_END_IF);
+ Op(INT_END_IF);
+ Op(INT_COPY_BIT_TO_BIT, storeName, stateInOut);
+
+ Op(INT_IF_VARIABLE_LES_LITERAL, l->d.counter.name,
+ l->d.counter.max);
+ Op(INT_CLEAR_BIT, stateInOut);
+ Op(INT_ELSE);
+ Op(INT_SET_BIT, stateInOut);
+ Op(INT_END_IF);
+ break;
+ }
+ case ELEM_CTD: {
+ CheckConstantInRange(l->d.counter.max);
+ char storeName[MAX_NAME_LEN];
+ GenSymOneShot(storeName);
+
+ Op(INT_IF_BIT_SET, stateInOut);
+ Op(INT_IF_BIT_CLEAR, storeName);
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch", 1);
+ Op(INT_SET_VARIABLE_SUBTRACT, l->d.counter.name,
+ l->d.counter.name, "$scratch", 0);
+ Op(INT_END_IF);
+ Op(INT_END_IF);
+ Op(INT_COPY_BIT_TO_BIT, storeName, stateInOut);
+
+ Op(INT_IF_VARIABLE_LES_LITERAL, l->d.counter.name,
+ l->d.counter.max);
+ Op(INT_CLEAR_BIT, stateInOut);
+ Op(INT_ELSE);
+ Op(INT_SET_BIT, stateInOut);
+ Op(INT_END_IF);
+ break;
+ }
+ case ELEM_CTC: {
+ char storeName[MAX_NAME_LEN];
+ GenSymOneShot(storeName);
+
+ Op(INT_IF_BIT_SET, stateInOut);
+ Op(INT_IF_BIT_CLEAR, storeName);
+ Op(INT_INCREMENT_VARIABLE, l->d.counter.name);
+ Op(INT_IF_VARIABLE_LES_LITERAL, l->d.counter.name,
+ l->d.counter.max+1);
+ Op(INT_ELSE);
+ Op(INT_SET_VARIABLE_TO_LITERAL, l->d.counter.name,
+ (SWORD)0);
+ Op(INT_END_IF);
+ Op(INT_END_IF);
+ Op(INT_END_IF);
+ Op(INT_COPY_BIT_TO_BIT, storeName, stateInOut);
+ break;
+ }
+ case ELEM_GRT:
+ case ELEM_GEQ:
+ case ELEM_LES:
+ case ELEM_LEQ:
+ case ELEM_NEQ:
+ case ELEM_EQU: {
+ char *op1 = VarFromExpr(l->d.cmp.op1, "$scratch");
+ char *op2 = VarFromExpr(l->d.cmp.op2, "$scratch2");
+
+ if(which == ELEM_GRT) {
+ Op(INT_IF_VARIABLE_GRT_VARIABLE, op1, op2);
+ Op(INT_ELSE);
+ } else if(which == ELEM_GEQ) {
+ Op(INT_IF_VARIABLE_GRT_VARIABLE, op2, op1);
+ } else if(which == ELEM_LES) {
+ Op(INT_IF_VARIABLE_GRT_VARIABLE, op2, op1);
+ Op(INT_ELSE);
+ } else if(which == ELEM_LEQ) {
+ Op(INT_IF_VARIABLE_GRT_VARIABLE, op1, op2);
+ } else if(which == ELEM_EQU) {
+ Op(INT_IF_VARIABLE_EQUALS_VARIABLE, op1, op2);
+ Op(INT_ELSE);
+ } else if(which == ELEM_NEQ) {
+ Op(INT_IF_VARIABLE_EQUALS_VARIABLE, op1, op2);
+ } else oops();
+
+ Op(INT_CLEAR_BIT, stateInOut);
+ Op(INT_END_IF);
+ break;
+ }
+ case ELEM_ONE_SHOT_RISING: {
+ char storeName[MAX_NAME_LEN];
+ GenSymOneShot(storeName);
+
+ Op(INT_COPY_BIT_TO_BIT, "$scratch", stateInOut);
+ Op(INT_IF_BIT_SET, storeName);
+ Op(INT_CLEAR_BIT, stateInOut);
+ Op(INT_END_IF);
+ Op(INT_COPY_BIT_TO_BIT, storeName, "$scratch");
+ break;
+ }
+ case ELEM_ONE_SHOT_FALLING: {
+ char storeName[MAX_NAME_LEN];
+ GenSymOneShot(storeName);
+
+ Op(INT_COPY_BIT_TO_BIT, "$scratch", stateInOut);
+
+ Op(INT_IF_BIT_CLEAR, stateInOut);
+ Op(INT_IF_BIT_SET, storeName);
+ Op(INT_SET_BIT, stateInOut);
+ Op(INT_END_IF);
+ Op(INT_ELSE);
+ Op(INT_CLEAR_BIT, stateInOut);
+ Op(INT_END_IF);
+
+ Op(INT_COPY_BIT_TO_BIT, storeName, "$scratch");
+ break;
+ }
+ case ELEM_MOVE: {
+ if(IsNumber(l->d.move.dest)) {
+ Error(_("Move instruction: '%s' not a valid destination."),
+ l->d.move.dest);
+ CompileError();
+ }
+ Op(INT_IF_BIT_SET, stateInOut);
+ if(IsNumber(l->d.move.src)) {
+ Op(INT_SET_VARIABLE_TO_LITERAL, l->d.move.dest,
+ CheckMakeNumber(l->d.move.src));
+ } else {
+ Op(INT_SET_VARIABLE_TO_VARIABLE, l->d.move.dest, l->d.move.src,
+ 0);
+ }
+ Op(INT_END_IF);
+ break;
+ }
+
+ // These four are highly processor-dependent; the int code op does
+ // most of the highly specific work
+ {
+ case ELEM_READ_ADC:
+ Op(INT_IF_BIT_SET, stateInOut);
+ Op(INT_READ_ADC, l->d.readAdc.name);
+ Op(INT_END_IF);
+ break;
+
+ case ELEM_SET_PWM: {
+ Op(INT_IF_BIT_SET, stateInOut);
+ char line[80];
+ // ugh; need a >16 bit literal though, could be >64 kHz
+ sprintf(line, "%d", l->d.setPwm.targetFreq);
+ Op(INT_SET_PWM, l->d.readAdc.name, line);
+ Op(INT_END_IF);
+ break;
+ }
+ case ELEM_PERSIST: {
+ Op(INT_IF_BIT_SET, stateInOut);
+
+ // At startup, get the persistent variable from flash.
+ char isInit[MAX_NAME_LEN];
+ GenSymOneShot(isInit);
+ Op(INT_IF_BIT_CLEAR, isInit);
+ Op(INT_CLEAR_BIT, "$scratch");
+ Op(INT_EEPROM_BUSY_CHECK, "$scratch");
+ Op(INT_IF_BIT_CLEAR, "$scratch");
+ Op(INT_SET_BIT, isInit);
+ Op(INT_EEPROM_READ, l->d.persist.var, EepromAddrFree);
+ Op(INT_END_IF);
+ Op(INT_END_IF);
+
+ // While running, continuously compare the EEPROM copy of
+ // the variable against the RAM one; if they are different,
+ // write the RAM one to EEPROM.
+ Op(INT_CLEAR_BIT, "$scratch");
+ Op(INT_EEPROM_BUSY_CHECK, "$scratch");
+ Op(INT_IF_BIT_CLEAR, "$scratch");
+ Op(INT_EEPROM_READ, "$scratch", EepromAddrFree);
+ Op(INT_IF_VARIABLE_EQUALS_VARIABLE, "$scratch",
+ l->d.persist.var);
+ Op(INT_ELSE);
+ Op(INT_EEPROM_WRITE, l->d.persist.var, EepromAddrFree);
+ Op(INT_END_IF);
+ Op(INT_END_IF);
+
+ Op(INT_END_IF);
+
+ EepromAddrFree += 2;
+ break;
+ }
+ case ELEM_UART_SEND:
+ Op(INT_UART_SEND, l->d.uart.name, stateInOut);
+ break;
+
+ case ELEM_UART_RECV:
+ Op(INT_IF_BIT_SET, stateInOut);
+ Op(INT_UART_RECV, l->d.uart.name, stateInOut);
+ Op(INT_END_IF);
+ break;
+ }
+
+ case ELEM_ADD:
+ case ELEM_SUB:
+ case ELEM_MUL:
+ case ELEM_DIV: {
+ if(IsNumber(l->d.math.dest)) {
+ Error(_("Math instruction: '%s' not a valid destination."),
+ l->d.math.dest);
+ CompileError();
+ }
+ Op(INT_IF_BIT_SET, stateInOut);
+
+ char *op1 = VarFromExpr(l->d.math.op1, "$scratch");
+ char *op2 = VarFromExpr(l->d.math.op2, "$scratch2");
+
+ int intOp;
+ if(which == ELEM_ADD) {
+ intOp = INT_SET_VARIABLE_ADD;
+ } else if(which == ELEM_SUB) {
+ intOp = INT_SET_VARIABLE_SUBTRACT;
+ } else if(which == ELEM_MUL) {
+ intOp = INT_SET_VARIABLE_MULTIPLY;
+ } else if(which == ELEM_DIV) {
+ intOp = INT_SET_VARIABLE_DIVIDE;
+ } else oops();
+
+ Op(intOp, l->d.math.dest, op1, op2, 0);
+
+ Op(INT_END_IF);
+ break;
+ }
+ case ELEM_MASTER_RELAY:
+ // Tricky: must set the master control relay if we reach this
+ // instruction while the master control relay is cleared, because
+ // otherwise there is no good way for it to ever become set
+ // again.
+ Op(INT_IF_BIT_CLEAR, "$mcr");
+ Op(INT_SET_BIT, "$mcr");
+ Op(INT_ELSE);
+ Op(INT_COPY_BIT_TO_BIT, "$mcr", stateInOut);
+ Op(INT_END_IF);
+ break;
+
+ case ELEM_SHIFT_REGISTER: {
+ char storeName[MAX_NAME_LEN];
+ GenSymOneShot(storeName);
+ Op(INT_IF_BIT_SET, stateInOut);
+ Op(INT_IF_BIT_CLEAR, storeName);
+ int i;
+ for(i = (l->d.shiftRegister.stages-2); i >= 0; i--) {
+ char dest[MAX_NAME_LEN+10], src[MAX_NAME_LEN+10];
+ sprintf(src, "%s%d", l->d.shiftRegister.name, i);
+ sprintf(dest, "%s%d", l->d.shiftRegister.name, i+1);
+ Op(INT_SET_VARIABLE_TO_VARIABLE, dest, src);
+ }
+ Op(INT_END_IF);
+ Op(INT_END_IF);
+ Op(INT_COPY_BIT_TO_BIT, storeName, stateInOut);
+ break;
+ }
+ case ELEM_LOOK_UP_TABLE: {
+ // God this is stupid; but it will have to do, at least until I
+ // add new int code instructions for this.
+ int i;
+ Op(INT_IF_BIT_SET, stateInOut);
+ ElemLookUpTable *t = &(l->d.lookUpTable);
+ for(i = 0; i < t->count; i++) {
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch", i);
+ Op(INT_IF_VARIABLE_EQUALS_VARIABLE, t->index, "$scratch");
+ Op(INT_SET_VARIABLE_TO_LITERAL, t->dest, t->vals[i]);
+ Op(INT_END_IF);
+ }
+ Op(INT_END_IF);
+ break;
+ }
+ case ELEM_PIECEWISE_LINEAR: {
+ // This one is not so obvious; we have to decide how best to
+ // perform the linear interpolation, using our 16-bit fixed
+ // point math.
+ ElemPiecewiseLinear *t = &(l->d.piecewiseLinear);
+ if(t->count == 0) {
+ Error(_("Piecewise linear lookup table with zero elements!"));
+ CompileError();
+ }
+ int i;
+ int xThis = t->vals[0];
+ for(i = 1; i < t->count; i++) {
+ if(t->vals[i*2] <= xThis) {
+ Error(_("x values in piecewise linear table must be "
+ "strictly increasing."));
+ CompileError();
+ }
+ xThis = t->vals[i*2];
+ }
+ Op(INT_IF_BIT_SET, stateInOut);
+ for(i = t->count - 1; i >= 1; i--) {
+ int thisDx = t->vals[i*2] - t->vals[(i-1)*2];
+ int thisDy = t->vals[i*2 + 1] - t->vals[(i-1)*2 + 1];
+ // The output point is given by
+ // yout = y[i-1] + (xin - x[i-1])*dy/dx
+ // and this is the best form in which to keep it, numerically
+ // speaking, because you can always fix numerical problems
+ // by moving the PWL points closer together.
+
+ // Check for numerical problems, and fail if we have them.
+ if((thisDx*thisDy) >= 32767 || (thisDx*thisDy) <= -32768) {
+ Error(_("Numerical problem with piecewise linear lookup "
+ "table. Either make the table entries smaller, "
+ "or space the points together more closely.\r\n\r\n"
+ "See the help file for details."));
+ CompileError();
+ }
+
+ // Hack to avoid AVR brge issue again, since long jumps break
+ Op(INT_CLEAR_BIT, "$scratch");
+ Op(INT_IF_VARIABLE_LES_LITERAL, t->index, t->vals[i*2]+1);
+ Op(INT_SET_BIT, "$scratch");
+ Op(INT_END_IF);
+
+ Op(INT_IF_BIT_SET, "$scratch");
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch", t->vals[(i-1)*2]);
+ Op(INT_SET_VARIABLE_SUBTRACT, "$scratch", t->index,
+ "$scratch", 0);
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch2", thisDx);
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch3", thisDy);
+ Op(INT_SET_VARIABLE_MULTIPLY, t->dest, "$scratch", "$scratch3",
+ 0);
+ Op(INT_SET_VARIABLE_DIVIDE, t->dest, t->dest, "$scratch2", 0);
+
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch",
+ t->vals[(i-1)*2 + 1]);
+ Op(INT_SET_VARIABLE_ADD, t->dest, t->dest, "$scratch", 0);
+ Op(INT_END_IF);
+ }
+ Op(INT_END_IF);
+ break;
+ }
+ case ELEM_FORMATTED_STRING: {
+ // Okay, this one is terrible and ineffcient, and it's a huge pain
+ // to implement, but people want it a lot. The hard part is that
+ // we have to let the PLC keep cycling, of course, and also that
+ // we must do the integer to ASCII conversion sensisbly, with
+ // only one divide per PLC cycle.
+
+ // This variable is basically our sequencer: it is a counter that
+ // increments every time we send a character.
+ char seq[MAX_NAME_LEN];
+ GenSymFormattedString(seq);
+
+ // The variable whose value we might interpolate.
+ char *var = l->d.fmtdStr.var;
+
+ // This is the state variable for our integer-to-string conversion.
+ // It contains the absolute value of var, possibly with some
+ // of the higher powers of ten missing.
+ char convertState[MAX_NAME_LEN];
+ GenSymFormattedString(convertState);
+
+ // We might need to suppress some leading zeros.
+ char isLeadingZero[MAX_NAME_LEN];
+ GenSymFormattedString(isLeadingZero);
+
+ // This is a table of characters to transmit, as a function of the
+ // sequencer position (though we might have a hole in the middle
+ // for the variable output)
+ char outputChars[MAX_LOOK_UP_TABLE_LEN];
+
+ BOOL mustDoMinus = FALSE;
+
+ // The total number of characters that we transmit, including
+ // those from the interpolated variable.
+ int steps;
+
+ // The total number of digits to convert.
+ int digits = -1;
+
+ // So count that now, and build up our table of fixed things to
+ // send.
+ steps = 0;
+ char *p = l->d.fmtdStr.string;
+ while(*p) {
+ if(*p == '\\' && (isdigit(p[1]) || p[1] == '-')) {
+ if(digits >= 0) {
+ Error(_("Multiple escapes (\\0-9) present in format "
+ "string, not allowed."));
+ CompileError();
+ }
+ p++;
+ if(*p == '-') {
+ mustDoMinus = TRUE;
+ outputChars[steps++] = 1;
+ p++;
+ }
+ if(!isdigit(*p) || (*p - '0') > 5 || *p == '0') {
+ Error(_("Bad escape sequence following \\; for a "
+ "literal backslash, use \\\\"));
+ CompileError();
+ }
+ digits = (*p - '0');
+ int i;
+ for(i = 0; i < digits; i++) {
+ outputChars[steps++] = 0;
+ }
+ } else if(*p == '\\') {
+ p++;
+ switch(*p) {
+ case 'r': outputChars[steps++] = '\r'; break;
+ case 'n': outputChars[steps++] = '\n'; break;
+ case 'b': outputChars[steps++] = '\b'; break;
+ case 'f': outputChars[steps++] = '\f'; break;
+ case '\\': outputChars[steps++] = '\\'; break;
+ case 'x': {
+ int h, l;
+ p++;
+ h = HexDigit(*p);
+ if(h >= 0) {
+ p++;
+ l = HexDigit(*p);
+ if(l >= 0) {
+ outputChars[steps++] = (h << 4) | l;
+ break;
+ }
+ }
+ Error(_("Bad escape: correct form is \\xAB."));
+ CompileError();
+ break;
+ }
+ default:
+ Error(_("Bad escape '\\%c'"), *p);
+ CompileError();
+ break;
+ }
+ } else {
+ outputChars[steps++] = *p;
+ }
+ if(*p) p++;
+ }
+
+ if(digits >= 0 && (strlen(var) == 0)) {
+ Error(_("Variable is interpolated into formatted string, but "
+ "none is specified."));
+ CompileError();
+ } else if(digits < 0 && (strlen(var) > 0)) {
+ Error(_("No variable is interpolated into formatted string, "
+ "but a variable name is specified. Include a string like "
+ "'\\-3', or leave variable name blank."));
+ CompileError();
+ }
+
+ // We want to respond to rising edges, so yes we need a one shot.
+ char oneShot[MAX_NAME_LEN];
+ GenSymOneShot(oneShot);
+
+ Op(INT_IF_BIT_SET, stateInOut);
+ Op(INT_IF_BIT_CLEAR, oneShot);
+ Op(INT_SET_VARIABLE_TO_LITERAL, seq, (SWORD)0);
+ Op(INT_END_IF);
+ Op(INT_END_IF);
+ Op(INT_COPY_BIT_TO_BIT, oneShot, stateInOut);
+
+ // Everything that involves seqScratch is a terrible hack to
+ // avoid an if statement with a big body, which is the risk
+ // factor for blowing up on PIC16 page boundaries.
+
+ char *seqScratch = "$scratch3";
+
+ Op(INT_SET_VARIABLE_TO_VARIABLE, seqScratch, seq);
+
+ // No point doing any math unless we'll get to transmit this
+ // cycle, so check that first.
+
+ Op(INT_IF_VARIABLE_LES_LITERAL, seq, steps);
+ Op(INT_ELSE);
+ Op(INT_SET_VARIABLE_TO_LITERAL, seqScratch, -1);
+ Op(INT_END_IF);
+
+ Op(INT_CLEAR_BIT, "$scratch");
+ Op(INT_UART_SEND, "$scratch", "$scratch");
+ Op(INT_IF_BIT_SET, "$scratch");
+ Op(INT_SET_VARIABLE_TO_LITERAL, seqScratch, -1);
+ Op(INT_END_IF);
+
+ // So we transmit this cycle, so check out which character.
+ int i;
+ int digit = 0;
+ for(i = 0; i < steps; i++) {
+ if(outputChars[i] == 0) {
+ // Note gross hack to work around limit of range for
+ // AVR brne op, which is +/- 64 instructions.
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch", i);
+ Op(INT_CLEAR_BIT, "$scratch");
+ Op(INT_IF_VARIABLE_EQUALS_VARIABLE, "$scratch", seqScratch);
+ Op(INT_SET_BIT, "$scratch");
+ Op(INT_END_IF);
+
+ Op(INT_IF_BIT_SET, "$scratch");
+
+ // Start the integer-to-string
+
+ // If there's no minus, then we have to load up
+ // convertState ourselves the first time.
+ if(digit == 0 && !mustDoMinus) {
+ Op(INT_SET_VARIABLE_TO_VARIABLE, convertState, var);
+ }
+ if(digit == 0) {
+ Op(INT_SET_BIT, isLeadingZero);
+ }
+
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch",
+ TenToThe((digits-digit)-1));
+ Op(INT_SET_VARIABLE_DIVIDE, "$scratch2", convertState,
+ "$scratch", 0);
+ Op(INT_SET_VARIABLE_MULTIPLY, "$scratch", "$scratch",
+ "$scratch2", 0);
+ Op(INT_SET_VARIABLE_SUBTRACT, convertState,
+ convertState, "$scratch", 0);
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch", '0');
+ Op(INT_SET_VARIABLE_ADD, "$scratch2", "$scratch2",
+ "$scratch", 0);
+
+ // Suppress all but the last leading zero.
+ if(digit != (digits - 1)) {
+ Op(INT_IF_VARIABLE_EQUALS_VARIABLE, "$scratch",
+ "$scratch2");
+ Op(INT_IF_BIT_SET, isLeadingZero);
+ Op(INT_SET_VARIABLE_TO_LITERAL,
+ "$scratch2", ' ');
+ Op(INT_END_IF);
+ Op(INT_ELSE);
+ Op(INT_CLEAR_BIT, isLeadingZero);
+ Op(INT_END_IF);
+ }
+
+ Op(INT_END_IF);
+
+ digit++;
+ } else if(outputChars[i] == 1) {
+ // do the minus; ugliness to get around the BRNE jump
+ // size limit, though
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch", i);
+ Op(INT_CLEAR_BIT, "$scratch");
+ Op(INT_IF_VARIABLE_EQUALS_VARIABLE, "$scratch", seqScratch);
+ Op(INT_SET_BIT, "$scratch");
+ Op(INT_END_IF);
+ Op(INT_IF_BIT_SET, "$scratch");
+
+ // Also do the `absolute value' calculation while
+ // we're at it.
+ Op(INT_SET_VARIABLE_TO_VARIABLE, convertState, var);
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch2", ' ');
+ Op(INT_IF_VARIABLE_LES_LITERAL, var, (SWORD)0);
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch2", '-');
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch",
+ (SWORD)0);
+ Op(INT_SET_VARIABLE_SUBTRACT, convertState,
+ "$scratch", var, 0);
+ Op(INT_END_IF);
+
+ Op(INT_END_IF);
+ } else {
+ // just another character
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch", i);
+ Op(INT_IF_VARIABLE_EQUALS_VARIABLE, "$scratch", seqScratch);
+ Op(INT_SET_VARIABLE_TO_LITERAL, "$scratch2",
+ outputChars[i]);
+ Op(INT_END_IF);
+ }
+ }
+
+ Op(INT_IF_VARIABLE_LES_LITERAL, seqScratch, (SWORD)0);
+ Op(INT_ELSE);
+ Op(INT_SET_BIT, "$scratch");
+ Op(INT_UART_SEND, "$scratch2", "$scratch");
+ Op(INT_INCREMENT_VARIABLE, seq);
+ Op(INT_END_IF);
+
+ // Rung-out state: true if we're still running, else false
+ Op(INT_CLEAR_BIT, stateInOut);
+ Op(INT_IF_VARIABLE_LES_LITERAL, seq, steps);
+ Op(INT_SET_BIT, stateInOut);
+ Op(INT_END_IF);
+ break;
+ }
+ case ELEM_OPEN:
+ Op(INT_CLEAR_BIT, stateInOut);
+ break;
+
+ case ELEM_SHORT:
+ // goes straight through
+ break;
+
+ case ELEM_PLACEHOLDER:
+ Error(
+ _("Empty row; delete it or add instructions before compiling."));
+ CompileError();
+ break;
+
+ default:
+ oops();
+ break;
+ }
+
+ if(which != ELEM_SERIES_SUBCKT && which != ELEM_PARALLEL_SUBCKT) {
+ // then it is a leaf; let the simulator know which leaf it
+ // should be updating for display purposes
+ SimState(&(l->poweredAfter), stateInOut);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Generate intermediate code for the entire program. Return TRUE if it worked,
+// else FALSE.
+//-----------------------------------------------------------------------------
+BOOL GenerateIntermediateCode(void)
+{
+ GenSymCountParThis = 0;
+ GenSymCountParOut = 0;
+ GenSymCountOneShot = 0;
+ GenSymCountFormattedString = 0;
+
+ // The EEPROM addresses for the `Make Persistent' op are assigned at
+ // int code generation time.
+ EepromAddrFree = 0;
+
+ IntCodeLen = 0;
+ memset(IntCode, 0, sizeof(IntCode));
+
+ if(setjmp(CompileErrorBuf) != 0) {
+ return FALSE;
+ }
+
+ Op(INT_SET_BIT, "$mcr");
+
+ int i;
+ for(i = 0; i < Prog.numRungs; i++) {
+ if(Prog.rungs[i]->count == 1 &&
+ Prog.rungs[i]->contents[0].which == ELEM_COMMENT)
+ {
+ // nothing to do for this one
+ continue;
+ }
+ Comment("");
+ Comment("start rung %d", i+1);
+ Op(INT_COPY_BIT_TO_BIT, "$rung_top", "$mcr");
+ SimState(&(Prog.rungPowered[i]), "$rung_top");
+ IntCodeFromCircuit(ELEM_SERIES_SUBCKT, Prog.rungs[i], "$rung_top");
+ }
+
+ return TRUE;
+}
diff --git a/ldmicro/intcode.h b/ldmicro/intcode.h
new file mode 100644
index 0000000..fa2f4dc
--- /dev/null
+++ b/ldmicro/intcode.h
@@ -0,0 +1,83 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Description of the intermediate code that we generate. The routines in
+// intcode.cpp generate this intermediate code from the program source. Then
+// either we simulate the intermediate code with the routines in simulate.cpp
+// or we convert it to PIC or AVR instructions so that it can run on a
+// real device.
+// Jonathan Westhues, Nov 2004
+//-----------------------------------------------------------------------------
+
+#ifndef __INTCODE_H
+#define __INTCODE_H
+
+#define INT_SET_BIT 1
+#define INT_CLEAR_BIT 2
+#define INT_COPY_BIT_TO_BIT 3
+#define INT_SET_VARIABLE_TO_LITERAL 4
+#define INT_SET_VARIABLE_TO_VARIABLE 5
+#define INT_INCREMENT_VARIABLE 6
+#define INT_SET_VARIABLE_ADD 7
+#define INT_SET_VARIABLE_SUBTRACT 8
+#define INT_SET_VARIABLE_MULTIPLY 9
+#define INT_SET_VARIABLE_DIVIDE 10
+
+#define INT_READ_ADC 11
+#define INT_SET_PWM 12
+#define INT_UART_SEND 13
+#define INT_UART_RECV 14
+#define INT_EEPROM_BUSY_CHECK 15
+#define INT_EEPROM_READ 16
+#define INT_EEPROM_WRITE 17
+
+#define INT_IF_GROUP(x) (((x) >= 50) && ((x) < 60))
+#define INT_IF_BIT_SET 50
+#define INT_IF_BIT_CLEAR 51
+#define INT_IF_VARIABLE_LES_LITERAL 52
+#define INT_IF_VARIABLE_EQUALS_VARIABLE 53
+#define INT_IF_VARIABLE_GRT_VARIABLE 54
+
+#define INT_ELSE 60
+#define INT_END_IF 61
+
+#define INT_SIMULATE_NODE_STATE 80
+
+#define INT_COMMENT 100
+
+// Only used for the interpretable code.
+#define INT_END_OF_PROGRAM 255
+
+#if !defined(INTCODE_H_CONSTANTS_ONLY)
+ typedef struct IntOpTag {
+ int op;
+ char name1[MAX_NAME_LEN];
+ char name2[MAX_NAME_LEN];
+ char name3[MAX_NAME_LEN];
+ SWORD literal;
+ BOOL *poweredAfter;
+ } IntOp;
+
+ #define MAX_INT_OPS (1024*16)
+ extern IntOp IntCode[MAX_INT_OPS];
+ extern int IntCodeLen;
+#endif
+
+
+#endif
diff --git a/ldmicro/interpreted.cpp b/ldmicro/interpreted.cpp
new file mode 100644
index 0000000..ff586c2
--- /dev/null
+++ b/ldmicro/interpreted.cpp
@@ -0,0 +1,248 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// 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
+#include
+#include
+#include
+
+#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);
+}
diff --git a/ldmicro/iolist.cpp b/ldmicro/iolist.cpp
new file mode 100644
index 0000000..d91abc9
--- /dev/null
+++ b/ldmicro/iolist.cpp
@@ -0,0 +1,885 @@
+//-----------------------------------------------------------------------------
+// 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 to maintain the processor I/O list. Whenever the user changes the
+// name of an element, rebuild the I/O list from the PLC program, so that new
+// assigned names are automatically reflected in the I/O list. Also keep a
+// list of old I/Os that have been deleted, so that if the user deletes a
+// a name and then recreates it the associated settings (e.g. pin number)
+// will not be forgotten. Also the dialog box for assigning I/O pins.
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+// I/O that we have seen recently, so that we don't forget pin assignments
+// when we re-extract the list
+#define MAX_IO_SEEN_PREVIOUSLY 512
+static struct {
+ char name[MAX_NAME_LEN];
+ int type;
+ int pin;
+} IoSeenPreviously[MAX_IO_SEEN_PREVIOUSLY];
+static int IoSeenPreviouslyCount;
+
+// stuff for the dialog box that lets you choose pin assignments
+static BOOL DialogDone;
+static BOOL DialogCancel;
+
+static HWND IoDialog;
+
+static HWND PinList;
+static HWND OkButton;
+static HWND CancelButton;
+
+// stuff for the popup that lets you set the simulated value of an analog in
+static HWND AnalogSliderMain;
+static HWND AnalogSliderTrackbar;
+static BOOL AnalogSliderDone;
+static BOOL AnalogSliderCancel;
+
+
+//-----------------------------------------------------------------------------
+// Append an I/O to the I/O list if it is not in there already.
+//-----------------------------------------------------------------------------
+static void AppendIo(char *name, int type)
+{
+ int i;
+ for(i = 0; i < Prog.io.count; i++) {
+ if(strcmp(Prog.io.assignment[i].name, name)==0) {
+ if(type != IO_TYPE_GENERAL && Prog.io.assignment[i].type ==
+ IO_TYPE_GENERAL)
+ {
+ Prog.io.assignment[i].type = type;
+ }
+ // already in there
+ return;
+ }
+ }
+ if(i < MAX_IO) {
+ Prog.io.assignment[i].type = type;
+ Prog.io.assignment[i].pin = NO_PIN_ASSIGNED;
+ strcpy(Prog.io.assignment[i].name, name);
+ (Prog.io.count)++;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Move an I/O pin into the `seen previously' list. This means that if the
+// user creates input Xasd, assigns it a pin, deletes, and then recreates it,
+// then it will come back with the correct pin assigned.
+//-----------------------------------------------------------------------------
+static void AppendIoSeenPreviously(char *name, int type, int pin)
+{
+ if(strcmp(name+1, "new")==0) return;
+
+ int i;
+ for(i = 0; i < IoSeenPreviouslyCount; i++) {
+ if(strcmp(name, IoSeenPreviously[i].name)==0 &&
+ type == IoSeenPreviously[i].type)
+ {
+ if(pin != NO_PIN_ASSIGNED) {
+ IoSeenPreviously[i].pin = pin;
+ }
+ return;
+ }
+ }
+ if(IoSeenPreviouslyCount >= MAX_IO_SEEN_PREVIOUSLY) {
+ // maybe improve later; just throw away all our old information, and
+ // the user might have to reenter the pin if they delete and recreate
+ // things
+ IoSeenPreviouslyCount = 0;
+ }
+
+ i = IoSeenPreviouslyCount;
+ IoSeenPreviously[i].type = type;
+ IoSeenPreviously[i].pin = pin;
+ strcpy(IoSeenPreviously[i].name, name);
+ IoSeenPreviouslyCount++;
+}
+
+//-----------------------------------------------------------------------------
+// Walk a subcircuit, calling ourselves recursively and extracting all the
+// I/O names out of it.
+//-----------------------------------------------------------------------------
+static void ExtractNamesFromCircuit(int which, void *any)
+{
+ ElemLeaf *l = (ElemLeaf *)any;
+
+ switch(which) {
+ case ELEM_PARALLEL_SUBCKT: {
+ ElemSubcktParallel *p = (ElemSubcktParallel *)any;
+ int i;
+ for(i = 0; i < p->count; i++) {
+ ExtractNamesFromCircuit(p->contents[i].which,
+ p->contents[i].d.any);
+ }
+ break;
+ }
+ case ELEM_SERIES_SUBCKT: {
+ ElemSubcktSeries *s = (ElemSubcktSeries *)any;
+ int i;
+ for(i = 0; i < s->count; i++) {
+ ExtractNamesFromCircuit(s->contents[i].which,
+ s->contents[i].d.any);
+ }
+ break;
+ }
+ case ELEM_CONTACTS:
+ switch(l->d.contacts.name[0]) {
+ case 'R':
+ AppendIo(l->d.contacts.name, IO_TYPE_INTERNAL_RELAY);
+ break;
+
+ case 'Y':
+ AppendIo(l->d.contacts.name, IO_TYPE_DIG_OUTPUT);
+ break;
+
+ case 'X':
+ AppendIo(l->d.contacts.name, IO_TYPE_DIG_INPUT);
+ break;
+
+ default:
+ oops();
+ break;
+ }
+ break;
+
+ case ELEM_COIL:
+ AppendIo(l->d.coil.name, l->d.coil.name[0] == 'R' ?
+ IO_TYPE_INTERNAL_RELAY : IO_TYPE_DIG_OUTPUT);
+ break;
+
+ case ELEM_TON:
+ case ELEM_TOF:
+ AppendIo(l->d.timer.name, which == ELEM_TON ? IO_TYPE_TON :
+ IO_TYPE_TOF);
+ break;
+
+ case ELEM_RTO:
+ AppendIo(l->d.timer.name, IO_TYPE_RTO);
+ break;
+
+ case ELEM_MOVE:
+ AppendIo(l->d.move.dest, IO_TYPE_GENERAL);
+ break;
+
+ case ELEM_ADD:
+ case ELEM_SUB:
+ case ELEM_MUL:
+ case ELEM_DIV:
+ AppendIo(l->d.math.dest, IO_TYPE_GENERAL);
+ break;
+
+ case ELEM_FORMATTED_STRING:
+ if(strlen(l->d.fmtdStr.var) > 0) {
+ AppendIo(l->d.fmtdStr.var, IO_TYPE_UART_TX);
+ }
+ break;
+
+ case ELEM_UART_SEND:
+ AppendIo(l->d.uart.name, IO_TYPE_UART_TX);
+ break;
+
+ case ELEM_UART_RECV:
+ AppendIo(l->d.uart.name, IO_TYPE_UART_RX);
+ break;
+
+ case ELEM_SET_PWM:
+ AppendIo(l->d.setPwm.name, IO_TYPE_PWM_OUTPUT);
+ break;
+
+ case ELEM_CTU:
+ case ELEM_CTD:
+ case ELEM_CTC:
+ AppendIo(l->d.counter.name, IO_TYPE_COUNTER);
+ break;
+
+ case ELEM_READ_ADC:
+ AppendIo(l->d.readAdc.name, IO_TYPE_READ_ADC);
+ break;
+
+ case ELEM_SHIFT_REGISTER: {
+ int i;
+ for(i = 0; i < l->d.shiftRegister.stages; i++) {
+ char str[MAX_NAME_LEN+10];
+ sprintf(str, "%s%d", l->d.shiftRegister.name, i);
+ AppendIo(str, IO_TYPE_GENERAL);
+ }
+ break;
+ }
+
+ case ELEM_LOOK_UP_TABLE:
+ AppendIo(l->d.lookUpTable.dest, IO_TYPE_GENERAL);
+ break;
+
+ case ELEM_PIECEWISE_LINEAR:
+ AppendIo(l->d.piecewiseLinear.dest, IO_TYPE_GENERAL);
+ break;
+
+ case ELEM_PLACEHOLDER:
+ case ELEM_COMMENT:
+ case ELEM_SHORT:
+ case ELEM_OPEN:
+ case ELEM_MASTER_RELAY:
+ 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:
+ case ELEM_RES:
+ case ELEM_PERSIST:
+ break;
+
+ default:
+ oops();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Compare function to qsort() the I/O list. Group by type, then
+// alphabetically within each section.
+//-----------------------------------------------------------------------------
+static int CompareIo(const void *av, const void *bv)
+{
+ PlcProgramSingleIo *a = (PlcProgramSingleIo *)av;
+ PlcProgramSingleIo *b = (PlcProgramSingleIo *)bv;
+
+ if(a->type != b->type) {
+ return a->type - b->type;
+ }
+
+ if(a->pin == NO_PIN_ASSIGNED && b->pin != NO_PIN_ASSIGNED) return 1;
+ if(b->pin == NO_PIN_ASSIGNED && a->pin != NO_PIN_ASSIGNED) return -1;
+
+ return strcmp(a->name, b->name);
+}
+
+//-----------------------------------------------------------------------------
+// Wipe the I/O list and then re-extract it from the PLC program, taking
+// care not to forget the pin assignments. Gets passed the selected item
+// as an index into the list; modifies the list, so returns the new selected
+// item as an index into the new list.
+//-----------------------------------------------------------------------------
+int GenerateIoList(int prevSel)
+{
+ int i, j;
+
+ char selName[MAX_NAME_LEN];
+ if(prevSel >= 0) {
+ strcpy(selName, Prog.io.assignment[prevSel].name);
+ }
+
+ if(IoSeenPreviouslyCount > MAX_IO_SEEN_PREVIOUSLY/2) {
+ // flush it so there's lots of room, and we don't run out and
+ // forget important things
+ IoSeenPreviouslyCount = 0;
+ }
+
+ // remember the pin assignments
+ for(i = 0; i < Prog.io.count; i++) {
+ AppendIoSeenPreviously(Prog.io.assignment[i].name,
+ Prog.io.assignment[i].type, Prog.io.assignment[i].pin);
+ }
+ // wipe the list
+ Prog.io.count = 0;
+ // extract the new list so that it must be up to date
+ for(i = 0; i < Prog.numRungs; i++) {
+ ExtractNamesFromCircuit(ELEM_SERIES_SUBCKT, Prog.rungs[i]);
+ }
+
+ for(i = 0; i < Prog.io.count; i++) {
+ if(Prog.io.assignment[i].type == IO_TYPE_DIG_INPUT ||
+ Prog.io.assignment[i].type == IO_TYPE_DIG_OUTPUT ||
+ Prog.io.assignment[i].type == IO_TYPE_READ_ADC)
+ {
+ for(j = 0; j < IoSeenPreviouslyCount; j++) {
+ if(strcmp(Prog.io.assignment[i].name,
+ IoSeenPreviously[j].name)==0)
+ {
+ Prog.io.assignment[i].pin = IoSeenPreviously[j].pin;
+ break;
+ }
+ }
+ }
+ }
+
+ qsort(Prog.io.assignment, Prog.io.count, sizeof(PlcProgramSingleIo),
+ CompareIo);
+
+ if(prevSel >= 0) {
+ for(i = 0; i < Prog.io.count; i++) {
+ if(strcmp(Prog.io.assignment[i].name, selName)==0)
+ break;
+ }
+ if(i < Prog.io.count)
+ return i;
+ }
+ // no previous, or selected was deleted
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Load the I/O list from a file. Since we are just loading pin assignments,
+// put it into IoSeenPreviously so that it will get used on the next
+// extraction.
+//-----------------------------------------------------------------------------
+BOOL LoadIoListFromFile(FILE *f)
+{
+ char line[80];
+ char name[MAX_NAME_LEN];
+ int pin;
+ while(fgets(line, sizeof(line), f)) {
+ if(strcmp(line, "END\n")==0) {
+ return TRUE;
+ }
+ // Don't internationalize this! It's the file format, not UI.
+ if(sscanf(line, " %s at %d", name, &pin)==2) {
+ int type;
+ switch(name[0]) {
+ case 'X': type = IO_TYPE_DIG_INPUT; break;
+ case 'Y': type = IO_TYPE_DIG_OUTPUT; break;
+ case 'A': type = IO_TYPE_READ_ADC; break;
+ default: oops();
+ }
+ AppendIoSeenPreviously(name, type, pin);
+ }
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Write the I/O list to a file. Since everything except the pin assignment
+// can be extracted from the schematic, just write the Xs and Ys.
+//-----------------------------------------------------------------------------
+void SaveIoListToFile(FILE *f)
+{
+ int i;
+ for(i = 0; i < Prog.io.count; i++) {
+ if(Prog.io.assignment[i].type == IO_TYPE_DIG_INPUT ||
+ Prog.io.assignment[i].type == IO_TYPE_DIG_OUTPUT ||
+ Prog.io.assignment[i].type == IO_TYPE_READ_ADC)
+ {
+ // Don't internationalize this! It's the file format, not UI.
+ fprintf(f, " %s at %d\n", Prog.io.assignment[i].name,
+ Prog.io.assignment[i].pin);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Dialog proc for the popup that lets you set the value of an analog input for
+// simulation.
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK AnalogSliderDialogProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_CLOSE:
+ case WM_DESTROY:
+ AnalogSliderDone = TRUE;
+ AnalogSliderCancel = TRUE;
+ return 1;
+
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// A little toolbar-style window that pops up to allow the user to set the
+// simulated value of an ADC pin.
+//-----------------------------------------------------------------------------
+void ShowAnalogSliderPopup(char *name)
+{
+ WNDCLASSEX wc;
+ memset(&wc, 0, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+
+ wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC |
+ CS_DBLCLKS;
+ wc.lpfnWndProc = (WNDPROC)AnalogSliderDialogProc;
+ wc.hInstance = Instance;
+ wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
+ wc.lpszClassName = "LDmicroAnalogSlider";
+ wc.lpszMenuName = NULL;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+
+ RegisterClassEx(&wc);
+
+ POINT pt;
+ GetCursorPos(&pt);
+
+ SWORD currentVal = GetAdcShadow(name);
+
+ SWORD maxVal;
+ if(Prog.mcu) {
+ maxVal = Prog.mcu->adcMax;
+ } else {
+ maxVal = 1023;
+ }
+ if(maxVal == 0) {
+ Error(_("No ADC or ADC not supported for selected micro."));
+ return;
+ }
+
+ int left = pt.x - 10;
+ // try to put the slider directly under the cursor (though later we might
+ // realize that that would put the popup off the screen)
+ int top = pt.y - (15 + (73*currentVal)/maxVal);
+
+ RECT r;
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
+
+ if(top + 110 >= r.bottom) {
+ top = r.bottom - 110;
+ }
+ if(top < 0) top = 0;
+
+ AnalogSliderMain = CreateWindowClient(0, "LDmicroAnalogSlider", "I/O Pin",
+ WS_VISIBLE | WS_POPUP | WS_DLGFRAME,
+ left, top, 30, 100, NULL, NULL, Instance, NULL);
+
+ AnalogSliderTrackbar = CreateWindowEx(0, TRACKBAR_CLASS, "", WS_CHILD |
+ TBS_AUTOTICKS | TBS_VERT | TBS_TOOLTIPS | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 0, 0, 30, 100, AnalogSliderMain, NULL, Instance, NULL);
+ SendMessage(AnalogSliderTrackbar, TBM_SETRANGE, FALSE,
+ MAKELONG(0, maxVal));
+ SendMessage(AnalogSliderTrackbar, TBM_SETTICFREQ, (maxVal + 1)/8, 0);
+ SendMessage(AnalogSliderTrackbar, TBM_SETPOS, TRUE, currentVal);
+
+ EnableWindow(MainWindow, FALSE);
+ ShowWindow(AnalogSliderMain, TRUE);
+ SetFocus(AnalogSliderTrackbar);
+
+ DWORD ret;
+ MSG msg;
+ AnalogSliderDone = FALSE;
+ AnalogSliderCancel = FALSE;
+
+ SWORD orig = GetAdcShadow(name);
+
+ while(!AnalogSliderDone && (ret = GetMessage(&msg, NULL, 0, 0))) {
+ SWORD v = (SWORD)SendMessage(AnalogSliderTrackbar, TBM_GETPOS, 0, 0);
+
+ if(msg.message == WM_KEYDOWN) {
+ if(msg.wParam == VK_RETURN) {
+ AnalogSliderDone = TRUE;
+ break;
+ } else if(msg.wParam == VK_ESCAPE) {
+ AnalogSliderDone = TRUE;
+ AnalogSliderCancel = TRUE;
+ break;
+ }
+ } else if(msg.message == WM_LBUTTONUP) {
+ if(v != orig) {
+ AnalogSliderDone = TRUE;
+ }
+ }
+ SetAdcShadow(name, v);
+
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ if(!AnalogSliderCancel) {
+ SWORD v = (SWORD)SendMessage(AnalogSliderTrackbar, TBM_GETPOS, 0, 0);
+ SetAdcShadow(name, v);
+ }
+
+ EnableWindow(MainWindow, TRUE);
+ DestroyWindow(AnalogSliderMain);
+ ListView_RedrawItems(IoList, 0, Prog.io.count - 1);
+}
+
+//-----------------------------------------------------------------------------
+// Window proc for the contacts dialog box
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK IoDialogProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (msg) {
+ case WM_COMMAND: {
+ HWND h = (HWND)lParam;
+ if(h == OkButton && wParam == BN_CLICKED) {
+ DialogDone = TRUE;
+ } else if(h == CancelButton && wParam == BN_CLICKED) {
+ DialogDone = TRUE;
+ DialogCancel = TRUE;
+ } else if(h == PinList && HIWORD(wParam) == LBN_DBLCLK) {
+ DialogDone = TRUE;
+ }
+ break;
+ }
+
+ case WM_CLOSE:
+ case WM_DESTROY:
+ DialogDone = TRUE;
+ DialogCancel = TRUE;
+ return 1;
+
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Create our window class; nothing exciting.
+//-----------------------------------------------------------------------------
+static BOOL MakeWindowClass()
+{
+ WNDCLASSEX wc;
+ memset(&wc, 0, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+
+ wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC |
+ CS_DBLCLKS;
+ wc.lpfnWndProc = (WNDPROC)IoDialogProc;
+ wc.hInstance = Instance;
+ wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
+ wc.lpszClassName = "LDmicroIo";
+ wc.lpszMenuName = NULL;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hIcon = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
+ IMAGE_ICON, 32, 32, 0);
+ wc.hIconSm = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
+ IMAGE_ICON, 16, 16, 0);
+
+ return RegisterClassEx(&wc);
+}
+
+static void MakeControls(void)
+{
+ HWND textLabel = CreateWindowEx(0, WC_STATIC, _("Assign:"),
+ WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 6, 1, 80, 17, IoDialog, NULL, Instance, NULL);
+ NiceFont(textLabel);
+
+ PinList = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTBOX, "",
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | WS_VSCROLL |
+ LBS_NOTIFY, 6, 18, 95, 320, IoDialog, NULL, Instance, NULL);
+ FixedFont(PinList);
+
+ OkButton = CreateWindowEx(0, WC_BUTTON, _("OK"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_DEFPUSHBUTTON,
+ 6, 325, 95, 23, IoDialog, NULL, Instance, NULL);
+ NiceFont(OkButton);
+
+ CancelButton = CreateWindowEx(0, WC_BUTTON, _("Cancel"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 6, 356, 95, 23, IoDialog, NULL, Instance, NULL);
+ NiceFont(CancelButton);
+}
+
+void ShowIoDialog(int item)
+{
+ if(!Prog.mcu) {
+ MessageBox(MainWindow,
+ _("No microcontroller has been selected. You must select a "
+ "microcontroller before you can assign I/O pins.\r\n\r\n"
+ "Select a microcontroller under the Settings menu and try "
+ "again."), _("I/O Pin Assignment"), MB_OK | MB_ICONWARNING);
+ return;
+ }
+
+ if(Prog.mcu->whichIsa == ISA_ANSIC) {
+ Error(_("Can't specify I/O assignment for ANSI C target; compile and "
+ "see comments in generated source code."));
+ return;
+ }
+
+ if(Prog.mcu->whichIsa == ISA_INTERPRETED) {
+ Error(_("Can't specify I/O assignment for interpretable target; see "
+ "comments in reference implementation of interpreter."));
+ return;
+ }
+
+ if(Prog.io.assignment[item].name[0] != 'X' &&
+ Prog.io.assignment[item].name[0] != 'Y' &&
+ Prog.io.assignment[item].name[0] != 'A')
+ {
+ Error(_("Can only assign pin number to input/output pins (Xname or "
+ "Yname or Aname)."));
+ return;
+ }
+
+ if(Prog.io.assignment[item].name[0] == 'A' && Prog.mcu->adcCount == 0) {
+ Error(_("No ADC or ADC not supported for this micro."));
+ return;
+ }
+
+ if(strcmp(Prog.io.assignment[item].name+1, "new")==0) {
+ Error(_("Rename I/O from default name ('%s') before assigning "
+ "MCU pin."), Prog.io.assignment[item].name);
+ return;
+ }
+
+ MakeWindowClass();
+
+ // We need the TOOLWINDOW style, or else the window will be forced to
+ // a minimum width greater than our current width. And without the
+ // APPWINDOW style, it becomes impossible to get the window back (by
+ // Alt+Tab or taskbar).
+ IoDialog = CreateWindowClient(WS_EX_TOOLWINDOW | WS_EX_APPWINDOW,
+ "LDmicroIo", _("I/O Pin"),
+ WS_OVERLAPPED | WS_SYSMENU,
+ 100, 100, 107, 387, NULL, NULL, Instance, NULL);
+
+ MakeControls();
+
+ SendMessage(PinList, LB_ADDSTRING, 0, (LPARAM)_("(no pin)"));
+ int i;
+ for(i = 0; i < Prog.mcu->pinCount; i++) {
+ int j;
+ for(j = 0; j < Prog.io.count; j++) {
+ if(j == item) continue;
+ if(Prog.io.assignment[j].pin == Prog.mcu->pinInfo[i].pin) {
+ goto cant_use_this_io;
+ }
+ }
+
+ if(UartFunctionUsed() && Prog.mcu &&
+ ((Prog.mcu->pinInfo[i].pin == Prog.mcu->uartNeeds.rxPin) ||
+ (Prog.mcu->pinInfo[i].pin == Prog.mcu->uartNeeds.txPin)))
+ {
+ goto cant_use_this_io;
+ }
+
+ if(PwmFunctionUsed() &&
+ Prog.mcu->pinInfo[i].pin == Prog.mcu->pwmNeedsPin)
+ {
+ goto cant_use_this_io;
+ }
+
+ if(Prog.io.assignment[item].name[0] == 'A') {
+ for(j = 0; j < Prog.mcu->adcCount; j++) {
+ if(Prog.mcu->adcInfo[j].pin == Prog.mcu->pinInfo[i].pin) {
+ // okay; we know how to connect it up to the ADC
+ break;
+ }
+ }
+ if(j == Prog.mcu->adcCount) {
+ goto cant_use_this_io;
+ }
+ }
+
+ char buf[40];
+ if(Prog.mcu->pinCount <= 21) {
+ sprintf(buf, "%3d %c%c%d", Prog.mcu->pinInfo[i].pin,
+ Prog.mcu->portPrefix, Prog.mcu->pinInfo[i].port,
+ Prog.mcu->pinInfo[i].bit);
+ } else {
+ sprintf(buf, "%3d %c%c%d", Prog.mcu->pinInfo[i].pin,
+ Prog.mcu->portPrefix, Prog.mcu->pinInfo[i].port,
+ Prog.mcu->pinInfo[i].bit);
+ }
+ SendMessage(PinList, LB_ADDSTRING, 0, (LPARAM)buf);
+cant_use_this_io:;
+ }
+
+ EnableWindow(MainWindow, FALSE);
+ ShowWindow(IoDialog, TRUE);
+ SetFocus(PinList);
+
+ MSG msg;
+ DWORD ret;
+ DialogDone = FALSE;
+ DialogCancel = FALSE;
+ while((ret = GetMessage(&msg, NULL, 0, 0)) && !DialogDone) {
+ if(msg.message == WM_KEYDOWN) {
+ if(msg.wParam == VK_RETURN) {
+ DialogDone = TRUE;
+ break;
+ } else if(msg.wParam == VK_ESCAPE) {
+ DialogDone = TRUE;
+ DialogCancel = TRUE;
+ break;
+ }
+ }
+
+ if(IsDialogMessage(IoDialog, &msg)) continue;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ if(!DialogCancel) {
+ int sel = SendMessage(PinList, LB_GETCURSEL, 0, 0);
+ char pin[16];
+ SendMessage(PinList, LB_GETTEXT, (WPARAM)sel, (LPARAM)pin);
+ if(strcmp(pin, _("(no pin)"))==0) {
+ int i;
+ for(i = 0; i < IoSeenPreviouslyCount; i++) {
+ if(strcmp(IoSeenPreviously[i].name,
+ Prog.io.assignment[item].name)==0)
+ {
+ IoSeenPreviously[i].pin = NO_PIN_ASSIGNED;
+ }
+ }
+ Prog.io.assignment[item].pin = NO_PIN_ASSIGNED;
+ } else {
+ Prog.io.assignment[item].pin = atoi(pin);
+ // Only one name can be bound to each pin; make sure that there's
+ // not another entry for this pin in the IoSeenPreviously list,
+ // that might get used if the user creates a new pin with that
+ // name.
+ int i;
+ for(i = 0; i < IoSeenPreviouslyCount; i++) {
+ if(IoSeenPreviously[i].pin == atoi(pin)) {
+ IoSeenPreviously[i].pin = NO_PIN_ASSIGNED;
+ }
+ }
+ }
+ }
+
+ EnableWindow(MainWindow, TRUE);
+ DestroyWindow(IoDialog);
+ return;
+}
+
+//-----------------------------------------------------------------------------
+// Called in response to a notify for the listview. Handles click, text-edit
+// operations etc., but also gets called to find out what text to display
+// where (LPSTR_TEXTCALLBACK); that way we don't have two parallel copies of
+// the I/O list to keep in sync.
+//-----------------------------------------------------------------------------
+void IoListProc(NMHDR *h)
+{
+ switch(h->code) {
+ case LVN_GETDISPINFO: {
+ NMLVDISPINFO *i = (NMLVDISPINFO *)h;
+ int item = i->item.iItem;
+ switch(i->item.iSubItem) {
+ case LV_IO_PIN:
+ // Don't confuse people by displaying bogus pin assignments
+ // for the C target.
+ if(Prog.mcu && (Prog.mcu->whichIsa == ISA_ANSIC ||
+ Prog.mcu->whichIsa == ISA_INTERPRETED) )
+ {
+ strcpy(i->item.pszText, "");
+ break;
+ }
+
+ PinNumberForIo(i->item.pszText,
+ &(Prog.io.assignment[item]));
+ break;
+
+ case LV_IO_TYPE: {
+ char *s = IoTypeToString(Prog.io.assignment[item].type);
+ strcpy(i->item.pszText, s);
+ break;
+ }
+ case LV_IO_NAME:
+ strcpy(i->item.pszText, Prog.io.assignment[item].name);
+ break;
+
+ case LV_IO_PORT: {
+ // Don't confuse people by displaying bogus pin assignments
+ // for the C target.
+ if(Prog.mcu && Prog.mcu->whichIsa == ISA_ANSIC) {
+ strcpy(i->item.pszText, "");
+ break;
+ }
+
+ int type = Prog.io.assignment[item].type;
+ if(type != IO_TYPE_DIG_INPUT && type != IO_TYPE_DIG_OUTPUT
+ && type != IO_TYPE_READ_ADC)
+ {
+ strcpy(i->item.pszText, "");
+ break;
+ }
+
+ int pin = Prog.io.assignment[item].pin;
+ if(pin == NO_PIN_ASSIGNED || !Prog.mcu) {
+ strcpy(i->item.pszText, "");
+ break;
+ }
+
+ if(UartFunctionUsed() && Prog.mcu) {
+ if((Prog.mcu->uartNeeds.rxPin == pin) ||
+ (Prog.mcu->uartNeeds.txPin == pin))
+ {
+ strcpy(i->item.pszText, _(""));
+ break;
+ }
+ }
+
+ if(PwmFunctionUsed() && Prog.mcu) {
+ if(Prog.mcu->pwmNeedsPin == pin) {
+ strcpy(i->item.pszText, _(""));
+ break;
+ }
+ }
+
+ int j;
+ for(j = 0; j < Prog.mcu->pinCount; j++) {
+ if(Prog.mcu->pinInfo[j].pin == pin) {
+ sprintf(i->item.pszText, "%c%c%d",
+ Prog.mcu->portPrefix,
+ Prog.mcu->pinInfo[j].port,
+ Prog.mcu->pinInfo[j].bit);
+ break;
+ }
+ }
+ if(j == Prog.mcu->pinCount) {
+ sprintf(i->item.pszText, _(""));
+ }
+ break;
+ }
+
+ case LV_IO_STATE: {
+ if(InSimulationMode) {
+ char *name = Prog.io.assignment[item].name;
+ DescribeForIoList(name, i->item.pszText);
+ } else {
+ strcpy(i->item.pszText, "");
+ }
+ break;
+ }
+
+ }
+ break;
+ }
+ case LVN_ITEMACTIVATE: {
+ NMITEMACTIVATE *i = (NMITEMACTIVATE *)h;
+ if(InSimulationMode) {
+ char *name = Prog.io.assignment[i->iItem].name;
+ if(name[0] == 'X') {
+ SimulationToggleContact(name);
+ } else if(name[0] == 'A') {
+ ShowAnalogSliderPopup(name);
+ }
+ } else {
+ UndoRemember();
+ ShowIoDialog(i->iItem);
+ ProgramChanged();
+ InvalidateRect(MainWindow, NULL, FALSE);
+ }
+ break;
+ }
+ }
+}
diff --git a/ldmicro/lang-de.txt b/ldmicro/lang-de.txt
new file mode 100644
index 0000000..8125281
--- /dev/null
+++ b/ldmicro/lang-de.txt
@@ -0,0 +1,762 @@
+"Target frequency %d Hz, closest achievable is %d Hz (warning, >5%% error)."
+"Zielfrequenz %d Hz, nächste erreichbare ist %d Hz (Warnung, >5% Abweichung)."
+
+"Compile successful; wrote IHEX for AVR to '%s'.\r\n\r\nRemember to set the processor configuration (fuses) correctly. This does not happen automatically."
+"Kompilierung war erfolgreich. IHEX für AVR gespeichert unter '%s'.\r\n\r\n Die Prozessor-Konfigurationsbits müssen richtig gesetzt werden. Dies geschieht nicht automatisch"
+
+"( ) Normal"
+"( ) Normal"
+
+"(/) Negated"
+"(/) Negiert"
+
+"(S) Set-Only"
+"(S) Setzen"
+
+"(R) Reset-Only"
+"(R) Rücksetzen"
+
+"Pin on MCU"
+"Prozessorpin"
+
+"Coil"
+"Spule"
+
+"Comment"
+"Kommentar"
+
+"Cycle Time (ms):"
+"Zykluszeit (ms):"
+
+"Crystal Frequency (MHz):"
+"Quarzfrequenz (MHz):"
+
+"UART Baud Rate (bps):"
+"UART Baudrate (bps):"
+
+"Serial (UART) will use pins %d and %d.\r\n\r\n"
+"Serielles (UART) verwendet die Pins %d und %d.\r\n\r\n"
+
+"Please select a micro with a UART.\r\n\r\n"
+"Einen Prozessor mit UART wählen.\r\n\r\n"
+
+"No serial instructions (UART Send/UART Receive) are in use; add one to program before setting baud rate.\r\n\r\n"
+"Keine UART-Anweisung Senden/Empfangen gefunden; die Baudrate festlegen, wenn diese Anweisung verwendet wird.\r\n\r\n"
+
+"The cycle time for the 'PLC' runtime generated by LDmicro is user-configurable. Very short cycle times may not be achievable due to processor speed constraints, and very long cycle times may not be achievable due to hardware overflows. Cycle times between 10 ms and 100 ms will usually be practical.\r\n\r\nThe compiler must know what speed crystal you are using with the micro to convert between timing in clock cycles and timing in seconds. A 4 MHz to 20 MHz crystal is typical; check the speed grade of the part you are using to determine the maximum allowable clock speed before choosing a crystal."
+"Die von LDmicro erzeugte Zykluszeit des SPS-Ablaufs ist vom Anwender konfigurierbar. Sehr kurze Zykluszeiten können wegen Prozessorbeschränkungen nicht erreichbar sein, und sehr lange Zykluszeiten können wegen Hardware-Überlaufs nicht erreichbar sein. Zykluszeiten zwischen 10 ms und 100 ms sind üblich.\r\n\r\nFür die Umrechnung des Taktzykluses und der Zeitberechnung in Sekunden, muss der Compiler wissen, welche Quarzfrequenz beim Prozessor verwendet wird. Ein Quarz mit 4 bis 20 MHz ist üblich; Überprüfen Sie die Geschwindigkeit Ihres Prozessors, um die maximal erlaubte Taktgeschwindigkeit zu bestimmen, bevor Sie einen Quarz wählen."
+
+"PLC Configuration"
+"SPS-Konfiguration"
+
+"Zero cycle time not valid; resetting to 10 ms."
+"Zykluszeit = 0, nicht zulässig; wird auf 10 ms gesetzt."
+
+"Source"
+"Quelle"
+
+"Internal Relay"
+"Merker"
+
+"Input pin"
+"Eingangspin"
+
+"Output pin"
+"Ausgangspin"
+
+"|/| Negated"
+"|/| Negiert"
+
+"Contacts"
+"Kontakte"
+
+"No ADC or ADC not supported for selected micro."
+"Kein A/D-Wandler vorhanden oder A/D-Wandler wird vom gewählten Prozessor nicht unterstützt."
+
+"Assign:"
+"Zuweisen:"
+
+"No microcontroller has been selected. You must select a microcontroller before you can assign I/O pins.\r\n\r\nSelect a microcontroller under the Settings menu and try again."
+"Kein Prozessor gewählt. Sie müssen einen Prozessor wählen, bevor Sie E/A Pins zuweisen können.\r\n\r\nWählen Sie einen Prozessor im Voreinstellungs-Menu und versuchen es noch mal."
+
+"I/O Pin Assignment"
+"E/A Pin Zuweisung"
+
+"Can't specify I/O assignment for ANSI C target; compile and see comments in generated source code."
+"Keine E/A Zuweisung für ANSI C-Ziel möglich; kompilieren Sie und beachten die Kommentare im erzeugten Quellcode."
+
+"Can't specify I/O assignment for interpretable target; see comments in reference implementation of interpreter."
+"Keine E/A Zuweisung für ANSI C-Ziel möglich; beachten Sie die Kommentare der Referenz-Ausführung des Interpreters."
+
+"Can only assign pin number to input/output pins (Xname or Yname or Aname)."
+"Nur für Ein- und Ausgangspins können Pin-Nummern vergeben werden (XName oder YName oder AName)."
+
+"No ADC or ADC not supported for this micro."
+"Kein A/D-Wandler vorhanden oder A/D-Wandler wird vom gewählten Prozessor nicht unterstützt."
+
+"Rename I/O from default name ('%s') before assigning MCU pin."
+"Die Standardbezeichnung ('%s') des E/A’s vor der Zuweisung des Prozessorpins ändern."
+
+"I/O Pin"
+"E/A Pin"
+
+"(no pin)"
+"(kein Pin)"
+
+""
+""
+
+""
+""
+
+""
+""
+
+"Export As Text"
+"Als Text exportieren"
+
+"Couldn't write to '%s'."
+"Speichern nicht möglich unter '%s'."
+
+"Compile To"
+"Kompilieren unter"
+
+"Must choose a target microcontroller before compiling."
+"Vor dem Kompilieren muss ein Prozessor gewählt werden."
+
+"UART function used but not supported for this micro."
+"Dieser Prozessor unterstützt keine UART-Funktion."
+
+"PWM function used but not supported for this micro."
+"Dieser Prozessor unterstützt keine PWM-Funktion."
+
+"The program has changed since it was last saved.\r\n\r\nDo you want to save the changes?"
+"Das Programm wurde nach dem letzten Speichern geändert.\r\n\r\n Möchten Sie die Änderungen speichern?"
+
+"--add comment here--"
+"--Hier Komentar einfügen--"
+
+"Start new program?"
+"Neues Programm starten?"
+
+"Couldn't open '%s'."
+"Kann nicht geöffnet werden '%s'."
+
+"Name"
+"Name"
+
+"State"
+"Status"
+
+"Pin on Processor"
+"Prozessorpin"
+
+"MCU Port"
+"Prozessor-Port"
+
+"LDmicro - Simulation (Running)"
+"LDmicro - Simulation (am Laufen)"
+
+"LDmicro - Simulation (Stopped)"
+"LDmicro - Simulation (Angehalten)"
+
+"LDmicro - Program Editor"
+"LDmicro – Programm-Editor"
+
+" - (not yet saved)"
+" - (noch nicht gespeichert)"
+
+"&New\tCtrl+N"
+"&Neu\tStrg+N"
+
+"&Open...\tCtrl+O"
+"&Öffnen...\tStrg+O"
+
+"&Save\tCtrl+S"
+"&Speichern\tStrg+S"
+
+"Save &As..."
+"Speichern &unter..."
+
+"&Export As Text...\tCtrl+E"
+"&Als Text exportieren...\tStrg+E"
+
+"E&xit"
+"&Beenden"
+
+"&Undo\tCtrl+Z"
+"&Aufheben\tStrg+Z"
+
+"&Redo\tCtrl+Y"
+"&Wiederherstellen\tStrg+Y"
+
+"Insert Rung &Before\tShift+6"
+"Netzwerk Einfügen &Davor\tShift+6"
+
+"Insert Rung &After\tShift+V"
+"Netzwerk Einfügen &Danach\tShift+V"
+
+"Move Selected Rung &Up\tShift+Up"
+"Gewähltes Netzwerk schieben &nach oben\tShift+Up"
+
+"Move Selected Rung &Down\tShift+Down"
+"Gewähltes Netzwerk schieben &nach unten\tShift+Down"
+
+"&Delete Selected Element\tDel"
+"&Gewähltes Element löschen\tEntf"
+
+"D&elete Rung\tShift+Del"
+"Netzwerk löschen\tShift+Entf"
+
+"Insert Co&mment\t;"
+"Kommentar &einfügen\t;"
+
+"Insert &Contacts\tC"
+"Kontakt &einfügen\tC"
+
+"Insert OSR (One Shot Rising)\t&/"
+"OSR einfügen (Steigende Flanke)\t&/"
+
+"Insert OSF (One Shot Falling)\t&\\"
+"OSF einfügen (Fallende Flanke)\t&\\"
+
+"Insert T&ON (Delayed Turn On)\tO"
+"T&ON einfügen (Anzugsverzögerung)\tO"
+
+"Insert TO&F (Delayed Turn Off)\tF"
+"TO&F einfügen (Abfallverzögerung)\tF"
+
+"Insert R&TO (Retentive Delayed Turn On)\tT"
+"R&TO einfügen (Speichernde Anzugsverzögerung)\tT"
+
+"Insert CT&U (Count Up)\tU"
+"CT&U einfügen (Aufwärtszähler)\tU"
+
+"Insert CT&D (Count Down)\tI"
+"CT&D einfügen (Abwärtszähler)\tI"
+
+"Insert CT&C (Count Circular)\tJ"
+"CT&C einfügen (Zirkulierender Zähler)\tJ"
+
+"Insert EQU (Compare for Equals)\t="
+"EQU einfügen (Vergleich auf gleich)\t="
+
+"Insert NEQ (Compare for Not Equals)"
+"NEQ einfügen (Vergleich auf ungleich)"
+
+"Insert GRT (Compare for Greater Than)\t>"
+"GRT einfügen (Vergleich auf größer)\t>"
+
+"Insert GEQ (Compare for Greater Than or Equal)\t."
+"GEQ einfügen (Vergleich auf größer oder gleich)\t."
+
+"Insert LES (Compare for Less Than)\t<"
+"LES einfügen (Vergleich auf kleiner)\t<"
+
+"Insert LEQ (Compare for Less Than or Equal)\t,"
+"LEQ einfügen (Vergleich auf kleiner oder gleich)\t,"
+
+"Insert Open-Circuit"
+"Öffnung einfügen"
+
+"Insert Short-Circuit"
+"Brücke einfügen"
+
+"Insert Master Control Relay"
+"Master Control Relais einfügen"
+
+"Insert Coi&l\tL"
+"Spule einfügen \tL"
+
+"Insert R&ES (Counter/RTO Reset)\tE"
+"R&ES einfügen (RTO/Zähler rücksetzen)\tE"
+
+"Insert MOV (Move)\tM"
+"Transferieren (Move) einfügen\tM"
+
+"Insert ADD (16-bit Integer Add)\t+"
+"ADD einfügen (16-bit Ganzzahl Addierer)\t+"
+
+"Insert SUB (16-bit Integer Subtract)\t-"
+"SUB einfügen (16-bit Ganzzahl Subtrahierer)\t-"
+
+"Insert MUL (16-bit Integer Multiply)\t*"
+"MUL einfügen (16-bit Ganzzahl Multiplizierer)\t*"
+
+"Insert DIV (16-bit Integer Divide)\tD"
+"DIV einfügen (16-bit Ganzzahl Dividierer)\tD"
+
+"Insert Shift Register"
+"Schieberegister einfügen"
+
+"Insert Look-Up Table"
+"Nachschlag-Tabelle einfügen"
+
+"Insert Piecewise Linear"
+"Näherungs-Linear-Tabelle einfügen"
+
+"Insert Formatted String Over UART"
+"Formatierte Zeichenfolge über UART einfügen"
+
+"Insert &UART Send"
+"&UART Senden einfügen"
+
+"Insert &UART Receive"
+"&UART Empfangen einfügen"
+
+"Insert Set PWM Output"
+"PWM Ausgang einfügen"
+
+"Insert A/D Converter Read\tP"
+"A/D-Wandler Einlesen einfügen\tP"
+
+"Insert Make Persistent"
+"Remanent machen einfügen"
+
+"Make Norm&al\tA"
+"Auf Normal ändern\tA"
+
+"Make &Negated\tN"
+"Auf &Negieren ändern\tN"
+
+"Make &Set-Only\tS"
+"Auf &Setzen ändern\tS"
+
+"Make &Reset-Only\tR"
+"Auf &Rücksetzen ändern\tR"
+
+"&MCU Parameters..."
+"&Prozessor-Parameter..."
+
+"(no microcontroller)"
+"(kein Prozessor)"
+
+"&Microcontroller"
+"&Mikroprozessor"
+
+"Si&mulation Mode\tCtrl+M"
+"Simulationsbetrieb\tStrg+M"
+
+"Start &Real-Time Simulation\tCtrl+R"
+"Start &Echtzeit- Simulation\tStrg+R"
+
+"&Halt Simulation\tCtrl+H"
+"&Simulation Anhalten\tStrg+H"
+
+"Single &Cycle\tSpace"
+"&Einzelzyklus\tLeertaste"
+
+"&Compile\tF5"
+"&Kompilieren\tF5"
+
+"Compile &As..."
+"Kompilieren &unter..."
+
+"&Manual...\tF1"
+"&Handbuch...\tF1"
+
+"&About..."
+"&Über LDmicro..."
+
+"&File"
+"&Datei"
+
+"&Edit"
+"&Bearbeiten"
+
+"&Settings"
+"&Voreinstellungen"
+
+"&Instruction"
+"&Anweisung"
+
+"Si&mulate"
+"Simulieren"
+
+"&Compile"
+"&Kompilieren"
+
+"&Help"
+"&Hilfe"
+
+"no MCU selected"
+"kein Prozessor gewählt"
+
+"cycle time %.2f ms"
+"Zykluszeit %.2f ms"
+
+"processor clock %.4f MHz"
+"Taktfrequenz Prozessor %.4f MHz"
+
+"Internal error relating to PIC paging; make program smaller or reshuffle it."
+"Interner Fehler beim PIC paging Seitenwechsel; Programm verkleinern oder umbilden"
+
+"PWM frequency too fast."
+"PWM Frequenz zu schnell."
+
+"PWM frequency too slow."
+"PWM Frequenz zu langsam."
+
+"Cycle time too fast; increase cycle time, or use faster crystal."
+"Zykluszeit zu schnell; Zykluszeit vergrößern oder schnelleren Quarz wählen."
+
+"Cycle time too slow; decrease cycle time, or use slower crystal."
+"Zykluszeit zu langsam; Zykluszeit verringern oder langsameren Quarz wählen."
+
+"Couldn't open file '%s'"
+"Datei konnte nicht geöffnet werden '%s'"
+
+"Zero baud rate not possible."
+"Baudrate = 0 nicht möglich"
+
+"Compile successful; wrote IHEX for PIC16 to '%s'.\r\n\r\nConfiguration word (fuses) has been set for crystal oscillator, BOD enabled, LVP disabled, PWRT enabled, all code protection off.\r\n\r\nUsed %d/%d words of program flash (chip %d%% full)."
+"Kompilierung war erfolgreich; IHEX für PIC16 gespeichert unter '%s'.\r\n\r\nKonfigurations-Wort (fuse) für Quarz-Oszillator festgelegt, BOD aktiviert, LVP gesperrt, PWRT aktiviert, Verschlüsselungsschutz aus.\r\n\r\nVerwendete %d/%d Worte des Flash-Speichers (Chip %d%% voll)."
+
+"Type"
+"Typ"
+
+"Timer"
+"Timer"
+
+"Counter"
+"Zähler"
+
+"Reset"
+"Rücksetzen"
+
+"OK"
+"OK"
+
+"Cancel"
+"Abbrechen"
+
+"Empty textbox; not permitted."
+"Leere Testbox; nicht erlaubt"
+
+"Bad use of quotes: <%s>"
+"Quoten falsch verwendet: <%s>"
+
+"Turn-On Delay"
+"Anzugsverzögerung"
+
+"Turn-Off Delay"
+"Abfallverzögerung"
+
+"Retentive Turn-On Delay"
+"Speichernde Anzugsverzögerung"
+
+"Delay (ms):"
+"Verzögerung (ms):"
+
+"Delay too long; maximum is 2**31 us."
+"Verzögerung zu lang; maximal 2**31 us."
+
+"Delay cannot be zero or negative."
+"Verzögerung kann nicht gleich Null oder negativ sein."
+
+"Count Up"
+"Aufwärtszählen"
+
+"Count Down"
+"Abwärtszählen"
+
+"Circular Counter"
+"Zirkulierender Zähler"
+
+"Max value:"
+"Maximalwert:"
+
+"True if >= :"
+"Wahr wenn >= :"
+
+"If Equals"
+"Wenn gleich"
+
+"If Not Equals"
+"Wenn ungleich"
+
+"If Greater Than"
+"Wenn größer als"
+
+"If Greater Than or Equal To"
+"Wenn größer als oder gleich"
+
+"If Less Than"
+"Wenn kleiner als"
+
+"If Less Than or Equal To"
+"Wenn kleiner als oder gleich"
+
+"'Closed' if:"
+"'Geschlossen' wenn:"
+
+"Move"
+"Transferieren"
+
+"Read A/D Converter"
+"A/D-Wandler einlesen"
+
+"Duty cycle var:"
+"Einsatzzyklus Var:"
+
+"Frequency (Hz):"
+"Frequenz (Hz):"
+
+"Set PWM Duty Cycle"
+"PWM Einsatzzyklus eingeben"
+
+"Source:"
+"Quelle:"
+
+"Receive from UART"
+"Mit UART empfangen"
+
+"Send to UART"
+"Mit UART senden"
+
+"Add"
+"Addieren"
+
+"Subtract"
+"Subtrahieren"
+
+"Multiply"
+"Multiplizieren"
+
+"Divide"
+"Dividieren"
+
+"Destination:"
+"Ziel:"
+
+"is set := :"
+"gesetzt auf := :"
+
+"Name:"
+"Name:"
+
+"Stages:"
+"Stufen:"
+
+"Shift Register"
+"Schieberegister"
+
+"Not a reasonable size for a shift register."
+"Kein angemessenes Format für ein Schieberegister."
+
+"String:"
+"Zeichensatz:"
+
+"Formatted String Over UART"
+"Formatierter Zeichensatz über UART"
+
+"Variable:"
+"Variable:"
+
+"Make Persistent"
+"Remanent machen"
+
+"Too many elements in subcircuit!"
+"Zu viele Elemente im Netzwerk!"
+
+"Too many rungs!"
+"Zu viele Netzwerke!"
+
+"Error"
+"Fehler"
+
+
+"ANSI C target does not support peripherals (UART, PWM, ADC, EEPROM). Skipping that instruction."
+"ANSI C Zieldatei unterstützt keine Peripherien (wie UART, ADC, EEPROM). Die Anweisung wird ausgelassen."
+
+"Compile successful; wrote C source code to '%s'.\r\n\r\nThis is not a complete C program. You have to provide the runtime and all the I/O routines. See the comments in the source code for information about how to do this."
+"Die Kompilierung war erfolgreich; der C-Quellcode wurde gespeichert unter '%s'.\r\n\r\nDies ist kein komplettes C-Programm. Sie müssen die Laufzeit und alle E/A Routinen vorgeben. Siehe die Kommentare im Quellcode für Informationen, wie man das macht."
+
+"Cannot delete rung; program must have at least one rung."
+"Das Netzwerk nicht löschbar, das Programm muss mindestens ein Netzwerk haben."
+
+"Out of memory; simplify program or choose microcontroller with more memory."
+"Speicher voll; vereinfachen Sie das Programm oder wählen Sie einen Prozessor mit mehr Speicherkapazität."
+
+"Must assign pins for all ADC inputs (name '%s')."
+"Für alle ADC-Eingänge müssen Pins zugewiesen werden (Name '%s')."
+
+"Internal limit exceeded (number of vars)"
+"Interne Begrenzung überschritten (Anzahl der Variablen)"
+
+"Internal relay '%s' never assigned; add its coil somewhere."
+"Keine Zuweisung für Merker '%s', vergeben Sie eine Spule im Programm."
+
+"Must assign pins for all I/O.\r\n\r\n'%s' is not assigned."
+"Für alle E/A's müssen Pins zugewiesen werden.\r\n\r\n'%s' ist nicht zugewiesen."
+
+"UART in use; pins %d and %d reserved for that."
+"UART in Verwendung; Pins %d und %d sind hierfür reserviert."
+
+"PWM in use; pin %d reserved for that."
+"PWM in Verwendung; Pin %d hierfür reserviert."
+
+"UART baud rate generator: divisor=%d actual=%.4f for %.2f%% error.\r\n\r\nThis 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\nCode will be generated anyways but serial may be unreliable or completely broken."
+"UART Baudraten-Generator: Divisor=%d aktuell=%.4f für %.2f%% Fehler.\r\n\r\nDiese ist zu hoch; versuchen Sie es mit einer anderen Baudrate (wahrscheinlich langsamer), oder eine Quarzfrequenz wählen die von vielen üblichen Baudraten teilbar ist (wie 3.6864MHz, 14.7456MHz).\r\n\r\nCode wird trotzdem erzeugt, aber er kann unzuverlässig oder beschädigt sein."
+
+"UART baud rate generator: too slow, divisor overflows. Use a slower crystal or a faster baud rate.\r\n\r\nCode will be generated anyways but serial will likely be completely broken."
+"UART Baudraten-Generator: Zu langsam, der Divisor hat Überlauf. Einen langsameren Quarz oder schnellere Baudrate verwenden.\r\n\r\nCode wird trotzdem erzeugt, aber er wird wahrscheinlich beschädigt sein."
+
+"Couldn't open '%s'\n"
+"Konnte nicht geöffnet werden '%s'\n"
+
+"Timer period too short (needs faster cycle time)."
+"Timer-Intervall zu kurz (schnellere Zykluszeit nötig)."
+
+"Timer period too long (max 32767 times cycle time); use a slower cycle time."
+"Timer-Intervall zu lang (max. 32767mal die Zykluszeit); langsamere Zykluszeit verwenden."
+
+"Constant %d out of range: -32768 to 32767 inclusive."
+"Konstante %d außerhalb des Bereichs: -32768 bis 32767 inklusive."
+
+"Move instruction: '%s' not a valid destination."
+"Transfer-Anweisung: '%s' ist keine gültige Zieladresse."
+
+"Math instruction: '%s' not a valid destination."
+"Mathem. Anweisung: '%s'keine gültige Zieladresse."
+
+"Piecewise linear lookup table with zero elements!"
+"Näherungs-Linear-Tabelle ohne Elemente!"
+
+"x values in piecewise linear table must be strictly increasing."
+"Die x-Werte in der Näherungs-Linear-Tabelle müssen strikt aufsteigend sein."
+
+"Numerical problem with piecewise linear lookup table. Either make the table entries smaller, or space the points together more closely.\r\n\r\nSee the help file for details."
+"Zahlenmäßiges Problem mit der Näherungs-Linear-Tabelle. Entweder die Eingangswerte der Tabelle verringern, oder die Punkte näher zusammen legen.\r\n\r\nFür Details siehe unter Hilfe."
+
+"Multiple escapes (\\0-9) present in format string, not allowed."
+"Mehrfacher Zeilenumbruch (\\0-9)in formatierter Zeichenfolge nicht gestattet."
+
+"Bad escape: correct form is \\xAB."
+"Falscher Zeilenumbruch: Korrekte Form = \\xAB."
+
+"Bad escape '\\%c'"
+"Falscher Zeilenumbruch '\\%c'"
+
+"Variable is interpolated into formatted string, but none is specified."
+"Formatierte Zeichenfolge enthält Variable, aber keine ist angegeben."
+
+"No variable is interpolated into formatted string, but a variable name is specified. Include a string like '\\-3', or leave variable name blank."
+"Keine Variable in formatierter Zeichenfolge eingefügt, aber ein Variabelen-Name wurde vergeben. Geben Sie eine Zeichenfolge ein, wie z.B. '\\-3', oder den Variabelen-Namen unausgefüllt lassen."
+
+"Empty row; delete it or add instructions before compiling."
+"Leere Reihe; vor dem Kompilieren löschen oder Anweisungen einfügen."
+
+"Couldn't write to '%s'"
+"Nicht möglich, speichern unter '%s'."
+
+"Unsupported op (anything ADC, PWM, UART, EEPROM) for interpretable target."
+"Keine unterstützte Operation der interpretierbaren Zieldatei (kein ADC, PWM, UART, EEPROM möglich)."
+
+"Compile successful; wrote interpretable code to '%s'.\r\n\r\nYou probably have to adapt the interpreter to your application. See the documentation."
+"Kompilierung war erfolgreich; interpretierbarer Code gespeichert unter '%s'.\r\n\r\nWahrscheinlich müssen Sie den Interpreter an Ihre Anwendung anpassen. Siehe Dokumentation."
+
+"Microcontroller '%s' not supported.\r\n\r\nDefaulting to no selected MCU."
+"Prozessor '%s' nicht unterstützt.\r\n\r\nZurück zu: Kein Prozessor gewählt."
+
+"File format error; perhaps this program is for a newer version of LDmicro?"
+"Fehler beim Dateiformat; vielleicht ist dies ein Programm für eine neuere Version von LDmicro."
+
+"Index:"
+"Liste:"
+
+"Points:"
+"Punkte:"
+
+"Count:"
+"Berechnung:"
+
+"Edit table of ASCII values like a string"
+"ASCII-Werte Tabelle als Zeichenfolge ausgeben"
+
+"Look-Up Table"
+"Nachschlag-Tabelle"
+
+"Piecewise Linear Table"
+"Näherungs-Linear-Tabelle"
+
+"LDmicro Error"
+"Fehler LDmicro"
+
+"Compile Successful"
+"Kompilierung war erfolgreich"
+
+"digital in"
+"Digitaler Eingang"
+
+"digital out"
+"Digitaler Ausgang"
+
+"int. relay"
+"Merker"
+
+"UART tx"
+"UART tx"
+
+"UART rx"
+"UART rx"
+
+"PWM out"
+"PWM Ausgang"
+
+"turn-on delay"
+"Anzugsverzögerung"
+
+"turn-off delay"
+"Abfallverzögerung"
+
+"retentive timer"
+"Speichernder Timer"
+
+"counter"
+"Zähler"
+
+"general var"
+"Allg. Variable"
+
+"adc input"
+"ADC Eingang"
+
+""
+""
+
+"(not assigned)"
+"(nicht zugewiesen)"
+
+""
+""
+
+""
+""
+
+"TOF: variable cannot be used elsewhere"
+"TOF: Variable kann andernorts nicht verwendet werden"
+
+"TON: variable cannot be used elsewhere"
+"TON: Variable kann andernorts nicht verwendet werden"
+
+"RTO: variable can only be used for RES elsewhere"
+"RTO: Variable kann andernorts nur für RES verwendet werden"
+
+"Variable '%s' not assigned to, e.g. with a MOV statement, an ADD statement, etc.\r\n\r\nThis is probably a programming error; now it will always be zero."
+"Variable '%s' nicht zugewiesen, z.B. zu einer Transfer- oder ADD-Anweisung usw.\r\n\r\nDas ist vermutlich ein Programmierungsfehler; jetzt wird sie immer Null sein."
+
+"Variable for '%s' incorrectly assigned: %s."
+"Variable für '%s' falsch zugewiesen: %s."
+
+"Division by zero; halting simulation"
+"Division durch Null; Simulation gestoppt"
+
+"!!!too long!!!"
+" !!!zu lang!!!"
+
+"\n\nI/O ASSIGNMENT:\n\n"
+"\n\nE/A Zuweisungen:\n\n"
+
+" Name | Type | Pin\n"
+" Name | Typ | Pin\n"
diff --git a/ldmicro/lang-es.txt b/ldmicro/lang-es.txt
new file mode 100644
index 0000000..623d6fa
--- /dev/null
+++ b/ldmicro/lang-es.txt
@@ -0,0 +1,767 @@
+"Target frequency %d Hz, closest achievable is %d Hz (warning, >5%% error)."
+"Frecuencia Micro %d Hz, la mejor aproximación es %d Hz (aviso, >5%% error)."
+
+"Compile successful; wrote IHEX for AVR to '%s'.\r\n\r\nRemember to set the processor configuration (fuses) correctly. This does not happen automatically."
+"Compilación correcta; se escribió IHEX para AVR en '%s'.\r\n\r\nRecuerde marcar la configuración (fuses) del micro correctamente. Esto NO se hace automaticamente."
+
+"( ) Normal"
+"( ) Normal"
+
+"(/) Negated"
+"(/) Negado"
+
+"(S) Set-Only"
+"(S) Activar"
+
+"(R) Reset-Only"
+"(R) Desactivar"
+
+"Pin on MCU"
+"Pata del Micro"
+
+"Coil"
+"Bobina"
+
+"Comment"
+"Comentario"
+
+"Cycle Time (ms):"
+"Tiempo Ciclo (ms):"
+
+"Crystal Frequency (MHz):"
+"Frecuencia Cristal (MHz):"
+
+"UART Baud Rate (bps):"
+"Baudios UART (bps):"
+
+"Serie (UART) will use pins %d and %d.\r\n\r\n"
+"Puerto Serie (UART) usará las patas %d y %d.\r\n\r\n"
+
+"Please select a micro with a UART.\r\n\r\n"
+"Por favor. Seleccione un micro con UART.\r\n\r\n"
+
+"No serial instructions (UART Send/UART Receive) are in use; add one to program before setting baud rate.\r\n\r\n"
+"No se han usado instrucciones (UART Enviar/UART Recibir) para el puerto serie aun; Añada una al programa antes de configurar los baudios.\r\n\r\n"
+
+"The cycle time for the 'PLC' runtime generated by LDmicro is user-configurable. Very short cycle times may not be achievable due to processor speed constraints, and very long cycle times may not be achievable due to hardware overflows. Cycle times between 10 ms and 100 ms will usually be practical.\r\n\r\nThe compiler must know what speed crystal you are using with the micro to convert between timing in clock cycles and timing in seconds. A 4 MHz to 20 MHz crystal is typical; check the speed grade of the part you are using to determine the maximum allowable clock speed before choosing a crystal."
+"El tiempo de ciclo de ejecución para el 'PLC' es configurable. Un tiempo de ciclo muy corto puede no funcionar debido a la baja velocidad del micro, y un tiempo de ciclo muy largo puede no funcionar por limitaciones del temporizador del micro. Ciclos de tiempo entre 10 y 100 ms suele ser lo normal.\r\n\r\nEl compilador debe conocer la velocidad del cristal que estas usando para poder convertir entre tiempo en ciclos de reloj y tiempo en segundos. Un cristal entre 4 Mhz y 20 Mhz es lo típico; Comprueba la velocidad a la que puede funcionar tu micro y calcula la velocidad máxima del reloj antes de elegir el cristal."
+
+"PLC Configuration"
+"Configuración PLC"
+
+"Zero cycle time not valid; resetting to 10 ms."
+"No es valido un tiempo de ciclo 0; forzado a 10 ms."
+
+"Source"
+"Fuente"
+
+"Internal Relay"
+"Rele Interno"
+
+"Input pin"
+"Pata Entrada"
+
+"Output pin"
+"Pata Salida"
+
+"|/| Negated"
+"|/| Negado"
+
+"Contacts"
+"Contacto"
+
+"No ADC or ADC not supported for selected micro."
+"El micro seleccionado no tiene ADC o no esta soportado."
+
+"Assign:"
+"Asignar:"
+
+"No microcontroller has been selected. You must select a microcontroller before you can assign I/O pins.\r\n\r\nSelect a microcontroller under the Settings menu and try again."
+"No se ha seleccionado micro. Debes seleccionar un micro antes de asignar patas E/S.\r\n\r\nElije un micro en el menu de configuración y prueba otra vez."
+
+"I/O Pin Assignment"
+"Asignación de pata E/S"
+
+"Can't specify I/O assignment for ANSI C target; compile and see comments in generated source code."
+"No se puede asignar la E/S especificadas para el ANSI C generado; compile y vea los comentarios generados en el código fuente."
+
+"Can't specify I/O assignment for interpretable target; see comments in reference implementation of interpreter."
+"No se puede asignar la E/S especificadas para el código generado para el interprete; vea los comentarios en la implementación del interprete."
+
+"Can only assign pin number to input/output pins (Xname or Yname or Aname)."
+"Solo puede asignar numero de pata a las patas de Entrada/Salida (Xname o Yname o Aname)."
+
+"No ADC or ADC not supported for this micro."
+"Este micro no tiene ADC o no esta soportado."
+
+"Rename I/O from default name ('%s') before assigning MCU pin."
+"Cambie el nombre por defecto ('%s') antes de asignarle una pata del micro."
+
+"I/O Pin"
+"E/S Pata"
+
+"(no pin)"
+"(falta pata)"
+
+""
+""
+
+""
+""
+
+""
+""
+
+"Export As Text"
+"Exportar como Texto"
+
+"Couldn't write to '%s'."
+"No puedo escribir en '%s'."
+
+"Compile To"
+"Compilar"
+
+"Must choose a target microcontroller before compiling."
+"Debe elegir un micro antes de compilar."
+
+"UART function used but not supported for this micro."
+"Usadas Funciones para UART. Este micro no las soporta."
+
+"PWM function used but not supported for this micro."
+"Usadas Funciones para PWM. Este micro no las soporta."
+
+"The program has changed since it was last saved.\r\n\r\nDo you want to save the changes?"
+"El programa ha cambiado desde la última vez que los guardo.\r\n\r\n¿Quieres guardar los cambios?"
+
+"--add comment here--"
+"--añade el comentario aquí--"
+
+"Start new program?"
+"¿Empezar un nuevo programa?"
+
+"Couldn't open '%s'."
+"No puedo abrir '%s'."
+
+"Name"
+"Nombre"
+
+"State"
+"Estado"
+
+"Pin on Processor"
+"Pata del Micro"
+
+"MCU Port"
+"Puerto del Micro"
+
+"LDmicro - Simulation (Running)"
+"LDmicro - Simulación (Ejecutando)"
+
+"LDmicro - Simulation (Stopped)"
+"LDmicro - Simulación (Parada)"
+
+"LDmicro - Program Editor"
+"LDmicro – Editor de Programa"
+
+" - (not yet saved)"
+" - (no guardado aún)"
+
+"&New\tCtrl+N"
+"&Nuevo\tCtrl+N"
+
+"&Open...\tCtrl+O"
+"&Abrir...\tCtrl+O"
+
+"&Save\tCtrl+S"
+"&Guardar\tCtrl+S"
+
+"Save &As..."
+"Guardar &Como..."
+
+"&Export As Text...\tCtrl+E"
+"&Exportar a Texto...\tCtrl+E"
+
+"E&xit"
+"&Salir"
+
+"&Undo\tCtrl+Z"
+"&Deshacer\tCtrl+Z"
+
+"&Redo\tCtrl+Y"
+"&Rehacer\tCtrl+Y"
+
+"Insert Rung &Before\tShift+6"
+"Insertar Línea (Rung) &Antes\tShift+6"
+
+"Insert Rung &After\tShift+V"
+"Insertar Línea (Rung) &Despues\tShift+V"
+
+"Move Selected Rung &Up\tShift+Up"
+"Subir Línea (Rung) Seleccionada\tShift+Up"
+
+"Move Selected Rung &Down\tShift+Down"
+"Bajar Línea (Rung) Seleccionada\tShift+Down"
+
+"&Delete Selected Element\tDel"
+"&Borrar Elemento Seleccionado\tSupr"
+
+"D&elete Rung\tShift+Del"
+"B&orrar Línea (Rung) Seleccionada\tShift+Supr"
+
+"Insert Co&mment\t;"
+"Insertar Co&mentario\t;"
+
+"Insert &Contacts\tC"
+"Insertar &Contacto\tC"
+
+"Insert OSR (One Shot Rising)\t&/"
+"Insertar OSR (Flanco de Subida)\t&/"
+
+"Insert OSF (One Shot Falling)\t&\\"
+"Insertar OSF (Flanco de Bajada)\t&\\"
+
+"Insert T&ON (Delayed Turn On)\tO"
+"Insertar T&ON (Encendido Retardado)\tO"
+
+"Insert TO&F (Delayed Turn Off)\tF"
+"Insertar TO&F (Apagado Retardado)\tF"
+
+"Insert R&TO (Retentive Delayed Turn On)\tT"
+"Insertar R&TO (Encendido Retardado con Memoria)\tT"
+
+"Insert CT&U (Count Up)\tU"
+"Insertar CT&U (Contador Incremental)\tU"
+
+"Insert CT&D (Count Down)\tI"
+"Insertar CT&D (Contador Decremental)\tI"
+
+"Insert CT&C (Count Circular)\tJ"
+"Insertar CT&C (Contador Circular)\tJ"
+
+"Insert EQU (Compare for Equals)\t="
+"Insertar EQU (Comparador si Igual)\t="
+
+"Insert NEQ (Compare for Not Equals)"
+"Insertar NEQ (Comparador si NO Igual)"
+
+"Insert GRT (Compare for Greater Than)\t>"
+"Insertar GRT (Comparador si Mayor que)\t>"
+
+"Insert GEQ (Compare for Greater Than or Equal)\t."
+"Insertar GEQ (Comparador si Mayor o Igual que)\t."
+
+"Insert LES (Compare for Less Than)\t<"
+"Insertar LES (Comparador si Menor que)\t<"
+
+"Insert LEQ (Compare for Less Than or Equal)\t,"
+"Insertar LEQ (Comparador si Menor o Igual que)\t,"
+
+"Insert Open-Circuit"
+"Insertar Circuito-Abierto"
+
+"Insert Short-Circuit"
+"Insertar Circuito-Cerrado"
+
+"Insert Master Control Relay"
+"Insertar Rele de Control Maestro"
+
+"Insert Coi&l\tL"
+"Insertar &Bobina\tL"
+
+"Insert R&ES (Counter/RTO Reset)\tE"
+"Insertar R&ES (Contador/RTO Reinicio)\tE"
+
+"Insert MOV (Move)\tM"
+"Insertar MOV (Mover)\tM"
+
+"Insert ADD (16-bit Integer Add)\t+"
+"Insertar ADD (Suma Entero 16-bit)\t+"
+
+"Insert SUB (16-bit Integer Subtract)\t-"
+"Insertar SUB (Resta Entero 16-bit)\t-"
+
+"Insert MUL (16-bit Integer Multiply)\t*"
+"Insertar MUL (Multiplica Entero 16-bit)\t*"
+
+"Insert DIV (16-bit Integer Divide)\tD"
+"Insertar DIV (Divide Entero 16-bit)\tD"
+
+"Insert Shift Register"
+"Insertar Registro de Desplazamiento"
+
+"Insert Look-Up Table"
+"Insertar Tabla de Busqueda"
+
+"Insert Piecewise Linear"
+"Insertar Linealización por Segmentos"
+
+"Insert Formatted String Over UART"
+"Insertar Cadena Formateada en la UART"
+
+"Insert &UART Send"
+"Insertar &UART Enviar"
+
+"Insert &UART Receive"
+"Insertar &UART Recibir"
+
+"Insert Set PWM Output"
+"Insertar Valor Salida PWM"
+
+"Insert A/D Converter Read\tP"
+"Insertar Lectura Conversor A/D\tP"
+
+"Insert Make Persistent"
+"Insertar Hacer Permanente"
+
+"Make Norm&al\tA"
+"Hacer Norm&al\tA"
+
+"Make &Negated\tN"
+"Hacer &Negado\tN"
+
+"Make &Set-Only\tS"
+"Hacer &Solo-Activar\tS"
+
+"Make &Reset-Only\tR"
+"Hace&r Solo-Desactivar\tR"
+
+"&MCU Parameters..."
+"&Parametros del Micro..."
+
+"(no microcontroller)"
+"(no microcontrolador)"
+
+"&Microcontroller"
+"&Microcontrolador"
+
+"Si&mulation Mode\tCtrl+M"
+"Modo Si&mulación \tCtrl+M"
+
+"Start &Real-Time Simulation\tCtrl+R"
+"Empezar Simulación en Tiempo &Real\tCtrl+R"
+
+"&Halt Simulation\tCtrl+H"
+"Parar Simulación\tCtrl+H"
+
+"Single &Cycle\tSpace"
+"Solo un &Ciclo\tSpace"
+
+"&Compile\tF5"
+"&Compilar\tF5"
+
+"Compile &As..."
+"Compilar &Como..."
+
+"&Manual...\tF1"
+"&Manual...\tF1"
+
+"&About..."
+"&Acerca de..."
+
+"&File"
+"&Archivo"
+
+"&Edit"
+"&Editar"
+
+"&Settings"
+"&Configuraciones"
+
+"&Instruction"
+"&Instrucción"
+
+"Si&mulate"
+"Si&mular"
+
+"&Compile"
+"&Compilar"
+
+"&Help"
+"&Ayuda"
+
+"no MCU selected"
+"micro no seleccionado"
+
+"cycle time %.2f ms"
+"tiempo ciclo %.2f ms"
+
+"processor clock %.4f MHz"
+"reloj procesador %.4f MHz"
+
+"Internal error relating to PIC paging; make program smaller or reshuffle it."
+"Error interno relativo a la paginación del PIC; Haz el programa mas pequeño o reorganizalo"
+
+"PWM frequency too fast."
+"Frecuencia del PWM demasiado alta."
+
+"PWM frequency too slow."
+"Frecuencia del PWM demasiado baja."
+
+"Cycle time too fast; increase cycle time, or use faster crystal."
+"Tiempo del Ciclo demasiado rapido; aumenta el tiempo de ciclo, o usa un cristal de mas Mhz."
+
+"Cycle time too slow; decrease cycle time, or use slower crystal."
+"Tiempo del Ciclo demasiado lento; incrementa el tiempo de ciclo, o usa un cristal de menos Mhz."
+
+"Couldn't open file '%s'"
+"No puedo abrir el archivo '%s'"
+
+"Zero baud rate not possible."
+"Cero baudios no es posible."
+
+"Compile successful; wrote IHEX for PIC16 to '%s'.\r\n\r\nConfiguration word (fuses) has been set for crystal oscillator, BOD enabled, LVP disabled, PWRT enabled, all code protection off.\r\n\r\nUsed %d/%d words of program flash (chip %d%% full)."
+"Compilación correcta; escrito IHEX para PIC16 en '%s'.\r\n\r\nBits de Configurarión (fuses) han sido establecidos para oscilador a cristal, BOD activado, LVP desactivado, PWRT activado, Todos los bits de protección desactivados.\r\n\r\nUsadas %d/%d palabras de programa en flash (Chip %d%% lleno)."
+
+"Type"
+"Tipo"
+
+"Timer"
+"Temporizador"
+
+"Counter"
+"Contador"
+
+"Reset"
+"Reiniciar"
+
+"OK"
+"OK"
+
+"Cancel"
+"Cancelar"
+
+"Empty textbox; not permitted."
+"Texto vacio; no permitido"
+
+"Bad use of quotes: <%s>"
+"Mal uso de las comillas: <%s>"
+
+"Turn-On Delay"
+"Activar Retardado"
+
+"Turn-Off Delay"
+"Desactivar Retardado"
+
+"Retentive Turn-On Delay"
+"Activar Retardado con Memoria"
+
+"Delay (ms):"
+"Retardo (ms):"
+
+"Delay too long; maximum is 2**31 us."
+"Retardo demasiado largo; maximo 2**31 us."
+
+"Delay cannot be zero or negative."
+"El retardo no puede ser cero o negativo."
+
+"Count Up"
+"Contador Creciente"
+
+"Count Down"
+"Contador Decreciente"
+
+"Circular Counter"
+"Contador Circular"
+
+"Max value:"
+"Valor Max:"
+
+"True if >= :"
+"Verdad si >= :"
+
+"If Equals"
+"Si igual"
+
+"If Not Equals"
+"Si NO igual"
+
+"If Greater Than"
+"Si mayor que"
+
+"If Greater Than or Equal To"
+"Si mayor o igual que"
+
+"If Less Than"
+"Si menor que"
+
+"If Less Than or Equal To"
+"Si menor o igual que"
+
+"'Closed' if:"
+"'Cerrado' si:"
+
+"Move"
+"Mover"
+
+"Read A/D Converter"
+"Leer Conversor A/D"
+
+"Duty cycle var:"
+"Var Ancho Ciclo:"
+
+"Frequency (Hz):"
+"Frecuencia (Hz):"
+
+"Set PWM Duty Cycle"
+"Poner Ancho de Pulso PWM"
+
+"Source:"
+"Fuente:"
+
+"Receive from UART"
+"Recibido en la UART"
+
+"Send to UART"
+"Enviado a la UART"
+
+"Add"
+"Sumar"
+
+"Subtract"
+"Restar"
+
+"Multiply"
+"Multiplicar"
+
+"Divide"
+"Dividir"
+
+"Destination:"
+"Destino:"
+
+"is set := :"
+"esta puesto := :"
+
+"Name:"
+"Nombre:"
+
+"Stages:"
+"Fases:"
+
+"Shift Register"
+"Registro Desplazamiento"
+
+"Not a reasonable size for a shift register."
+"No es un tamaño razonable para el Registro de Desplazamiento."
+
+"String:"
+"Cadena:"
+
+"Formatted String Over UART"
+"Cadena Formateada para UART"
+
+"Variable:"
+"Variable:"
+
+"Make Persistent"
+"Hacer permanente"
+
+"Too many elements in subcircuit!"
+"Demasiados elementos en un SubCircuito!"
+
+"Too many rungs!"
+"Demasiadas Lineas (rungs)!"
+
+"Error"
+"Error"
+
+"ANSI C target does not support peripherals (UART, PWM, ADC, EEPROM). Skipping that instruction."
+"ANSI C de destino no soporta perifericos (UART, PWM, ADC, EEPROM). Evite esa instrucción."
+
+"Compile successful; wrote C source code to '%s'.\r\n\r\nThis is not a complete C program. You have to provide the runtime and all the I/O routines. See the comments in the source code for information about how to do this."
+"Compilación correcta: Escrito Código Fuente en C en '%s'.\r\n\r\nNo es un programa completo en C. Tiene que añadirle el procedimiento principal y todas las rutinas de E/S. Vea los comentarios en el código fuente para mas información sobre como hacer esto"
+
+"Cannot delete rung; program must have at least one rung."
+"No puedo borrar la Linea (rung); el programa debe tener al menos una Linea (rung)."
+
+"Out of memory; simplify program or choose microcontroller with more memory."
+"Fuera de Memoria; Simplifique el programa o elija un micro con mas memoria.."
+
+"Must assign pins for all ADC inputs (name '%s')."
+"Debe asignar patas para todas las entradas del ADC (nombre '%s')."
+
+"Internal limit exceeded (number of vars)"
+"Limite interno superado (numero de variables)"
+
+"Internal relay '%s' never assigned; add its coil somewhere."
+"No ha asignado el rele interno '%s'; añada la bobina en cualquier parte del programa."
+
+"Must assign pins for all I/O.\r\n\r\n'%s' is not assigned."
+"Debe asignar patas a todas las E/S.\r\n\r\n'%s' no esta asignada."
+
+"UART in use; pins %d and %d reserved for that."
+"UART en uso; patas %d y %d reservadas para eso."
+
+"PWM in use; pin %d reserved for that."
+"PWM en uso; pata %d reservada para eso."
+
+"UART baud rate generator: divisor=%d actual=%.4f for %.2f%% error.\r\n\r\nThis 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\nCode will be generated anyways but serial may be unreliable or completely broken."
+"UART generador de baudios: divisor=%d actual=%.4f para %.2f%% error.\r\n\r\nEs demasiado grande; Prueba con otro valor de baudios (probablemente menor), o un cristal cuya frecuencia sea divible por los baudios mas comunes (p.e. 3.6864MHz, 14.7456MHz).\r\n\r\nEl código se genera de todas formas pero las tramas serie sean inestable o no entendible."
+
+"UART baud rate generator: too slow, divisor overflows. Use a slower crystal or a faster baud rate.\r\n\r\nCode will be generated anyways but serial will likely be completely broken."
+"UART generador de baudios: demasiado lento, divisor demasiado grande. Use un cristal mas lento o mayor baudios.\r\n\r\nEl código se genera de todas formas pero las tramas serie serán no entendible.."
+
+"Couldn't open '%s'\n"
+"No puedo abrir '%s'\n"
+
+"Timer period too short (needs faster cycle time)."
+"Periodo de Tiempo demasiado corto (se necesita un tiempo de ciclo menor)."
+
+"Timer period too long (max 32767 times cycle time); use a slower cycle time."
+"Periodo del temporizador demasiado largo (max. 32767 veces el tiempo de ciclo); use un tiempo de ciclo mayor."
+
+"Constant %d out of range: -32768 to 32767 inclusive."
+"Constante %d fuera de rango: -32768 a 32767 inclusive."
+
+"Move instruction: '%s' not a valid destination."
+"Instrucción Move: '%s' no es valido el destino."
+
+"Math instruction: '%s' not a valid destination."
+"Instrucción Math: '%s' no es valido el destino."
+
+"Piecewise linear lookup table with zero elements!"
+"tabla de linealizacion por segmentos con cero elementos!"
+
+"x values in piecewise linear table must be strictly increasing."
+"Los valores X en la tabla de linealización por segmentos deben ser estrictamente incrementales."
+
+"Numerical problem with piecewise linear lookup table. Either make the table entries smaller, or space the points together more closely.\r\n\r\nSee the help file for details."
+"Problema numérico con la tabla de linealización por segmentos. Haz la tabla de entradas mas pequeña, o aleja mas los puntos juntos.\r\n\r\nMira la ayuda para mas detalles."
+
+"Multiple escapes (\\0-9) present in format string, not allowed."
+"No esta permitido mas de un caracter especial (\\0-9) dentro de la cadena de caractares."
+
+"Bad escape: correct form is \\xAB."
+"Caracter Especial Erroneo: la forma correcta es = \\xAB."
+
+"Bad escape '\\%c'"
+"Caracter Especial Erroneo '\\%c'"
+
+"Variable is interpolated into formatted string, but none is specified."
+"Se ha declarado un parametro dentro la cadena de caracteres, pero falta especificar la variable."
+
+"No variable is interpolated into formatted string, but a variable name is specified. Include a string like '\\-3', or leave variable name blank."
+"No se ha declarado un parametro dentro de la cadena de caractares pero sin embargo se ha especificado una variable. Añada un cadena como '\\-3', o quite el nombre de la variable."
+
+"Empty row; delete it or add instructions before compiling."
+"Fila vacia; borrela o añada instrucciones antes de compilar."
+
+"Couldn't write to '%s'"
+"No puedo escribir en '%s'."
+
+"Unsupported op (anything ADC, PWM, UART, EEPROM) for interpretable target."
+"Op no soportada en el interprete (algun ADC, PWM, UART, EEPROM)."
+
+"Compile successful; wrote interpretable code to '%s'.\r\n\r\nYou probably have to adapt the interpreter to your application. See the documentation."
+"Compilación correcta: Código para interprete escrito en '%s'.\r\n\r\nProblablemente tengas que adaptar el interprete a tu aplicación. Mira la documentación."
+
+"Microcontroller '%s' not supported.\r\n\r\nDefaulting to no selected MCU."
+"Microcontrolador '%s' no sorportado.\r\n\r\nForzando ninguna CPU."
+
+"File format error; perhaps this program is for a newer version of LDmicro?"
+"Error en el formato de archivo; quizas este programa es una version mas moderna de LDmicro?."
+
+"Index:"
+"Indice:"
+
+"Points:"
+"Puntos:"
+
+"Count:"
+"Cantidad:"
+
+"Edit table of ASCII values like a string"
+"Editar tabla de valores ascii como una cadena"
+
+"Look-Up Table"
+"Buscar en Tabla"
+
+"Piecewise Linear Table"
+"Tabla de linealización por segmentos"
+
+"LDmicro Error"
+"LDmicro Error"
+
+"Compile Successful"
+"Compilación Correcta"
+
+"digital in"
+"entrada digital"
+
+"digital out"
+"salida digital"
+
+"int. relay"
+"rele interno"
+
+"UART tx"
+"UART tx"
+
+"UART rx"
+"UART rx"
+
+"PWM out"
+"salida PWM"
+
+"turn-on delay"
+"activar retardo"
+
+"turn-off delay"
+"desactivar retardo"
+
+"retentive timer"
+"temporizador con memoria"
+
+"counter"
+"contador"
+
+"general var"
+"var general"
+
+"adc input"
+"entrada adc"
+
+""
+""
+
+"(not assigned)"
+"(no asignado)"
+
+""
+""
+
+""
+""
+
+"TOF: variable cannot be used elsewhere"
+"TOF: la variable no puede ser usada en otra parte"
+
+"TON: variable cannot be used elsewhere"
+"TON: la variable no puede ser usada en otra parte"
+
+"RTO: variable can only be used for RES elsewhere"
+"RTO: la variable solo puede ser usada como RES en otra parte"
+
+"Variable '%s' not assigned to, e.g. with a MOV statement, an ADD statement, etc.\r\n\r\nThis is probably a programming error; now it will always be zero."
+"Variable '%s' no asignada, p.e. con el comando MOV, una instrucción ADD, etc.\r\n\r\nEsto es probablemente un error de programación; valdrá cero."
+
+"Variable for '%s' incorrectly assigned: %s."
+"Variable para '%s' incorrectamente asignada: %s."
+
+"Division by zero; halting simulation"
+"División por cero; Parando simulación"
+
+"!!!too long!!!"
+"!!Muy grande!!"
+
+"\n\nI/O ASSIGNMENT:\n\n"
+"\n\nE/S ASIGNACIÓN:\n\n"
+
+" Name | Type | Pin\n"
+" Nombre | Tipo | Pata\n"
+
+"Serial (UART) will use pins %d and %d.\r\n\r\n"
+"El Puerto Serie (UART) usará los pines %d y %d.\r\n\r\n"
+
+
+
diff --git a/ldmicro/lang-fr.txt b/ldmicro/lang-fr.txt
new file mode 100644
index 0000000..8ef6088
--- /dev/null
+++ b/ldmicro/lang-fr.txt
@@ -0,0 +1,761 @@
+"Target frequency %d Hz, closest achievable is %d Hz (warning, >5%% error)."
+"Fréquence de la cible %d Hz, fonction accomplie à %d Hz (ATTENTION, >5% erreur)."
+
+"Compile successful; wrote IHEX for AVR to '%s'.\r\n\r\nRemember to set the processor configuration (fuses) correctly. This does not happen automatically."
+"Compilé avec succès. Ecriture du fichier IHEX pour AVR sous '%s'.\r\n\r\nVous devez configurer manuellement les Bits de configuration (fusibles). Ceci n'est pas accompli automatiquement."
+
+"( ) Normal"
+"( ) Normal"
+
+"(/) Negated"
+"(/) Inversée"
+
+"(S) Set-Only"
+"(S) Activer"
+
+"(R) Reset-Only"
+"(R) RAZ"
+
+"Pin on MCU"
+"Broche MCU"
+
+"Coil"
+"Bobine"
+
+"Comment"
+"Commentaire"
+
+"Cycle Time (ms):"
+"Temps de cycle (ms):"
+
+"Crystal Frequency (MHz):"
+"Fréquence quartz (MHz):"
+
+"UART Baud Rate (bps):"
+"UART Vitesse (bps):"
+
+"Serial (UART) will use pins %d and %d.\r\n\r\n"
+"Communication série utilisera broches %d et %d.\r\n\r\n"
+
+"Please select a micro with a UART.\r\n\r\n"
+"Sélectionnez un processeur avec UART.\r\n\r\n"
+
+"No serial instructions (UART Send/UART Receive) are in use; add one to program before setting baud rate.\r\n\r\n"
+"Aucune instruction (émission ou réception UART) n'est utilisée; ajouter une instruction avant de fixer les vitesses.\r\n\r\n"
+
+"The cycle time for the 'PLC' runtime generated by LDmicro is user-configurable. Very short cycle times may not be achievable due to processor speed constraints, and very long cycle times may not be achievable due to hardware overflows. Cycle times between 10 ms and 100 ms will usually be practical.\r\n\r\nThe compiler must know what speed crystal you are using with the micro to convert between timing in clock cycles and timing in seconds. A 4 MHz to 20 MHz crystal is typical; check the speed grade of the part you are using to determine the maximum allowable clock speed before choosing a crystal."
+"Le temps de cycle de l'API (automate programmable) est configurable par l'utilisateur. Un temps trop court n'est pas utilisable dû à des contraintes de vitesse du processeur. Des temps de cycle trop longs ne sont pas utilisables à cause du dépassement de capacité des registres. Des temps de cycle compris entre 10 ms et 100 ms sont géneralement utilisables.\r\n\r\nLe compilateur doit connaitre la fréquence du quartz utilisé pour définir les temps de cycles d'horloge ainsi que les temporisations, les fréquences de 4 à 20 mhz sont typiques. Déterminer la vitesse désirée avant de choisir le quartz."
+
+"PLC Configuration"
+"Configuration API"
+
+"Zero cycle time not valid; resetting to 10 ms."
+"Temps de cycle non valide ; remis à 10 ms."
+
+"Source"
+"Source"
+
+"Internal Relay"
+"Relais interne"
+
+"Input pin"
+"Entrée"
+
+"Output pin"
+"Sortie"
+
+"|/| Negated"
+"|/| Normalement fermé"
+
+"Contacts"
+"Contacts"
+
+"No ADC or ADC not supported for selected micro."
+"Pas de convertisseur A/D ou convertisseur non supporté pour le MCU sélectionné."
+
+"Assign:"
+"Affectations:"
+
+"No microcontroller has been selected. You must select a microcontroller before you can assign I/O pins.\r\n\r\nSelect a microcontroller under the Settings menu and try again."
+"Aucun microcontrolleur sélectionné. Vous devez sélectionner un microcontroleur avant de définir l'utilisation des broches.\r\n\r\nSélectionnez un micro dans le menu et essayez à nouveau."
+
+"I/O Pin Assignment"
+"Affectation broches E/S"
+
+"Can't specify I/O assignment for ANSI C target; compile and see comments in generated source code."
+"Ne pas spécifier les E/S pour code en ANSI C; Compiler et voir les commentaires dans le code source généré."
+
+"Can't specify I/O assignment for interpretable target; see comments in reference implementation of interpreter."
+"Ne pas spécifier les E/S pour sortie en code interprété; Voir les commentaires pour l'implémentation de l'interpréteur ."
+
+"Can only assign pin number to input/output pins (Xname or Yname or Aname)."
+"Vous pouvez uniquement affecter les broches Entrées/Sorties (XName , YName ou AName)."
+
+"No ADC or ADC not supported for this micro."
+"Pas de convertisseur A/D ou convertisseur non supporté pour ce micro."
+
+"Rename I/O from default name ('%s') before assigning MCU pin."
+"Changer les noms par défauts des E/S ('%s') avant de leur affecter une broche MCU."
+
+"I/O Pin"
+"Broches E/S"
+
+"(no pin)"
+"(Aucune broche)"
+
+""
+""
+
+""
+""
+
+""
+""
+
+"Export As Text"
+"Exporter en texte"
+
+"Couldn't write to '%s'."
+"Impossible d'écrire '%s'."
+
+"Compile To"
+"Compiler sous"
+
+"Must choose a target microcontroller before compiling."
+"Choisir un microcontrolleur avant de compiler."
+
+"UART function used but not supported for this micro."
+"Des fonctions UART sont utilisées, mais non supportées par ce micro."
+
+"PWM function used but not supported for this micro."
+"Fonctions PWM utilisées mais non supportées par ce micro."
+
+"The program has changed since it was last saved.\r\n\r\nDo you want to save the changes?"
+"Le programme a changé depuis la dernière sauvegarde.\r\n\r\nVoulez-vous sauvegarder les changements?"
+
+"--add comment here--"
+"--Ajouter les commentaires ICI--"
+
+"Start new program?"
+"Commencer un nouveau programme?"
+
+"Couldn't open '%s'."
+"Impossible d'ouvrir '%s'."
+
+"Name"
+"Nom"
+
+"State"
+"Etat"
+
+"Pin on Processor"
+"Broche du Micro"
+
+"MCU Port"
+"Port du processeur"
+
+"LDmicro - Simulation (Running)"
+"LDmicro - Simulation (en cours)"
+
+"LDmicro - Simulation (Stopped)"
+"LDmicro - Simulation (Arrétée)"
+
+"LDmicro - Program Editor"
+"LDmicro – Edition du programme "
+
+" - (not yet saved)"
+" - (fichier non sauvegardé)"
+
+"&New\tCtrl+N"
+"&Nouveau\tCtrl+N"
+
+"&Open...\tCtrl+O"
+"&Ouvrir...\tCtrl+O"
+
+"&Save\tCtrl+S"
+"&Sauvegarder\tCtrl+S"
+
+"Save &As..."
+"S&auvegarder sous..."
+
+"&Export As Text...\tCtrl+E"
+"&Exporter en texte...\tCtrl+E"
+
+"E&xit"
+"Quitter"
+
+"&Undo\tCtrl+Z"
+"Annuler\tCtrl+Z"
+
+"&Redo\tCtrl+Y"
+"&Refaire\tCtrl+Y"
+
+"Insert Rung &Before\tShift+6"
+"Insérer ligne avant\tShift+6"
+
+"Insert Rung &After\tShift+V"
+"Insérer ligne &après\tShift+V"
+
+"Move Selected Rung &Up\tShift+Up"
+"Déplacer la ligne sélectionnée au dessus\tShift+Up"
+
+"Move Selected Rung &Down\tShift+Down"
+"Déplacer la ligne sélectionnée au dessous\tShift+Down"
+
+"&Delete Selected Element\tDel"
+"&Effacer l'élement sélectionné\tSuppr"
+
+"D&elete Rung\tShift+Del"
+"Supprimer la ligne\tShift+Suppr"
+
+"Insert Co&mment\t;"
+"Insérer commentaire\t;"
+
+"Insert &Contacts\tC"
+"Insérer &contact\tC"
+
+"Insert OSR (One Shot Rising)\t&/"
+"Insérer OSR (Front montant)\t&/"
+
+"Insert OSF (One Shot Falling)\t&\\"
+"Insérer OSF (Front descendant)\t&\\"
+
+"Insert T&ON (Delayed Turn On)\tO"
+"Insérer T&ON (Tempo travail)\tO"
+
+"Insert TO&F (Delayed Turn Off)\tF"
+"Insérer TO&F (Tempo repos)\tF"
+
+"Insert R&TO (Retentive Delayed Turn On)\tT"
+"Insérer R&TO (Tempo totalisatrice)\tT"
+
+"Insert CT&U (Count Up)\tU"
+"Insérer CT&U (Compteur)\tU"
+
+"Insert CT&D (Count Down)\tI"
+"Insérer CT&D (Décompteur)\tI"
+
+"Insert CT&C (Count Circular)\tJ"
+"Insérer CT&C (Compteur cyclique)\tJ"
+
+"Insert EQU (Compare for Equals)\t="
+"Insérer EQU (Compare pour égalité)\t="
+
+"Insert NEQ (Compare for Not Equals)"
+"Insérer NEQ (Compare pour inégalité)"
+
+"Insert GRT (Compare for Greater Than)\t>"
+"Insérer GRT (Compare plus grand que)\t>"
+
+"Insert GEQ (Compare for Greater Than or Equal)\t."
+"Insérer GEQ (Compare plus grand ou égal à)\t."
+
+"Insert LES (Compare for Less Than)\t<"
+"Insérer LES (Compare plus petit que)\t<"
+
+"Insert LEQ (Compare for Less Than or Equal)\t,"
+"Insérer LEQ (Compare plus petit ou égal à)\t,"
+
+"Insert Open-Circuit"
+"Insérer circuit ouvert"
+
+"Insert Short-Circuit"
+"Insérer court circuit"
+
+"Insert Master Control Relay"
+"Insérer relais de contrôle maitre"
+
+"Insert Coi&l\tL"
+"Insérer bobine re&lais \tL"
+
+"Insert R&ES (Counter/RTO Reset)\tE"
+"Insérer R&ES (Remise à zéro RTO/compteur)\tE"
+
+"Insert MOV (Move)\tM"
+"Insérer MOV (Mouvoir)\tM"
+
+"Insert ADD (16-bit Integer Add)\t+"
+"Insérer ADD (Addition entier 16-bit)\t+"
+
+"Insert SUB (16-bit Integer Subtract)\t-"
+"Insérer SUB (Soustraction entier 16-bit)\t-"
+
+"Insert MUL (16-bit Integer Multiply)\t*"
+"Insérer MUL (Multiplication entier 16-bit)\t*"
+
+"Insert DIV (16-bit Integer Divide)\tD"
+"Insérer DIV (Division entier 16-bit)\tD"
+
+"Insert Shift Register"
+"Insérer registre à décalage"
+
+"Insert Look-Up Table"
+"Insérer tableau indexé"
+
+"Insert Piecewise Linear"
+"Insérer tableau d'éléments linéaires"
+
+"Insert Formatted String Over UART"
+"Insérer chaine formattée pour l'UART"
+
+"Insert &UART Send"
+"Insérer émission &UART"
+
+"Insert &UART Receive"
+"Insérer réception &UART"
+
+"Insert Set PWM Output"
+"Insérer fixer sortie PWM"
+
+"Insert A/D Converter Read\tP"
+"Insérer lecture convertisseur A/D\tP"
+
+"Insert Make Persistent"
+"Insérer mettre rémanent"
+
+"Make Norm&al\tA"
+"Mettre norm&al\tA"
+
+"Make &Negated\tN"
+"I&nverser\tN"
+
+"Make &Set-Only\tS"
+"Activer uniquement\tS"
+
+"Make &Reset-Only\tR"
+"Faire RAZ uniquement\tR"
+
+"&MCU Parameters..."
+"&Paramètres MCU..."
+
+"(no microcontroller)"
+"(pas de microcontrolleur)"
+
+"&Microcontroller"
+"&Microcontrolleur"
+
+"Si&mulation Mode\tCtrl+M"
+"Mode si&mulation\tCtrl+M"
+
+"Start &Real-Time Simulation\tCtrl+R"
+"Commencer la simulation en temps &réel\tCtrl+R"
+
+"&Halt Simulation\tCtrl+H"
+"&Arrêter la simulation\tCtrl+H"
+
+"Single &Cycle\tSpace"
+"&Cycle unique\tEspace"
+
+"&Compile\tF5"
+"&Compiler\tF5"
+
+"Compile &As..."
+"Compiler sous..."
+
+"&Manual...\tF1"
+"&Manuel...\tF1"
+
+"&About..."
+"&A propos..."
+
+"&File"
+"&Fichier"
+
+"&Edit"
+"&Edition"
+
+"&Settings"
+"&Paramètres"
+
+"&Instruction"
+"&Instruction"
+
+"Si&mulate"
+"Si&mulation"
+
+"&Compile"
+"&Compilation"
+
+"&Help"
+"&Aide"
+
+"no MCU selected"
+"pas de MCU sélectionné"
+
+"cycle time %.2f ms"
+"cycle %.2f ms"
+
+"processor clock %.4f MHz"
+"horloge processeur %.4f MHz"
+
+"Internal error relating to PIC paging; make program smaller or reshuffle it."
+"Erreur interne dans l'utilisation des pages du PIC; Diminuer le programme ou le remanier"
+
+"PWM frequency too fast."
+"Fréquence PWM trop rapide."
+
+"PWM frequency too slow."
+"Frequence PWM trop lente."
+
+"Cycle time too fast; increase cycle time, or use faster crystal."
+"Temps cycle trop court; augmenter le temps de cycle ou utiliser un quartz plus rapide."
+
+"Cycle time too slow; decrease cycle time, or use slower crystal."
+"Temps de cycle trop long ; Diminuer le temps de cycle ou la fréquence du quartz ."
+
+"Couldn't open file '%s'"
+"Impossible d'ouvrir le fichier '%s'"
+
+"Zero baud rate not possible."
+"Vitesse transmission = 0 : impossible"
+
+"Compile successful; wrote IHEX for PIC16 to '%s'.\r\n\r\nConfiguration word (fuses) has been set for crystal oscillator, BOD enabled, LVP disabled, PWRT enabled, all code protection off.\r\n\r\nUsed %d/%d words of program flash (chip %d%% full)."
+"Compilé avec succès; Ecriture IHEX pour PIC16 sous '%s'.\r\n\r\nLes bits de configuration (fuse) : Oscillateur quartz, BOD activé, LVP déactivé, PWRT activé, sans code de protection.\r\n\r\nUtilise %d/%d mots du programme flash (Chip %d%% au total)."
+
+"Type"
+"Type"
+
+"Timer"
+"Temporisation"
+
+"Counter"
+"Compteur"
+
+"Reset"
+"RAZ"
+
+"OK"
+"OK"
+
+"Cancel"
+"Annuler"
+
+"Empty textbox; not permitted."
+"Zone de texte vide; interdite"
+
+"Bad use of quotes: <%s>"
+"Utilisation incorrecte des guillemets: <%s>"
+
+"Turn-On Delay"
+"Tempo travail"
+
+"Turn-Off Delay"
+"Tempo repos"
+
+"Retentive Turn-On Delay"
+"Temporisation totalisatrice"
+
+"Delay (ms):"
+"Temps (ms):"
+
+"Delay too long; maximum is 2**31 us."
+"Temps trop long; maximum 2**31 us."
+
+"Delay cannot be zero or negative."
+"Tempo ne peut être à ZERO ou NEGATIF."
+
+"Count Up"
+"Compteur"
+
+"Count Down"
+"Décompteur"
+
+"Circular Counter"
+"Compteur cyclique"
+
+"Max value:"
+"Valeur max.:"
+
+"True if >= :"
+"Vrai si >= :"
+
+"If Equals"
+"Si EGAL"
+
+"If Not Equals"
+"Si non EGAL à"
+
+"If Greater Than"
+"Si plus grand que"
+
+"If Greater Than or Equal To"
+"Si plus grand ou égal à"
+
+"If Less Than"
+"Si plus petit que"
+
+"If Less Than or Equal To"
+"Si plus petit ou égal à"
+
+"'Closed' if:"
+"'Fermé' si:"
+
+"Move"
+"Mouvoir"
+
+"Read A/D Converter"
+"Lecture du convertisseur A/D"
+
+"Duty cycle var:"
+"Utilisation:"
+
+"Frequency (Hz):"
+"Frequence (Hz):"
+
+"Set PWM Duty Cycle"
+"Fixer le rapport de cycle PWM"
+
+"Source:"
+"Source:"
+
+"Receive from UART"
+"Reception depuis l'UART"
+
+"Send to UART"
+"Envoyer vers l'UART"
+
+"Add"
+"Addition"
+
+"Subtract"
+"Soustraction"
+
+"Multiply"
+"Multiplication"
+
+"Divide"
+"Division"
+
+"Destination:"
+"Destination:"
+
+"is set := :"
+"Valeur := :"
+
+"Name:"
+"Nom:"
+
+"Stages:"
+"Etapes:"
+
+"Shift Register"
+"Registre à décalage"
+
+"Not a reasonable size for a shift register."
+"N'est pas une bonne taille pour un registre à décalage."
+
+"String:"
+"Chaine:"
+
+"Formatted String Over UART"
+"Chaine formatée pour l'UART"
+
+"Variable:"
+"Variable:"
+
+"Make Persistent"
+"Mettre rémanent"
+
+"Too many elements in subcircuit!"
+"Trop d'éléments dans le circuit secondaire !"
+
+"Too many rungs!"
+"Trop de séquences!"
+
+"Error"
+"Erreur"
+
+"ANSI C target does not support peripherals (UART, PWM, ADC, EEPROM). Skipping that instruction."
+"La sortie en code ANSI C ne supporte pas les périphériques (UART, ADC, EEPROM). Ne pas utiliser ces instructions."
+
+"Compile successful; wrote C source code to '%s'.\r\n\r\nThis is not a complete C program. You have to provide the runtime and all the I/O routines. See the comments in the source code for information about how to do this."
+"Compilé avec succès; Le code source C enregistré sous '%s'.\r\n\r\nCe programme n'est pas complet. Vous devez prévoir le runtime ainsi que toutes les routines d'entrées/sorties. Voir les commentaires pour connaitre la façon de procéder."
+
+"Cannot delete rung; program must have at least one rung."
+"Impossible de supprimer la ligne, le programme doit avoir au moins une ligne."
+
+"Out of memory; simplify program or choose microcontroller with more memory."
+"Mémoire insuffisante; simplifiez le programme ou utiliser un controleur avec plus de mémoire."
+
+"Must assign pins for all ADC inputs (name '%s')."
+"Vous devez spécifier une broche pour toutes les entrées ADC (nom '%s')."
+
+"Internal limit exceeded (number of vars)"
+"Vous dépassez la limite interne du nombre de variables"
+
+"Internal relay '%s' never assigned; add its coil somewhere."
+"Relais internes '%s', jamais utilisés, à utiliser pour la commande de bobines dans le programme."
+
+"Must assign pins for all I/O.\r\n\r\n'%s' is not assigned."
+"Vous devez spécifier les broches pour toutes les E/S.\r\n\r\n'%s' ."
+
+"UART in use; pins %d and %d reserved for that."
+"UART utilisé; broches %d et %d réservée pour cette fonction."
+
+"PWM in use; pin %d reserved for that."
+"PWM utilisé; broche %d réservée pour cela."
+
+"UART baud rate generator: divisor=%d actual=%.4f for %.2f%% error.\r\n\r\nThis 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\nCode will be generated anyways but serial may be unreliable or completely broken."
+"UART Générateur vitesse: Diviseur=%d actuel=%.4f pour %.2f%% Erreur.\r\n\r\nCeci est trop important; Essayez une autre vitesse (probablement plus lente), ou choisir un quartz divisible par plus de vitesses de transmission (comme 3.6864MHz, 14.7456MHz).\r\n\r\nCode est tout de même généré, mais la liaison peut être inutilisable ou la transmission erronée."
+
+"UART baud rate generator: too slow, divisor overflows. Use a slower crystal or a faster baud rate.\r\n\r\nCode will be generated anyways but serial will likely be completely broken."
+"Générateur vitesse UART trop lent, dépassement de capacité du diviseur. Utiliser un quartz plus lent ou une vitesse plus élevée.\r\n\r\nCode est généré mais la liaison semble inutilisable."
+
+"Couldn't open '%s'\n"
+"Impossible d'ouvrir '%s'\n"
+
+"Timer period too short (needs faster cycle time)."
+"Période temporisation trop courte (Diminuer le temps de cycle)."
+
+"Timer period too long (max 32767 times cycle time); use a slower cycle time."
+"Période de tempo trop longue (max. 32767 fois le temps de cycle); utiliser un temps de cycle plus long."
+
+"Constant %d out of range: -32768 to 32767 inclusive."
+"Constante %d hors limites: -32768 à 32767 inclus."
+
+"Move instruction: '%s' not a valid destination."
+"Instruction 'Mouvoir': '%s' n'est pas une destination valide."
+
+"Math instruction: '%s' not a valid destination."
+"Instruction Math: '%s' n'est pas une destination valide."
+
+"Piecewise linear lookup table with zero elements!"
+"Le tableau indexé linéaire ne comporte aucun élément!"
+
+"x values in piecewise linear table must be strictly increasing."
+"Les valeurs x du tableau indexé linéaire doivent être obligatoirement dans un ordre croissant."
+
+"Numerical problem with piecewise linear lookup table. Either make the table entries smaller, or space the points together more closely.\r\n\r\nSee the help file for details."
+"Problème numérique avec le tableau. Commencer par les faibles valeurs ou rapprocher les points du tableau .\r\n\r\nVoir fichier d'aide pour plus de détails."
+
+"Multiple escapes (\\0-9) present in format string, not allowed."
+"Multiples ESC (\\0-9)présents dans un format chaines non permit."
+
+"Bad escape: correct form is \\xAB."
+"ESC incorrect: Forme correcte = \\xAB."
+
+"Bad escape '\\%c'"
+"ESC incorrect '\\%c'"
+
+"Variable is interpolated into formatted string, but none is specified."
+"Une variable est appellée dans une chaine formattée, mais n'est pas spécifiée."
+
+"No variable is interpolated into formatted string, but a variable name is specified. Include a string like '\\-3', or leave variable name blank."
+"Aucune variable n'est appelée dans une chaine formattée, mais un nom de variable est spécifié . Insérer une chaine comme: '\\-3', ou laisser le nom de variable vide."
+
+"Empty row; delete it or add instructions before compiling."
+"Ligne vide; la supprimer ou ajouter instructions avant compilation."
+
+"Couldn't write to '%s'"
+"Impossible d'écrire sous '%s'."
+
+"Unsupported op (anything ADC, PWM, UART, EEPROM) for interpretable target."
+"Instructions non supportées (comme ADC, PWM, UART, EEPROM ) pour sortie en code interprété."
+
+"Compile successful; wrote interpretable code to '%s'.\r\n\r\nYou probably have to adapt the interpreter to your application. See the documentation."
+"Compilé avec succès; Code interprété écrit sous '%s'.\r\n\r\nQue vous devez adapter probablement pour votre application. Voir documentation."
+
+"Microcontroller '%s' not supported.\r\n\r\nDefaulting to no selected MCU."
+"Microcontrolleur '%s' non supporté.\r\n\r\nErreur pas de MCU sélectionné."
+
+"File format error; perhaps this program is for a newer version of LDmicro?"
+"Erreur format du fichier; ce programme est pour une nouvelle version de LDmicro."
+
+"Index:"
+"Index:"
+
+"Points:"
+"Points:"
+
+"Count:"
+"Compteur:"
+
+"Edit table of ASCII values like a string"
+"Editer tableau de valeur ASCII comme chaine"
+
+"Look-Up Table"
+"Tableau indexé"
+
+"Piecewise Linear Table"
+"Tableau d'éléments linéaire"
+
+"LDmicro Error"
+"Erreur LDmicro"
+
+"Compile Successful"
+"Compilé avec succès"
+
+"digital in"
+"Entrée digitale"
+
+"digital out"
+"Sortie digitale"
+
+"int. relay"
+"Relais interne"
+
+"UART tx"
+"UART tx"
+
+"UART rx"
+"UART rx"
+
+"PWM out"
+"Sortie PWM"
+
+"turn-on delay"
+"Tempo travail"
+
+"turn-off delay"
+"Tempo repos"
+
+"retentive timer"
+"Tempo totalisatrice"
+
+"counter"
+"Compteur"
+
+"general var"
+"Var. générale"
+
+"adc input"
+"Entrée ADC"
+
+""
+""
+
+"(not assigned)"
+"(Non affecté)"
+
+""
+""
+
+""
+""
+
+"TOF: variable cannot be used elsewhere"
+"TOF: Variable ne peut être utilisée nullepart ailleurs"
+
+"TON: variable cannot be used elsewhere"
+"TON: Variable ne peut être utilisée nullepart ailleurs"
+
+"RTO: variable can only be used for RES elsewhere"
+"RTO: Variable ne peut être uniquement utilisée que pour RAZ"
+
+"Variable '%s' not assigned to, e.g. with a MOV statement, an ADD statement, etc.\r\n\r\nThis is probably a programming error; now it will always be zero."
+"Variable '%s' non affectée, ex: avec une commande MOV, une instruction ADD-etc.\r\n\r\nCeci est probablement une erreur de programmation; elle reste toujours à zéro."
+
+"Variable for '%s' incorrectly assigned: %s."
+"Variable pour '%s' Affectation incorrecte: %s."
+
+"Division by zero; halting simulation"
+"Division par zéro; Simulation arrétée"
+
+"!!!too long!!!"
+"!!!trop long!!"
+
+"\n\nI/O ASSIGNMENT:\n\n"
+"\n\nE/S AFFECTATIONS:\n\n"
+
+" Name | Type | Pin\n"
+" Nom | Type | Broche\n"
diff --git a/ldmicro/lang-it.txt b/ldmicro/lang-it.txt
new file mode 100644
index 0000000..baf6ba3
--- /dev/null
+++ b/ldmicro/lang-it.txt
@@ -0,0 +1,761 @@
+"Target frequency %d Hz, closest achievable is %d Hz (warning, >5%% error)."
+"Target frequenza %d Hz, il più vicino realizzabile è %d Hz (avvertimento, >5%% di errore)."
+
+"Compile successful; wrote IHEX for AVR to '%s'.\r\n\r\nRemember to set the processor configuration (fuses) correctly. This does not happen automatically."
+"Compilazione ok; scritto IHEX per AVR a '%s'.\r\n\r\nRicorda di impostare il processore (Configurazione fusibili) correttamente. Questo non avviene automaticamente."
+
+"( ) Normal"
+"( ) Aperto"
+
+"(/) Negated"
+"(/) Negato"
+
+"(S) Set-Only"
+"(S) Setta"
+
+"(R) Reset-Only"
+"(R) Resetta"
+
+"Pin on MCU"
+"Pin MCU"
+
+"Coil"
+"Bobina"
+
+"Comment"
+"Commento"
+
+"Cycle Time (ms):"
+"Tempo di ciclo (ms):"
+
+"Crystal Frequency (MHz):"
+"Frequenza quarzo (MHz):"
+
+"UART Baud Rate (bps):"
+"UART Baud Rate (bps):"
+
+"Serial (UART) will use pins %d and %d.\r\n\r\n"
+"Comunicazione seriale pin utilizzati %d et %d.\r\n\r\n"
+
+"Please select a micro with a UART.\r\n\r\n"
+"Selezionare un processore dotato di UART.\r\n\r\n"
+
+"No serial instructions (UART Send/UART Receive) are in use; add one to program before setting baud rate.\r\n\r\n"
+"Nessuna istruzione (UART trasmetti o UART ricevi) è utilizzata; aggiungere un istruzione prima di settare il baud rate.\r\n\r\n"
+
+"The cycle time for the 'PLC' runtime generated by LDmicro is user-configurable. Very short cycle times may not be achievable due to processor speed constraints, and very long cycle times may not be achievable due to hardware overflows. Cycle times between 10 ms and 100 ms will usually be practical.\r\n\r\nThe compiler must know what speed crystal you are using with the micro to convert between timing in clock cycles and timing in seconds. A 4 MHz to 20 MHz crystal is typical; check the speed grade of the part you are using to determine the maximum allowable clock speed before choosing a crystal."
+"Il tempo di ciclo per il 'PLC' LDmicro è configurabile dall' utente. Tempi di ciclo molto brevi possono non essere realizzabili a causa di vincoli di velocità del processore, e tempi di ciclo molto lunghi possono essere causa di overflow. Tempi di ciclo tra i 10 ms e di 100 ms di solito sono usuali.\r\n\r\nIl compilatore deve sapere che velocità di cristallo stai usando con il micro per convertire in cicli di clock i Tempi. Da 4 MHz a 20 MHz, è il cristallo tipico; determinare la velocità massima consentita prima di scegliere un cristallo."
+
+"PLC Configuration"
+"Configurazione PLC"
+
+"Zero cycle time not valid; resetting to 10 ms."
+"Tempo di ciclo non valido; reimpostare a 10 ms."
+
+"Source"
+"Sorgente"
+
+"Internal Relay"
+"Relè interno"
+
+"Input pin"
+"Input pin"
+
+"Output pin"
+"Output pin"
+
+"|/| Negated"
+"|/| Normalmente chiuso"
+
+"Contacts"
+"Contatto"
+
+"No ADC or ADC not supported for selected micro."
+"Questo micro non suppota l'ADC."
+
+"Assign:"
+"Assegna:"
+
+"No microcontroller has been selected. You must select a microcontroller before you can assign I/O pins.\r\n\r\nSelect a microcontroller under the Settings menu and try again."
+"Microcontrollore non selezionato. Selezionare un microcontrollore prima di assegnare i pin.\r\n\r\nSeleziona un microcontrollore dal il menu Impostazioni e riprova."
+
+"I/O Pin Assignment"
+"Assegnazione Pin I/O"
+
+"Can't specify I/O assignment for ANSI C target; compile and see comments in generated source code."
+"Non specificare l'assegnazione dell' I/O per la generazione di ANSI C; compilare e visualizzare i commenti nel codice sorgente."
+
+"Can't specify I/O assignment for interpretable target; see comments in reference implementation of interpreter."
+"Non specificare l'assegnazione dell' I/O per la generazione di codice interpretabile; compilare e visualizzare i commenti nel codice sorgente."
+
+"Can only assign pin number to input/output pins (Xname or Yname or Aname)."
+"Assegnare unicamente il numero di pin input/output (XNome, YNome ou ANome)."
+
+"No ADC or ADC not supported for this micro."
+"Questo micro non contiene ADC."
+
+"Rename I/O from default name ('%s') before assigning MCU pin."
+"Rinominare l' I/O per ottenere il nome di defolt ('%s') prima di assegnare i pin del Micro."
+
+"I/O Pin"
+"I/O Pin"
+
+"(no pin)"
+"(senza pin)"
+
+""
+""
+
+""
+""
+
+""
+""
+
+"Export As Text"
+"Esportare il testo"
+
+"Couldn't write to '%s'."
+"Scrittura Impossibile '%s'."
+
+"Compile To"
+"Per compilare"
+
+"Must choose a target microcontroller before compiling."
+"Scegliere un microcontrollore prima di compilare."
+
+"UART function used but not supported for this micro."
+"Le funzioni UART sono usate ma non supportate da questo micro."
+
+"PWM function used but not supported for this micro."
+"Le funzioni PWM sono usate ma non supportate da questo micro."
+
+"The program has changed since it was last saved.\r\n\r\nDo you want to save the changes?"
+"Il programma è cambiato da quando è stato salvato l'ultima volta.\r\n\r\nDo si desidera salvare le modifiche? "
+
+"--add comment here--"
+"--aggiungere il commento--"
+
+"Start new program?"
+"Iniziare nuovo programma?"
+
+"Couldn't open '%s'."
+"Impossibile aprire '%s'."
+
+"Name"
+"Nome"
+
+"State"
+"Stato"
+
+"Pin on Processor"
+"Pin del Micro"
+
+"MCU Port"
+"Porta del processore"
+
+"LDmicro - Simulation (Running)"
+"LDmicro - Simulazione (in corso)"
+
+"LDmicro - Simulation (Stopped)"
+"LDmicro - Simulazione (Ferma)"
+
+"LDmicro - Program Editor"
+"LDmicro - Scrivere il programma"
+
+" - (not yet saved)"
+" - (non ancora salvato)"
+
+"&New\tCtrl+N"
+"&Nuovo\tCtrl+N"
+
+"&Open...\tCtrl+O"
+"&Aprire...\tCtrl+O"
+
+"&Save\tCtrl+S"
+"&Salva\tCtrl+S"
+
+"Save &As..."
+"Salva &con nome..."
+
+"&Export As Text...\tCtrl+E"
+"&Esportare il testo...\tCtrl+E"
+
+"E&xit"
+"U&scita"
+
+"&Undo\tCtrl+Z"
+"&Torna indietro\tCtrl+Z"
+
+"&Redo\tCtrl+Y"
+"&Rifare\tCtrl+Y"
+
+"Insert Rung &Before\tShift+6"
+"Inserire Ramo &Davanti\tShift+6"
+
+"Insert Rung &After\tShift+V"
+"Inserire Ramo &Dietro\tShift+V"
+
+"Move Selected Rung &Up\tShift+Up"
+"Muove il Ramo selezionato &Su\tShift+Up"
+
+"Move Selected Rung &Down\tShift+Down"
+"Muove il Ramo selezionato &Giù\tShift+Down"
+
+"&Delete Selected Element\tDel"
+"&Cancella l'elemento selezionato\tDel"
+
+"D&elete Rung\tShift+Del"
+"Cancellare il Ramo\tShift+Del"
+
+"Insert Co&mment\t;"
+"Inserire Co&mmento\t;"
+
+"Insert &Contacts\tC"
+"Inserire &Contatto\tC"
+
+"Insert OSR (One Shot Rising)\t&/"
+"Inserire PULSUP (Fronte di salita)\t&/"
+
+"Insert OSF (One Shot Falling)\t&\\"
+"Inserire PULSDW (Fronte di discesa)\t&\\"
+
+"Insert T&ON (Delayed Turn On)\tO"
+"Inserire T&ON (Tempo di ritardo On)\tO"
+
+"Insert TO&F (Delayed Turn Off)\tF"
+"Inserire TO&F (Tempo di ritardo Off)\tF"
+
+"Insert R&TO (Retentive Delayed Turn On)\tT"
+"Inserire R&TO (Tempo di ritardo ritentivo On)\tT"
+
+"Insert CT&U (Count Up)\tU"
+"Inserire CT&U (Contatore Up)\tU"
+
+"Insert CT&D (Count Down)\tI"
+"Inserire CT&D (Contatore Down)\tI"
+
+"Insert CT&C (Count Circular)\tJ"
+"Inserire CT&C (Contatore ciclico)\tJ"
+
+"Insert EQU (Compare for Equals)\t="
+"Inserire EQU (Compara per Uguale)\t="
+
+"Insert NEQ (Compare for Not Equals)"
+"Inserire NEQ (Compara per Diverso)"
+
+"Insert GRT (Compare for Greater Than)\t>"
+"Inserire GRT (Compara per Maggiore)\t>"
+
+"Insert GEQ (Compare for Greater Than or Equal)\t."
+"Inserire GEQ (Compara per Maggiore o Uguale)\t."
+
+"Insert LES (Compare for Less Than)\t<"
+"Inserire LES (Compara per Minore)\t<"
+
+"Insert LEQ (Compare for Less Than or Equal)\t,"
+"Inserire LEQ (Compara per Minore o Uguale)\t,"
+
+"Insert Open-Circuit"
+"Inserire Circuito Aperto"
+
+"Insert Short-Circuit"
+"Inserire Corto Circuito"
+
+"Insert Master Control Relay"
+"Inserire Master Control Relay"
+
+"Insert Coi&l\tL"
+"Inserire Bobina Re&lè\tL"
+
+"Insert R&ES (Counter/RTO Reset)\tE"
+"Inserire R&ES (Contatore/RTO Reset)\tE"
+
+"Insert MOV (Move)\tM"
+"Inserire MOV (Spostare)\tM"
+
+"Insert ADD (16-bit Integer Add)\t+"
+"Inserire ADD (Addizione intera 16-bit)\t+"
+
+"Insert SUB (16-bit Integer Subtract)\t-"
+"Inserire SUB (Sottrazione intera 16-bit)\t-"
+
+"Insert MUL (16-bit Integer Multiply)\t*"
+"Inserire MUL (Moltiplicazione intera 16-bit)\t*"
+
+"Insert DIV (16-bit Integer Divide)\tD"
+"Inserire DIV (Divisione intera 16-bit)\tD"
+
+"Insert Shift Register"
+"Inserire Shift Register"
+
+"Insert Look-Up Table"
+"Inserire Tavola Indicizzata"
+
+"Insert Piecewise Linear"
+"Inserire Tavola di Elementi Lineari"
+
+"Insert Formatted String Over UART"
+"Inserire Stringhe Formattate per l'UART"
+
+"Insert &UART Send"
+"Inserire Trasmissione &UART"
+
+"Insert &UART Receive"
+"Inserire Ricezione &UART"
+
+"Insert Set PWM Output"
+"Inserire Valore di Uscita PWM"
+
+"Insert A/D Converter Read\tP"
+"Inserire Lettura del Convertitore A/D\tP"
+
+"Insert Make Persistent"
+"Inserire Scrittura Permanente"
+
+"Make Norm&al\tA"
+"Attivazione Norm&ale\tA"
+
+"Make &Negated\tN"
+"Attivazione &Negata\tN"
+
+"Make &Set-Only\tS"
+"Attivazione &Solo-Set\tS"
+
+"Make &Reset-Only\tR"
+"Attivazione &Solo-Reset\tR"
+
+"&MCU Parameters..."
+"&Parametri MCU..."
+
+"(no microcontroller)"
+"(nessun microcontroller)"
+
+"&Microcontroller"
+"&Microcontroller"
+
+"Si&mulation Mode\tCtrl+M"
+"Modo Si&mulazione\tCtrl+M"
+
+"Start &Real-Time Simulation\tCtrl+R"
+"Avviare la &Simulazione in Tempo Reale\tCtrl+R"
+
+"&Halt Simulation\tCtrl+H"
+"&Arrestare la Simulazione\tCtrl+H"
+
+"Single &Cycle\tSpace"
+"Singolo &Ciclo\tSpazio"
+
+"&Compile\tF5"
+"&Compila\tF5"
+
+"Compile &As..."
+"Compila &Come..."
+
+"&Manual...\tF1"
+"&Manuale...\tF1"
+
+"&About..."
+"&About..."
+
+"&File"
+"&File"
+
+"&Edit"
+"&Editazione"
+
+"&Settings"
+"&Settaggi"
+
+"&Instruction"
+"&Istruzione"
+
+"Si&mulate"
+"Si&mulazione"
+
+"&Compile"
+"&Compilazione"
+
+"&Help"
+"&Aiuto"
+
+"no MCU selected"
+"nessuna MCU selezionata"
+
+"cycle time %.2f ms"
+"tempo ciclo %.2f ms"
+
+"processor clock %.4f MHz"
+"clock processore %.4f MHz"
+
+"Internal error relating to PIC paging; make program smaller or reshuffle it."
+"Errore interno relativo allla paginazione PIC; rendere il programma più piccolo."
+
+"PWM frequency too fast."
+"Frequenza PWM troppo alta."
+
+"PWM frequency too slow."
+"Frequenza PWM tropppo lenta."
+
+"Cycle time too fast; increase cycle time, or use faster crystal."
+"Tempo di ciclo troppo veloce; aumentare il tempo di ciclo o utilizzare un quarzo più veloce."
+
+"Cycle time too slow; decrease cycle time, or use slower crystal."
+"Tempo di ciclo troppo lento; diminuire il tempo di ciclo o utilizzare un quarzo più lento."
+
+"Couldn't open file '%s'"
+"Impossibile aprire il file '%s'"
+
+"Zero baud rate not possible."
+"baud rate = Zero non è possibile"
+
+"Compile successful; wrote IHEX for PIC16 to '%s'.\r\n\r\nConfiguration word (fuses) has been set for crystal oscillator, BOD enabled, LVP disabled, PWRT enabled, all code protection off.\r\n\r\nUsed %d/%d words of program flash (chip %d%% full)."
+"Compilato con successo; scritto IHEX per PIC16 per '%s'.\r\n\r\nConfigurazione word (fusibili), è stato fissato per il cristallo oscillatore, BOD abilitato, LVP disabilitati, PWRT attivato, tutto il codice di protezione off.\r\n\r\nUsed %d/%d word di Programma flash (chip %d%% full)."
+
+"Type"
+"Tipo"
+
+"Timer"
+"Temporizzazione"
+
+"Counter"
+"Contatore"
+
+"Reset"
+"Reset"
+
+"OK"
+"OK"
+
+"Cancel"
+"Cancellare"
+
+"Empty textbox; not permitted."
+"Spazio vuoto per testo; non è consentito"
+
+"Bad use of quotes: <%s>"
+"Quota utilizzata scorretta: <%s>"
+
+"Turn-On Delay"
+"Ritardo all' eccitazione"
+
+"Turn-Off Delay"
+"Ritardo alla diseccitazione"
+
+"Retentive Turn-On Delay"
+"Ritardo Ritentivo all' eccitazione"
+
+"Delay (ms):"
+"Ritardo (ms):"
+
+"Delay too long; maximum is 2**31 us."
+"Ritardo troppo lungo; massimo 2**31 us."
+
+"Delay cannot be zero or negative."
+"Ritardo zero o negativo non possibile."
+
+"Count Up"
+"Contatore in avanti"
+
+"Count Down"
+"Contatore in discesa"
+
+"Circular Counter"
+"Contatore ciclico"
+
+"Max value:"
+"Valore massimo:"
+
+"True if >= :"
+"Vero se >= :"
+
+"If Equals"
+"Se Uguale"
+
+"If Not Equals"
+"Se non Uguale"
+
+"If Greater Than"
+"Se più grande di"
+
+"If Greater Than or Equal To"
+"Se più grande o uguale di"
+
+"If Less Than"
+"Se più piccolo di"
+
+"If Less Than or Equal To"
+"Se più piccolo o uguale di"
+
+"'Closed' if:"
+"Chiuso se:"
+
+"Move"
+"Spostare"
+
+"Read A/D Converter"
+"Lettura del convertitore A/D"
+
+"Duty cycle var:"
+"Duty cycle var:"
+
+"Frequency (Hz):"
+"Frequenza (Hz):"
+
+"Set PWM Duty Cycle"
+"Settare PWM Duty Cycle"
+
+"Source:"
+"Sorgente:"
+
+"Receive from UART"
+"Ricezione dall' UART"
+
+"Send to UART"
+"Trasmissione all' UART"
+
+"Add"
+"Addizione"
+
+"Subtract"
+"Sottrazione"
+
+"Multiply"
+"Moltiplicazione"
+
+"Divide"
+"Divisione"
+
+"Destination:"
+"Destinazione:"
+
+"is set := :"
+"é impostato := :"
+
+"Name:"
+"Nome:"
+
+"Stages:"
+"Stati:"
+
+"Shift Register"
+"Registro a scorrimento"
+
+"Not a reasonable size for a shift register."
+"Non è una dimensione ragionevole per un registro scorrimento."
+
+"String:"
+"Stringhe:"
+
+"Formatted String Over UART"
+"Stringhe formattate per l'UART"
+
+"Variable:"
+"Variabile:"
+
+"Make Persistent"
+"Memoria rimanante"
+
+"Too many elements in subcircuit!"
+"Troppi elementi nel circuito secondario!"
+
+"Too many rungs!"
+"Troppi rami!"
+
+"Error"
+"Errore"
+
+"ANSI C target does not support peripherals (UART, PWM, ADC, EEPROM). Skipping that instruction."
+"Il codice in uscita ANSI C non supporta queste istruzioni (UART, PWM, ADC, EEPROM). Non usare queste istruzioni."
+
+"Compile successful; wrote C source code to '%s'.\r\n\r\nThis is not a complete C program. You have to provide the runtime and all the I/O routines. See the comments in the source code for information about how to do this."
+"Compilato con successo; scritto il codice sorgente in C '%s'.\r\n\r\nIl non è un completo programma in C. Si deve fornire il runtime e tutti gli I / O di routine. Vedere i commenti nel codice sorgente per informazioni su come fare."
+
+"Cannot delete rung; program must have at least one rung."
+"Non è in grado di eliminare le linee; il programma deve avere almeno una linea."
+
+"Out of memory; simplify program or choose microcontroller with more memory."
+"Memoria insufficiente; semplificare il programma oppure scegliere microcontrollori con più memoria ."
+
+"Must assign pins for all ADC inputs (name '%s')."
+"Devi assegnare i pin per tutti gli ingressi ADC (nom '%s')."
+
+"Internal limit exceeded (number of vars)"
+"Superato il limite interno (numero di variabili)"
+
+"Internal relay '%s' never assigned; add its coil somewhere."
+"Relè interno '%s' non assegnato; aggiungere la sua bobina."
+
+"Must assign pins for all I/O.\r\n\r\n'%s' is not assigned."
+"Devi assegnare tutti i pin di I / O.\r\n\r\n'%s' non è stato assegnato."
+
+"UART in use; pins %d and %d reserved for that."
+"UART in uso; pins %d et %d riservati per questa."
+
+"PWM in use; pin %d reserved for that."
+"PWM utilizzato; pin %d riservati per questo."
+
+"UART baud rate generator: divisor=%d actual=%.4f for %.2f%% error.\r\n\r\nThis 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\nCode will be generated anyways but serial may be unreliable or completely broken."
+"UART generatore di baud rate: divisore = %d effettivo = %.4f per %.2f%% di errore.\r\n\r\nIl è troppo grande; prova un altro baud rate (probabilmente più lento), o il quarzo scelto non è divisibile per molti comuni baud (ad esempio, 3.6864 MHz, 14.7456 MHz).\r\n\r\nIl codice verrà generato, ma comunque può essere inaffidabile o completamente non funzionante."
+
+"UART baud rate generator: too slow, divisor overflows. Use a slower crystal or a faster baud rate.\r\n\r\nCode will be generated anyways but serial will likely be completely broken."
+"UART generatore di baud rate: troppo lento, overflow. Utilizzare un quarzo più lento o una velocità più alta di baud.\r\n\r\nIl codice verrà generato, ma comunque sarà probabilmente completamente inutilizzabile."
+
+"Couldn't open '%s'\n"
+"Impossibile aprire '%s'\n"
+
+"Timer period too short (needs faster cycle time)."
+"Periodo troppo corto (Diminuire il tempo de ciclo)."
+
+"Timer period too long (max 32767 times cycle time); use a slower cycle time."
+"Tempo troppo lungo (max 32767 volte di ciclo); utilizzare un tempo di ciclo più lento."
+
+"Constant %d out of range: -32768 to 32767 inclusive."
+"Costante %d oltre il limite: -32768 a 32767 inclusi."
+
+"Move instruction: '%s' not a valid destination."
+"Sposta istruzione: '%s' non è una destinazione valida."
+
+"Math instruction: '%s' not a valid destination."
+"Math istruzione: '%s' non è una destinazione valida"
+
+"Piecewise linear lookup table with zero elements!"
+"La tabella lineare di ricerca non contiene elementi!"
+
+"x values in piecewise linear table must be strictly increasing."
+"X valori nella tabella lineare devono essere crescenti."
+
+"Numerical problem with piecewise linear lookup table. Either make the table entries smaller, or space the points together more closely.\r\n\r\nSee the help file for details."
+"Problema numerico con la tabella lineare di ricerca. Sia la tabella voci più piccole, o insieme di punti più vicini.\r\n\r\nVedere il file di aiuto per ulteriori dettagli."
+
+"Multiple escapes (\\0-9) present in format string, not allowed."
+"Uscita multipla (\\0-9) presente nel formato stringa, non consentito."
+
+"Bad escape: correct form is \\xAB."
+"Uscita errata: Forma non corretta = \\xAB."
+
+"Bad escape '\\%c'"
+"Uscita non corretta '\\%c'"
+
+"Variable is interpolated into formatted string, but none is specified."
+"Variabile interpolata in formato stringa, ma non è specificata."
+
+"No variable is interpolated into formatted string, but a variable name is specified. Include a string like '\\-3', or leave variable name blank."
+"Variabile interpolata non in formato stringa, ma un nome di variabile è stato specificato. Includi una stringa come '\\-3', o lasciare vuoto."
+
+"Empty row; delete it or add instructions before compiling."
+"Riga vuota, eliminare o aggiungere istruzioni prima di compilare."
+
+"Couldn't write to '%s'"
+"Impossibile scrivere qui '%s'."
+
+"Unsupported op (anything ADC, PWM, UART, EEPROM) for interpretable target."
+"Istruzioni non supportate (come ADC, PWM, UART, EEPROM ) per il codice interpretato."
+
+"Compile successful; wrote interpretable code to '%s'.\r\n\r\nYou probably have to adapt the interpreter to your application. See the documentation."
+"Compilazione riuscita; codice interpretabile scritto per '%s'.\r\n\r\nSi dovrà probabilmente adattare l'interprete per la vostra applicazione. Vedi la documentazione."
+
+"Microcontroller '%s' not supported.\r\n\r\nDefaulting to no selected MCU."
+"Microcontrollore '%s' non è supportato.\r\n\r\nNessuna MCU selezionata di default."
+
+"File format error; perhaps this program is for a newer version of LDmicro?"
+"Errore del formato del file, forse questo è il programma per una nuova versione di LDmicro?"
+
+"Index:"
+"Indice:"
+
+"Points:"
+"Punto:"
+
+"Count:"
+"Contatore:"
+
+"Edit table of ASCII values like a string"
+"Modifica tabella dei valori ASCII come stringa"
+
+"Look-Up Table"
+"Tabella indicizzata"
+
+"Piecewise Linear Table"
+"Tabella di elementi lineari"
+
+"LDmicro Error"
+"Errore LDmicro"
+
+"Compile Successful"
+"Compilato con successo"
+
+"digital in"
+"Input digitale"
+
+"digital out"
+"Uscita digitale"
+
+"int. relay"
+"Relè interno"
+
+"UART tx"
+"UART tx"
+
+"UART rx"
+"UART rx"
+
+"PWM out"
+"Uscita PWM"
+
+"turn-on delay"
+"ritardo all' eccitazione"
+
+"turn-off delay"
+"ritardo alla diseccitazione"
+
+"retentive timer"
+"ritardo ritentivo"
+
+"counter"
+"contatore"
+
+"general var"
+"Var. generale"
+
+"adc input"
+"Ingrasso ADC"
+
+""
+""
+
+"(not assigned)"
+"(non assegnato)"
+
+""
+""
+
+""
+""
+
+"TOF: variable cannot be used elsewhere"
+"TOF: la variabile non può essere utilizzata altrove"
+
+"TON: variable cannot be used elsewhere"
+"TON: la variabile non può essere utilizzata altrove"
+
+"RTO: variable can only be used for RES elsewhere"
+"RTO: la variabile può essere utilizzata altrove solo per il RES"
+
+"Variable '%s' not assigned to, e.g. with a MOV statement, an ADD statement, etc.\r\n\r\nThis is probably a programming error; now it will always be zero."
+"Variabile '%s' non assegnate, ad esempio, con una dichiarazione, MOV, una dichiarazione ADD, ecc\r\n\r\nQuesto è probabilmente un errore di programmazione; ora sarà sempre uguale a zero."
+
+"Variable for '%s' incorrectly assigned: %s."
+"Variabile per '%s' assegnazione incorretta: %s."
+
+"Division by zero; halting simulation"
+"Divisione per zero; Simulazione fermata"
+
+"!!!too long!!!"
+"!troppo lungo!"
+
+"\n\nI/O ASSIGNMENT:\n\n"
+"\n\nE/S ASSEGNAZIONE:\n\n"
+
+" Name | Type | Pin\n"
+" Nome | Type | Pin\n"
diff --git a/ldmicro/lang-make.pl b/ldmicro/lang-make.pl
new file mode 100644
index 0000000..f153c29
--- /dev/null
+++ b/ldmicro/lang-make.pl
@@ -0,0 +1,66 @@
+#!/usr/bin/perl
+
+$engl = 1;
+while(<>) {
+ chomp;
+
+ if(/^\s*$/) {
+ if($engl) {
+ next;
+ } else {
+ die "blank line mid-translation at $file, $.\n";
+ }
+ }
+
+ if($engl) {
+ $toTranslate = $_;
+ $engl = 0;
+ } else {
+ $translated = $_;
+
+ $toTranslate =~ s/\0/\\"/g;
+ $toTranslate =~ s#"##g;
+ $Already{$toTranslate} = 1;
+ $engl = 1;
+ }
+}
+
+# A tool to generate a list of strings to internationalize
+
+for $file (<*.cpp>) {
+ open(IN, $file) or die;
+ $source = join("", );
+ close IN;
+
+ $source =~ s/\\"/\0/g;
+
+ while($source =~ m#
+ _\( \s*
+ ((
+ \s* "
+ [^"]+
+ " \s*
+ )+)
+ \s*\)
+ #xsg) {
+ $str = $1;
+ $strIn = '';
+ while($str =~ m#\s*"([^"]+)"\s*#g) {
+ $strIn .= $1;
+ }
+ $Occurs{$strIn} = $c++;
+ $String{$strIn}++;
+ }
+}
+
+sub by_occurrence {
+ $Occurs{$a} <=> $Occurs{$b};
+}
+
+for (sort by_occurrence (keys %String)) {
+ if (not $Already{$_}) {
+ $_ =~ s/\0/\\"/g;
+ print qq{"$_"\n\n\n};
+ }
+}
+
diff --git a/ldmicro/lang-pt.txt b/ldmicro/lang-pt.txt
new file mode 100644
index 0000000..5c41558
--- /dev/null
+++ b/ldmicro/lang-pt.txt
@@ -0,0 +1,765 @@
+"Target frequency %d Hz, closest achievable is %d Hz (warning, >5%% error)."
+"Freqüência do Micro %d Hz, a melhor aproximação é %d Hz (aviso, >5%% error)."
+
+"Compile successful; wrote IHEX for AVR to '%s'.\r\n\r\nRemember to set the processor configuration (fuses) correctly. This does not happen automatically."
+"Compilação sucedida; se escrito em IHEX para AVR para '%s'.\r\n\r\nLembrar de anotar as configurações (fuses) do micro, corretamente. Isto não acontece automaticamente."
+
+"( ) Normal"
+"( ) Normal"
+
+"(/) Negated"
+"(/) Negado"
+
+"(S) Set-Only"
+"(S) Ativar"
+
+"(R) Reset-Only"
+"(R) Desativar"
+
+"Pin on MCU"
+"Pino no Micro"
+
+"Coil"
+"Bobina"
+
+"Comment"
+"Comentário"
+
+"Cycle Time (ms):"
+"Tempo de Ciclo (ms):"
+
+"Crystal Frequency (MHz):"
+"Freqüência Cristal (MHz):"
+
+"UART Baud Rate (bps):"
+"Baud Rate UART (bps):"
+
+"Serie (UART) will use pins %d and %d.\r\n\r\n"
+"Porta Serial (UART) usará os pinos %d e %d.\r\n\r\n"
+
+"Please select a micro with a UART.\r\n\r\n"
+"Por favor, selecione um micro com UART.\r\n\r\n"
+
+"No serial instructions (UART Send/UART Receive) are in use; add one to program before setting baud rate.\r\n\r\n"
+"Nenhuma instrução serial (UART Enviar/UART Recebe) está em uso; adicione uma ao programa antes de configurar a taxa de transmissão (baud rate).\r\n\r\n"
+
+"The cycle time for the 'PLC' runtime generated by LDmicro is user-configurable. Very short cycle times may not be achievable due to processor speed constraints, and very long cycle times may not be achievable due to hardware overflows. Cycle times between 10 ms and 100 ms will usually be practical.\r\n\r\nThe compiler must know what speed crystal you are using with the micro to convert between timing in clock cycles and timing in seconds. A 4 MHz to 20 MHz crystal is typical; check the speed grade of the part you are using to determine the maximum allowable clock speed before choosing a crystal."
+"O tempo de ciclo para o PLC generalizado pelo LDmicro é configuravel pelo usuário. Tempos de ciclo muito pequenos podem não ser realizáveis devido a baixa velocidade do processador, e tempos de ciclo muito longos podem não ser realizáveis devido ao temporizador do micro. Ciclos de tempo entre 10 e 100ms são mais usuais.\r\n\r\n O compilador tem que saber qual é a freqüência do cristal que está sendo usado para poder converter entre o tempo em ciclos do clock e tempo em segundos. Cristais entre 4 Mhz e 20 Mhz são os mais típicos. Confira a velocidade que pode funcionar seu micro e calcule a velocidade máxima do clock antes de encolher o cristal."
+
+"PLC Configuration"
+"Configuração PLC"
+
+"Zero cycle time not valid; resetting to 10 ms."
+"Não é válido um tempo de ciclo 0; retornando a 10 ms."
+
+"Source"
+"Fonte"
+
+"Internal Relay"
+"Rele Interno"
+
+"Input pin"
+"Pino Entrada"
+
+"Output pin"
+"Pino Saida"
+
+"|/| Negated"
+"|/| Negado"
+
+"Contacts"
+"Contatos"
+
+"No ADC or ADC not supported for selected micro."
+"O micro selecionado não possui ADC ou não suporta."
+
+"Assign:"
+"Atribua:"
+
+"No microcontroller has been selected. You must select a microcontroller before you can assign I/O pins.\r\n\r\nSelect a microcontroller under the Settings menu and try again."
+"Nenhum micro esta selecionado. Você deve selecionar um micro antes de atribuir os pinos E/S.\r\n\r\nSelecione um micro no menu de configuração e tente novamente."
+
+"I/O Pin Assignment"
+"Atribuição de Pinos de E/S"
+
+"Can't specify I/O assignment for ANSI C target; compile and see comments in generated source code."
+"Não se pode atribuir as E/S especificadas para o ANSI C gerado; compile e veja os comentários gerados no código fonte."
+
+"Can't specify I/O assignment for interpretable target; see comments in reference implementation of interpreter."
+"Não se pode atribuir as E/S especificadas para o código gerado para o interpretador; veja os comentários na implementação do interpretador."
+
+"Can only assign pin number to input/output pins (Xname or Yname or Aname)."
+"Somente pode atribuir números dos pinos aos pinos de Entrada/Saída (Xname ou Yname ou Aname)."
+
+"No ADC or ADC not supported for this micro."
+"Este micro não tem ADC ou não é suportado."
+
+"Rename I/O from default name ('%s') before assigning MCU pin."
+"Renomear as E/S para nome padrão ('%s') antes de atribuir um pino do micro."
+
+"I/O Pin"
+"Pino E/S"
+
+"(no pin)"
+"(sem pino)"
+
+""
+""
+
+""
+""
+
+""
+""
+
+"Export As Text"
+"Exportar como Texto"
+
+"Couldn't write to '%s'."
+"Não pode gravar para '%s'."
+
+"Compile To"
+"Compilar Para"
+
+"Must choose a target microcontroller before compiling."
+"Deve selecionar um microcontrolador antes de compilar."
+
+"UART function used but not supported for this micro."
+"Função UART é usada porem não suportada por este micro."
+
+"PWM function used but not supported for this micro."
+"Função PWM é usada porem não suportada por este micro."
+
+"The program has changed since it was last saved.\r\n\r\nDo you want to save the changes?"
+"Este programa contem mudanças desde a ultima vez salva.\r\n\r\n Você quer salvar as mudanças?"
+
+"--add comment here--"
+"--Adicione Comentários Aqui--"
+
+"Start new program?"
+"Iniciar um novo programa?"
+
+"Couldn't open '%s'."
+"Não pode abrir '%s'."
+
+"Name"
+"Nome"
+
+"State"
+"Estado"
+
+"Pin on Processor"
+"Pino do Processador"
+
+"MCU Port"
+"Porta do Micro"
+
+"LDmicro - Simulation (Running)"
+"LDmicro - Simulação (Executando)"
+
+"LDmicro - Simulation (Stopped)"
+"LDmicro - Simulação (Parado)"
+
+"LDmicro - Program Editor"
+"LDmicro - Editor de Programa"
+
+" - (not yet saved)"
+" - (ainda não salvo)"
+
+"&New\tCtrl+N"
+"&Novo\tCtrl+N"
+
+"&Open...\tCtrl+O"
+"&Abrir...\tCtrl+O"
+
+"&Save\tCtrl+S"
+"&Salvar\tCtrl+S"
+
+"Save &As..."
+"Salvar &Como..."
+
+"&Export As Text...\tCtrl+E"
+"&Exportar como Texto...\tCtrl+E"
+
+"E&xit"
+"&Sair"
+
+"&Undo\tCtrl+Z"
+"&Desfazer\tCtrl+Z"
+
+"&Redo\tCtrl+Y"
+"&Refazer\tCtrl+Y"
+
+"Insert Rung &Before\tShift+6"
+"Inserir Linha (Rung) &Antes\tShift+6"
+
+"Insert Rung &After\tShift+V"
+"Inserir Linha (Rung) &Depois\tShift+V"
+
+"Move Selected Rung &Up\tShift+Up"
+"Mover Linha Selecionada (Rung) &Acima\tShift+Up"
+
+"Move Selected Rung &Down\tShift+Down"
+"Mover Linha Selecionada(Rung) &Abaixo\tShift+Down"
+
+"&Delete Selected Element\tDel"
+"&Apagar Elemento Selecionado\tDel"
+
+"D&elete Rung\tShift+Del"
+"A&pagar Linha (Rung) \tShift+Del"
+
+"Insert Co&mment\t;"
+"Inserir Co&mentário\t;"
+
+"Insert &Contacts\tC"
+"Inserir &Contatos\tC"
+
+"Insert OSR (One Shot Rising)\t&/"
+"Inserir OSR (Detecta Borda de Subida)\t&/"
+
+"Insert OSF (One Shot Falling)\t&\\"
+"Inserir OSF (Detecta Borda de Descida)\t&\\"
+
+"Insert T&ON (Delayed Turn On)\tO"
+"Inserir T&ON (Temporizador para Ligar)\tO"
+
+"Insert TO&F (Delayed Turn Off)\tF"
+"Inserir TO&F (Temporizador para Desligar)\tF"
+
+"Insert R&TO (Retentive Delayed Turn On)\tT"
+"Inserir R&TO (Temporizar Retentivo para Ligar)\tT"
+
+"Insert CT&U (Count Up)\tU"
+"Inserir CT&U (Contador Incremental)\tU"
+
+"Insert CT&D (Count Down)\tI"
+"Inserir CT&D (Contador Decremental)\tI"
+
+"Insert CT&C (Count Circular)\tJ"
+"Inserir CT&C (Contador Circular)\tJ"
+
+"Insert EQU (Compare for Equals)\t="
+"Inserir EQU (Comparar se é Igual)\t="
+
+"Insert NEQ (Compare for Not Equals)"
+"Inserir NEQ (Comparar se é Diferente)"
+
+"Insert GRT (Compare for Greater Than)\t>"
+"Inserir GRT (Comparar se Maior Que)\t>"
+
+"Insert GEQ (Compare for Greater Than or Equal)\t."
+"Inserir GEQ (Comparar se Maior ou Igual Que)\t."
+
+"Insert LES (Compare for Less Than)\t<"
+"Inserir LES (Comparar se Menor Que)\t<"
+
+"Insert LEQ (Compare for Less Than or Equal)\t,"
+"Inserir LEQ (Comparar se Menor ou Igual Que)\t,"
+
+"Insert Open-Circuit"
+"Inserir Circuito Aberto"
+
+"Insert Short-Circuit"
+"Inserir Curto Circuito"
+
+"Insert Master Control Relay"
+"Inserir Rele de Controle Mestre"
+
+"Insert Coi&l\tL"
+"Inserir &Bobina\tL"
+
+"Insert R&ES (Counter/RTO Reset)\tE"
+"Inserir R&ES (Contador/RTO Reinicia)\tE"
+
+"Insert MOV (Move)\tM"
+"Inserir MOV (Mover)\tM"
+
+"Insert ADD (16-bit Integer Add)\t+"
+"Inserir ADD (Soma Inteiro 16-bit)\t+"
+
+"Insert SUB (16-bit Integer Subtract)\t-"
+"Inserir SUB (Subtrair Inteiro 16-bit)\t-"
+
+"Insert MUL (16-bit Integer Multiply)\t*"
+"Inserir MUL (Multiplica Inteiro 16-bit)\t*"
+
+"Insert DIV (16-bit Integer Divide)\tD"
+"Inserir DIV (Divide Inteiro 16-bit)\tD"
+
+"Insert Shift Register"
+"Inserir Registro de Troca"
+
+"Insert Look-Up Table"
+"Inserir Tabela de Busca"
+
+"Insert Piecewise Linear"
+"Inserir Linearização por Segmentos"
+
+"Insert Formatted String Over UART"
+"Inserir String Formatada na UART"
+
+"Insert &UART Send"
+"Inserir &UART Enviar"
+
+"Insert &UART Receive"
+"Inserir &UART Receber"
+
+"Insert Set PWM Output"
+"Inserir Valor de Saída PWM"
+
+"Insert A/D Converter Read\tP"
+"Inserir Leitura do Conversor A/D\tP"
+
+"Insert Make Persistent"
+"Inserir Fazer Permanente"
+
+"Make Norm&al\tA"
+"Fazer Norm&al\tA"
+
+"Make &Negated\tN"
+"Fazer &Negado\tN"
+
+"Make &Set-Only\tS"
+"Fazer &Ativar-Somente\tS"
+
+"Make &Reset-Only\tR"
+"Fazer&Desativar-Somente\tR"
+
+"&MCU Parameters..."
+"&Parâmetros do Micro..."
+
+"(no microcontroller)"
+"(sem microcontrolador)"
+
+"&Microcontroller"
+"&Microcontrolador"
+
+"Si&mulation Mode\tCtrl+M"
+"Modo Si&mulação \tCtrl+M"
+
+"Start &Real-Time Simulation\tCtrl+R"
+"Iniciar Simulação em Tempo &Real\tCtrl+R"
+
+"&Halt Simulation\tCtrl+H"
+"Parar Simulação\tCtrl+H"
+
+"Single &Cycle\tSpace"
+"Simples &Ciclo\tEspaço"
+
+"&Compile\tF5"
+"&Compilar\tF5"
+
+"Compile &As..."
+"Compilar &Como..."
+
+"&Manual...\tF1"
+"&Manual...\tF1"
+
+"&About..."
+"&Sobre..."
+
+"&File"
+"&Arquivo"
+
+"&Edit"
+"&Editar"
+
+"&Settings"
+"&Configurações"
+
+"&Instruction"
+"&Instruções"
+
+"Si&mulate"
+"Si&mular"
+
+"&Compile"
+"&Compilar"
+
+"&Help"
+"&Ajuda"
+
+"no MCU selected"
+"Micro não selecionado"
+
+"cycle time %.2f ms"
+"tempo de ciclo %.2f ms"
+
+"processor clock %.4f MHz"
+"clock do processador %.4f MHz"
+
+"Internal error relating to PIC paging; make program smaller or reshuffle it."
+"Erro interno relativo a paginação do PIC; fazer um programa menor ou reorganiza-lo"
+
+"PWM frequency too fast."
+"Freqüência do PWM muito alta."
+
+"PWM frequency too slow."
+"Freqüência do PWM muito baixa."
+
+"Cycle time too fast; increase cycle time, or use faster crystal."
+"Tempo de Ciclo muito rápido; aumentar tempo do ciclo, ou usar um cristal de maior Mhz."
+
+"Cycle time too slow; decrease cycle time, or use slower crystal."
+"Tempo de Ciclo muito lento; diminuir tempo do ciclo, ou usar um cristal de menor Mhz."
+
+"Couldn't open file '%s'"
+"Não pode abrir o arquivo '%s'"
+
+"Zero baud rate not possible."
+"Zero Baud Rate não é possível."
+
+"Compile successful; wrote IHEX for PIC16 to '%s'.\r\n\r\nConfiguration word (fuses) has been set for crystal oscillator, BOD enabled, LVP disabled, PWRT enabled, all code protection off.\r\n\r\nUsed %d/%d words of program flash (chip %d%% full)."
+"Compilação sucedida; escrito IHEX para PIC16 em '%s'.\r\n\r\nBits de Configuração (fuses) foi estabelecido para o cristal oscilador, BOD ativado, LVP desativado, PWRT ativado, Todos os bits de proteção desativados.\r\n\r\nUsadas %d/%d palavras de programa em flash (Chip %d%% completo)."
+
+"Type"
+"Tipo"
+
+"Timer"
+"Temporizador"
+
+"Counter"
+"Contador"
+
+"Reset"
+"Reiniciar"
+
+"OK"
+"OK"
+
+"Cancel"
+"Cancelar"
+
+"Empty textbox; not permitted."
+"Texto vazio; não é permitido"
+
+"Bad use of quotes: <%s>"
+"Mau uso das aspas: <%s>"
+
+"Turn-On Delay"
+"Temporizador para Ligar"
+
+"Turn-Off Delay"
+"Temporizador para Desligar"
+
+"Retentive Turn-On Delay"
+"Temporizador Retentivo para Ligar"
+
+"Delay (ms):"
+"Tempo (ms):"
+
+"Delay too long; maximum is 2**31 us."
+"Tempo muito longo; Maximo 2**31 us."
+
+"Delay cannot be zero or negative."
+"Tempo não pode ser zero ou negativo."
+
+"Count Up"
+"Contador Crescente"
+
+"Count Down"
+"Contador Decrescente"
+
+"Circular Counter"
+"Contador Circular"
+
+"Max value:"
+"Valor Max:"
+
+"True if >= :"
+"Verdadeiro se >= :"
+
+"If Equals"
+"Se Igual"
+
+"If Not Equals"
+" Se Diferente"
+
+"If Greater Than"
+"Se Maior Que"
+
+"If Greater Than or Equal To"
+"Se Maior ou Igual Que"
+
+"If Less Than"
+"Se Menor Que"
+
+"If Less Than or Equal To"
+"Se Menor ou Igual Que"
+
+"'Closed' if:"
+"'Fechado' se:"
+
+"Move"
+"Mover"
+
+"Read A/D Converter"
+"Ler Conversor A/D"
+
+"Duty cycle var:"
+"Var Duty cycle:"
+
+"Frequency (Hz):"
+"Frequencia (Hz):"
+
+"Set PWM Duty Cycle"
+"Acionar PWM Duty Cycle"
+
+"Source:"
+"Fonte:"
+
+"Receive from UART"
+"Recebe da UART"
+
+"Send to UART"
+"Envia para UART"
+
+"Add"
+"Somar"
+
+"Subtract"
+"Subtrair"
+
+"Multiply"
+"Multiplicar"
+
+"Divide"
+"Dividir"
+
+"Destination:"
+"Destino:"
+
+"is set := :"
+"esta acionado := :"
+
+"Name:"
+"Nome:"
+
+"Stages:"
+"Fases:"
+
+"Shift Register"
+"Deslocar Registro"
+
+"Not a reasonable size for a shift register."
+"Não é um tamanho razoável para um registro de deslocamento."
+
+"String:"
+"String:"
+
+"Formatted String Over UART"
+"String Formatada para UART"
+
+"Variable:"
+"Variavel:"
+
+"Make Persistent"
+"Fazer Permanente"
+
+"Too many elements in subcircuit!"
+"Excesso de elementos no SubCircuito!"
+
+"Too many rungs!"
+"Muitas Linhas (rungs)!"
+
+"Error"
+"Error"
+
+"ANSI C target does not support peripherals (UART, PWM, ADC, EEPROM). Skipping that instruction."
+"ANSI C não suporta periféricos (UART, PWM, ADC, EEPROM). Evite essas instruções."
+
+"Compile successful; wrote C source code to '%s'.\r\n\r\nThis is not a complete C program. You have to provide the runtime and all the I/O routines. See the comments in the source code for information about how to do this."
+"Compilação sucedida: Código Fonte escrito em C para '%s'.\r\n\r\nEste programa nao é completo em C. Você tem que fornecer o tempo de execução e de todas as rotinas de E/S. Veja os comentários no código fonte para mais informação sobre como fazer isto."
+
+"Cannot delete rung; program must have at least one rung."
+"Não pode apagar a Linha (rung); O programa deve contem pelo menos uma Linha (rung)."
+
+"Out of memory; simplify program or choose microcontroller with more memory."
+"Fora de Memória; Simplifique o programa ou escolha um microcontrolador com mais memória."
+
+"Must assign pins for all ADC inputs (name '%s')."
+"Deve associar pinos para todas as entradas do ADC (nome '%s')."
+
+"Internal limit exceeded (number of vars)"
+"Limite interno excedido (numero de variáveis)"
+
+"Internal relay '%s' never assigned; add its coil somewhere."
+"Rele Interno'%s' não foi associado; adicione esta bobina em outro lugar."
+
+"Must assign pins for all I/O.\r\n\r\n'%s' is not assigned."
+"Deve associar pinos a todas E/S.\r\n\r\n'%s' não esta associado."
+
+"UART in use; pins %d and %d reserved for that."
+"UART em uso; pinos %d e %d reservado para isso."
+
+"PWM in use; pin %d reserved for that."
+"PWM em uso; pino %d reservada para isso."
+
+"UART baud rate generator: divisor=%d actual=%.4f for %.2f%% error.\r\n\r\nThis 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\nCode will be generated anyways but serial may be unreliable or completely broken."
+"Gerador UART baud rate: divisor=%d atual=%.4f para %.2f%% error.\r\n\r\nEste é muito grande; Tente com outro valor (provavelmente menor), ou um cristal cuja freqüência seja divisível pelos baud rate mais comuns (p.e. 3.6864MHz, 14.7456MHz).\r\n\r\nO código será gerado de qualquer maneira, porem a serial poderá ser incerta ou completamente fragmentada."
+
+"UART baud rate generator: too slow, divisor overflows. Use a slower crystal or a faster baud rate.\r\n\r\nCode will be generated anyways but serial will likely be completely broken."
+"Gerador UART baud rate: muito lento, divisor excedido. Use um cristal mais lento ou um baud rate maior.\r\n\r\nO código será gerado de qualquer maneira, porem a serial poderá ser incerta ou completamente fragmentada.."
+
+"Couldn't open '%s'\n"
+"Não pode abrir '%s'\n"
+
+"Timer period too short (needs faster cycle time)."
+"Período de Tempo muito curto (necessitara de um tempo de ciclo maior)."
+
+"Timer period too long (max 32767 times cycle time); use a slower cycle time."
+"Tempo do Temporizador muito grande(max. 32767 tempo de ciclo); use um tempo de ciclo menor."
+
+"Constant %d out of range: -32768 to 32767 inclusive."
+"Constante %d fora do range: -32768 a 32767 inclusive."
+
+"Move instruction: '%s' not a valid destination."
+"Instrução Mover: '%s' o destino não é válido."
+
+"Math instruction: '%s' not a valid destination."
+"Instruções Math: '%s' o destino não é válido."
+
+"Piecewise linear lookup table with zero elements!"
+"Consulta da Tabela de Linearização por Segmentos com elementos zero!"
+
+"x values in piecewise linear table must be strictly increasing."
+"Os valores X na Tabela de Linearização por Segmentos deve ser estritamente incrementais."
+
+"Numerical problem with piecewise linear lookup table. Either make the table entries smaller, or space the points together more closely.\r\n\r\nSee the help file for details."
+"Problema numérico com a Tabela de Linearização por segmentos. Faça qualquer tabela com entradas menores. ou espace os pontos para mais pròximo.\r\n\r\nVeja em ajuda para mais detalhes."
+
+"Multiple escapes (\\0-9) present in format string, not allowed."
+"Não é permitido mais de um caractere especial (\\0-9) dentro da string formatada."
+
+"Bad escape: correct form is \\xAB."
+"Caractere Especial com Erro: A forma correta é \\xAB."
+
+"Bad escape '\\%c'"
+"Caractere Especial com Erro '\\%c'"
+
+"Variable is interpolated into formatted string, but none is specified."
+"A variável é interpolada dentro da string formatada, mas nenhuma é especificado."
+
+"No variable is interpolated into formatted string, but a variable name is specified. Include a string like '\\-3', or leave variable name blank."
+"Nenhuma variável esta interpolada dentro da string formatada, porem um nome de variável é especificada. Inclua uma string como '\\-3', ou deixe o nome da variável em branco."
+
+"Empty row; delete it or add instructions before compiling."
+"Linha Vazia; apague ou adicione instruções antes de compilar."
+
+"Couldn't write to '%s'"
+"Não pode ser gravado para '%s'."
+
+"Unsupported op (anything ADC, PWM, UART, EEPROM) for interpretable target."
+"Op não suportada no interpretador (algum ADC, PWM, UART, EEPROM)."
+
+"Compile successful; wrote interpretable code to '%s'.\r\n\r\nYou probably have to adapt the interpreter to your application. See the documentation."
+"Compilação sucedida: Código para interpretador escrito para '%s'.\r\n\r\nVocê provavelmente tem que adaptar o interpretador para sua aplicação. Veja a documentação."
+
+"Microcontroller '%s' not supported.\r\n\r\nDefaulting to no selected MCU."
+"Microcontrolador '%s' não suportado.\r\n\r\nFalha nenhum Micro selecionado."
+
+"File format error; perhaps this program is for a newer version of LDmicro?"
+"Erro no formato de arquivo; talvez este programa é para uma versão mais nova do LDmicro?."
+
+"Index:"
+"Índice:"
+
+"Points:"
+"Pontos:"
+
+"Count:"
+"Contador:"
+
+"Edit table of ASCII values like a string"
+"Editar tabela do ASCII, valores como uma string"
+
+"Look-Up Table"
+"Buscar na Tabela"
+
+"Piecewise Linear Table"
+"Tabela de Linearização por Segmentos"
+
+"LDmicro Error"
+"LDmicro Error"
+
+"Compile Successful"
+"Compilação Sucedida"
+
+"digital in"
+"entrada digital"
+
+"digital out"
+"saída digital"
+
+"int. relay"
+"rele interno"
+
+"UART tx"
+"UART tx"
+
+"UART rx"
+"UART rx"
+
+"PWM out"
+"saída PWM"
+
+"turn-on delay"
+"ativar atraso"
+
+"turn-off delay"
+"desativar atraso"
+
+"retentive timer"
+"temporizador com memória"
+
+"counter"
+"contador"
+
+"general var"
+"var geral"
+
+"adc input"
+"entrada adc"
+
+""
+""
+
+"(not assigned)"
+"(sem atribuição)"
+
+""
+""
+
+""
+""
+
+"TOF: variable cannot be used elsewhere"
+"TOF: a variável não pode ser usada em outra parte"
+
+"TON: variable cannot be used elsewhere"
+"TON: a variável não pode ser usada em outra parte"
+
+"RTO: variable can only be used for RES elsewhere"
+"RTO: a variável somente pode ser usada como RES em outra parte"
+
+"Variable '%s' not assigned to, e.g. with a MOV statement, an ADD statement, etc.\r\n\r\nThis is probably a programming error; now it will always be zero."
+"variável '%s' não atribuída, p.e. com o comando MOV, comando ADD, etc.\r\n\r\nIsto é provavelmente um erro de programação; será sempre zero."
+
+"Variable for '%s' incorrectly assigned: %s."
+"Variável para '%s' atribuída incorretamente : %s."
+
+"Division by zero; halting simulation"
+"Divisão por zero; Parando simulação"
+
+"!!!too long!!!"
+"!Muito longo!"
+
+"\n\nI/O ASSIGNMENT:\n\n"
+"\n\nE/S ATRIBUIDA:\n\n"
+
+" Name | Type | Pin\n"
+" Nome | Tipo | Pino\n"
+
+"Serial (UART) will use pins %d and %d.\r\n\r\n"
+"A porta Serial (UART) usará os pinos %d e %d.\r\n\r\n"
+
diff --git a/ldmicro/lang-tables.pl b/ldmicro/lang-tables.pl
new file mode 100644
index 0000000..8b37df8
--- /dev/null
+++ b/ldmicro/lang-tables.pl
@@ -0,0 +1,52 @@
+#!/usr/bin/perl
+
+$t = '';
+
+for $file (sort ) {
+ open(IN, $file);
+
+ $name = $file;
+ $name =~ s#lang-##;
+ $name =~ s#(.)#uc($1)#e;
+ $name =~ s#\.txt##;
+ $nameUc = uc($name);
+
+ print "#ifdef LDLANG_$nameUc\n";
+ print "static LangTable Lang${name}Table[] = {\n";
+
+ $engl = 1;
+ $. = 0;
+ while() {
+ chomp;
+
+ if(/^\s*$/) {
+ if($engl) {
+ next;
+ } else {
+ die "blank line mid-translation at $file, $.\n";
+ }
+ }
+
+ if($engl) {
+ $toTranslate = $_;
+ $engl = 0;
+ } else {
+ $translated = $_;
+
+ print " { $toTranslate, $translated },\n";
+ $engl = 1;
+ }
+ }
+
+ print "};\n";
+
+ print <5%% error)."
+"Hedef frekans %d Hz, bu deðere en yakýn olasý deðer %d Hz (Uyarý, >5% hatasý)."
+
+"Compile successful; wrote IHEX for AVR to '%s'.\r\n\r\nRemember to set the processor configuration (fuses) correctly. This does not happen automatically."
+"Derleme baþarýlý. AVR için derlenen '%s' IHEX dosyasýna kaydedildi.\r\n\r\n MÝB konfigürasyonunu ayarlamayý unutmayýn. Bu iþlem otomatik olarak yapýlmamaktadýr."
+
+"( ) Normal"
+"( ) Normal"
+
+"(/) Negated"
+"(/) Terslenmiþ"
+
+"(S) Set-Only"
+"(S) Set"
+
+"(R) Reset-Only"
+"(R) Reset"
+
+"Pin on MCU"
+"MÝB Bacaðý"
+
+"Coil"
+"Bobin"
+
+"Comment"
+"Açýklama"
+
+"Cycle Time (ms):"
+"Çevrim Süresi (ms):"
+
+"Crystal Frequency (MHz):"
+"Kristal Frekansý (MHz):"
+
+"UART Baud Rate (bps):"
+"UART Baud Rate (bps):"
+
+"Serial (UART) will use pins %d and %d.\r\n\r\n"
+"Seri iletiþim için (UART) %d ve %d bacaklarý kullanýlacaktýr.\r\n\r\n"
+
+"Please select a micro with a UART.\r\n\r\n"
+"Lütfen UART olan bir MÝB seçiniz.\r\n\r\n"
+
+"No serial instructions (UART Send/UART Receive) are in use; add one to program before setting baud rate.\r\n\r\n"
+"Seri iletiþim komutu kullanmadýnýz. Hýzý ayarlamadan önce komut kullanmalýsýnýz.\r\n\r\n"
+
+"The cycle time for the 'PLC' runtime generated by LDmicro is user-configurable. Very short cycle times may not be achievable due to processor speed constraints, and very long cycle times may not be achievable due to hardware overflows. Cycle times between 10 ms and 100 ms will usually be practical.\r\n\r\nThe compiler must know what speed crystal you are using with the micro to convert between timing in clock cycles and timing in seconds. A 4 MHz to 20 MHz crystal is typical; check the speed grade of the part you are using to determine the maximum allowable clock speed before choosing a crystal."
+"PLC için LDMicro tarafýndan verilen çevrim süresi kullanýcý tarafýndan deðiþtirilebilir. Çok kýsa süreler iþlemcinin hýzýndan kaynaklanan sebeplerden dolayý verilemeyebilir. 10ms ile 100ms arasýndaki deðerler çevrim süresi için genellikle uygun deðerlerdir.\r\n\r\n.LDMicro kullanýlan kristalin hýzýný bilmelidir. Bu deðer program içerisinde kullanýlýr. Bilindiði üzere genellikle 4MHz ile 20MHz arasýnda deðere sahip kristaller kullanýlmaktadýr. Kullandýðýnýz kristalin deðerini ayarlamalýsýnýz."
+
+"PLC Configuration"
+"PLC Ayarlarý"
+
+"Zero cycle time not valid; resetting to 10 ms."
+"Çevrim süresi sýfýr olamaz. 10ms olarak deðiþtirildi."
+
+"Source"
+"Kaynak"
+
+"Internal Relay"
+"Dahili Röle"
+
+"Input pin"
+"Giriþ Bacaðý"
+
+"Output pin"
+"Çýkýþ Bacaðý"
+
+"|/| Negated"
+"|/| Terslenmiþ"
+
+"Contacts"
+"Kontak"
+
+"No ADC or ADC not supported for selected micro."
+"Bu iþlemcide ADC yok yada ADC desteklenmiyor."
+
+"Assign:"
+"Deðeri:"
+
+"No microcontroller has been selected. You must select a microcontroller before you can assign I/O pins.\r\n\r\nSelect a microcontroller under the Settings menu and try again."
+"Ýþlemci seçmediniz.\r\n\r\n G/Ç uçlarýný seçmeden önce Ayarlar menüsünden iþlemci seçiniz."
+
+"I/O Pin Assignment"
+"G/Ç Bacak Tanýmý"
+
+"Can't specify I/O assignment for ANSI C target; compile and see comments in generated source code."
+"ANSI C hedef için G/Ç tanýmlamasý yapýlamadý. Dosyayý derleyerek oluþturulan kaynak kodundaki yorumlarý inceleyiniz."
+
+"Can't specify I/O assignment for interpretable target; see comments in reference implementation of interpreter."
+"Hedef için G/Ç tanýmý yapýlamadý. Derleyicinin kullaným kitapçýðýný inceleyiniz."
+
+"Can only assign pin number to input/output pins (Xname or Yname or Aname)."
+"Sadece giriþ/çýkýþ uçlarý için bacak tanýmlamasý yapýlabilir. (XName veya YName veya AName)."
+
+"No ADC or ADC not supported for this micro."
+"Bu iþlemcide ADC yok yada ADC desteklenmiyor."
+
+"Rename I/O from default name ('%s') before assigning MCU pin."
+"('%s') öntanýmlý isimdir. MÝB bacaðý tanýmlamadan önce bu ismi deðiþtiriniz."
+
+"I/O Pin"
+"G/Ç Bacaklarý"
+
+"(no pin)"
+"(Bacak Yok)"
+
+""
+""
+
+""
+""
+
+""
+""
+
+"Export As Text"
+"Yazý dosyasý olarak ver"
+
+"Couldn't write to '%s'."
+"'%s' dosyasýna yazýlamadý."
+
+"Compile To"
+"Derlenecek Yer"
+
+"Must choose a target microcontroller before compiling."
+"Derlemeden önce iþlemci seçilmelidir."
+
+"UART function used but not supported for this micro."
+"Komut kullanmýþsýnýz, ancak UART iþlemleri bu iþlemci için desteklenmiyor."
+
+"PWM function used but not supported for this micro."
+"Komut kullanmýþsýnýz, ancak PWM iþlemleri bu iþlemci için desteklenmiyor."
+
+"The program has changed since it was last saved.\r\n\r\nDo you want to save the changes?"
+"Dosyada deðiþiklik yaptýnýz.\r\n\r\n Kaydetmek ister misiniz?"
+
+"--add comment here--"
+"--aciklamanizi buraya ekleyiniz--"
+
+"Start new program?"
+"Yeni dosya oluþturulsun mu?"
+
+"Couldn't open '%s'."
+"'%s' dosyasýna kayýt yapýlamýyor."
+
+"Name"
+"Ýsim"
+
+"State"
+"Durum/Deðer"
+
+"Pin on Processor"
+"Ýþlemci Bacak No"
+
+"MCU Port"
+"Ýþlemci Portu"
+
+"LDmicro - Simulation (Running)"
+"LDmicro - Simülasyon (Çalýþýyor)"
+
+"LDmicro - Simulation (Stopped)"
+"LDmicro - Simülasyon (Durduruldu)"
+
+"LDmicro - Program Editor"
+"LDmicro – Program Editörü "
+
+" - (not yet saved)"
+" - (deðiþiklikler kaydedilmedi)"
+
+"&New\tCtrl+N"
+"&Yeni Dosya\tCtrl+N"
+
+"&Open...\tCtrl+O"
+"&Dosya Aç\tCtrl+O"
+
+"&Save\tCtrl+S"
+"&Dosyayý Kaydet\tCtrl+S"
+
+"Save &As..."
+"Dosyayý Farklý Kaydet"
+
+"&Export As Text...\tCtrl+E"
+"&Metin Dosyasý Olarak Kaydet\tCtrl+E"
+
+"E&xit"
+"Çýkýþ"
+
+"&Undo\tCtrl+Z"
+"Deðiþikliði Geri Al\tCtrl+Z"
+
+"&Redo\tCtrl+Y"
+"&Deðiþikliði Tekrarla\tCtrl+Y"
+
+"Insert Rung &Before\tShift+6"
+"Üst kýsma satýr (Rung) ekle\tShift+6"
+
+"Insert Rung &After\tShift+V"
+"Alt kýsma satýr (Rung) ekle\tShift+V"
+
+"Move Selected Rung &Up\tShift+Up"
+"Seçili satýrý üste taþý\tShift+Up"
+
+"Move Selected Rung &Down\tShift+Down"
+"Seçili satýrý alta taþý\tShift+Down"
+
+"&Delete Selected Element\tDel"
+"&Seçili Elemaný Sil\tDel"
+
+"D&elete Rung\tShift+Del"
+"Seçili Satýrý Sil\tShift+Del"
+
+"Insert Co&mment\t;"
+"Açýklama Ekle\t;"
+
+"Insert &Contacts\tC"
+"Kontak Eekle\tC"
+
+"Insert OSR (One Shot Rising)\t&/"
+"OSR ekle (Yükselen Kenar)\t&/"
+
+"Insert OSF (One Shot Falling)\t&\\"
+"OSF ekle (Düþen Kenar)\t&\\"
+
+"Insert T&ON (Delayed Turn On)\tO"
+"T&ON ekle (Turn-On Gecikme)\tO"
+
+"Insert TO&F (Delayed Turn Off)\tF"
+"T&OF ekle (Turn-Off Gecikme)\tF"
+
+"Insert R&TO (Retentive Delayed Turn On)\tT"
+"R&TO ekle (Süre Sayan Turn-On Gecikme)\tT"
+
+"Insert CT&U (Count Up)\tU"
+"CT&U ekle (Yukarý Sayýcý)\tU"
+
+"Insert CT&D (Count Down)\tI"
+"CT&D ekle (Aþaðý Sayýcý)\tI"
+
+"Insert CT&C (Count Circular)\tJ"
+"CT&C ekle (Döngüsel Sayýcý)\tJ"
+
+"Insert EQU (Compare for Equals)\t="
+"EQU ekle (Eþitlik Kontrolü)\t="
+
+"Insert NEQ (Compare for Not Equals)"
+"NEQ ekle (Farklýlýk Kontrolü)"
+
+"Insert GRT (Compare for Greater Than)\t>"
+"GRT ekle (Büyük Kontrolü)\t>"
+
+"Insert GEQ (Compare for Greater Than or Equal)\t."
+"GEQ ekle (Büyük yada Eþit Kontrolü)\t."
+
+"Insert LES (Compare for Less Than)\t<"
+"LES ekle (Küçük Kontrolü)\t<"
+
+"Insert LEQ (Compare for Less Than or Equal)\t,"
+"LEQ ekle (Küçük yada Eþit Kontrolü)\t,"
+
+"Insert Open-Circuit"
+"Açýk Devre ekle"
+
+"Insert Short-Circuit"
+"Kapalý Devre ekle"
+
+"Insert Master Control Relay"
+"Ana Kontrol Rölesi ekle"
+
+"Insert Coi&l\tL"
+"Bobin ek&le\tL"
+
+"Insert R&ES (Counter/RTO Reset)\tE"
+"R&ES ekle (RTO/Sayýcý Sýfýrlamasý)\tE"
+
+"Insert MOV (Move)\tM"
+"MOV (Taþý) ekle\tM"
+
+"Insert ADD (16-bit Integer Add)\t+"
+"ADD (16 bit Toplama)ekle\t+"
+
+"Insert SUB (16-bit Integer Subtract)\t-"
+"SUB (16 bit çýkarma) ekle\t-"
+
+"Insert MUL (16-bit Integer Multiply)\t*"
+"MUL (16 bit çarpma) ekle\t*"
+
+"Insert DIV (16-bit Integer Divide)\tD"
+"DIV (16 bit bölme) ekle\tD"
+
+"Insert Shift Register"
+"Shift Register ekle"
+
+"Insert Look-Up Table"
+"Deðer Tablosu ekle"
+
+"Insert Piecewise Linear"
+"Parçalý Lineer ekle"
+
+"Insert Formatted String Over UART"
+"UART Üzerinden Biçimlendirilmiþ Kelime Ekle"
+
+"Insert &UART Send"
+"&UART'dan Gönderme ekle"
+
+"Insert &UART Receive"
+"&UART'dan Alma ekle"
+
+"Insert Set PWM Output"
+"PWM Çýkýþý Akit Et ekle"
+
+"Insert A/D Converter Read\tP"
+"A/D Çeviriciden Oku ekle\tP"
+
+"Insert Make Persistent"
+"EPROM'da Sakla ekle"
+
+"Make Norm&al\tA"
+"Normale Çevir\tA"
+
+"Make &Negated\tN"
+"Terslenmiþ Yap\tN"
+
+"Make &Set-Only\tS"
+"Set Yap\tS"
+
+"Make &Reset-Only\tR"
+"Reset Yap\tR"
+
+"&MCU Parameters..."
+"&Ýþlemci Ayarlarý..."
+
+"(no microcontroller)"
+"(iþlemci yok)"
+
+"&Microcontroller"
+"Ýþlemci Seçimi"
+
+"Si&mulation Mode\tCtrl+M"
+"Si&mülasyon Modu\tCtrl+M"
+
+"Start &Real-Time Simulation\tCtrl+R"
+"Ge&rçek Zamanlý Simülasyonu Baþlat\tCtrl+R"
+
+"&Halt Simulation\tCtrl+H"
+"Simülasyonu Durdur\tCtrl+H"
+
+"Single &Cycle\tSpace"
+"Satýr Satýr Simülasyon\tEspace"
+
+"&Compile\tF5"
+"&Derle\tF5"
+
+"Compile &As..."
+"Farklý Derle..."
+
+"&Manual...\tF1"
+"&Kitapçýk...\tF1"
+
+"&About..."
+"&Bilgi..."
+
+"&File"
+"&Dosya"
+
+"&Edit"
+"&Düzen"
+
+"&Settings"
+"&Ayarlar"
+
+"&Instruction"
+"K&omutlar"
+
+"Si&mulate"
+"Si&mülasyon"
+
+"&Compile"
+"&Derle"
+
+"&Help"
+"&Yardým"
+
+"no MCU selected"
+"Ýþlemci Seçilmedi"
+
+"cycle time %.2f ms"
+"çevrim süresi %.2f ms"
+
+"processor clock %.4f MHz"
+"iþlemci frekansý %.4f MHz"
+
+"Internal error relating to PIC paging; make program smaller or reshuffle it."
+"PIC sayfalamasý ile ilgili dahili hata oluþtu. Programý kýsaltýnýz yada deðiþtiriniz."
+
+"PWM frequency too fast."
+"PWM frekansý çok hýzlý."
+
+"PWM frequency too slow."
+"PWM frekansý çok yavaþ."
+
+"Cycle time too fast; increase cycle time, or use faster crystal."
+"Çevrim süresi çok kýsa. Süreyi yada kristal frekansýný artýrýnýz."
+
+"Cycle time too slow; decrease cycle time, or use slower crystal."
+"Çevrim süresi çok uzun. Süreyi yada kristal frekansýný azaltýnýz.."
+
+"Couldn't open file '%s'"
+"'%s' dosyasý açýlamadý."
+
+"Zero baud rate not possible."
+"Ýletiþim hýzý (Baudrate) sýfýr olamaz."
+
+"Compile successful; wrote IHEX for PIC16 to '%s'.\r\n\r\nConfiguration word (fuses) has been set for crystal oscillator, BOD enabled, LVP disabled, PWRT enabled, all code protection off.\r\n\r\nUsed %d/%d words of program flash (chip %d%% full)."
+"Derleme baþarýyla yapýldý; PIC16 için IHEX dosyasý '%s' dosyasýna kaydedildi.\r\n\r\nAyarlar: (PIC konfigürasyonu) kristal osilatör, BOD aktif, LVP pasif, PWRT aktif, tüm kod korumasý kapalý.\r\n\r\nPIC hafýzasýnýn %d/%d kelimesi kullanýldý. (Hafýza %d%% dolu)."
+
+"Type"
+"Tipi"
+
+"Timer"
+"Zamanlayýcý"
+
+"Counter"
+"Sayýcý"
+
+"Reset"
+"Reset"
+
+"OK"
+"Tamam"
+
+"Cancel"
+"Vazgeç"
+
+"Empty textbox; not permitted."
+"Yazý kutusu boþ olamaz"
+
+"Bad use of quotes: <%s>"
+"Týrnaklar yanlýþ kullanýlmýþ: <%s>"
+
+"Turn-On Delay"
+"Turn-On Gecikme Devresi"
+
+"Turn-Off Delay"
+"Turn-Off Gecikme Devresi"
+
+"Retentive Turn-On Delay"
+"Deðeri Saklanan Turn-On Gecikme"
+
+"Delay (ms):"
+"Gecikme (ms):"
+
+"Delay too long; maximum is 2**31 us."
+"Gecikme çok fazla. En fazla 2**31 us olabilir."
+
+"Delay cannot be zero or negative."
+"Gecikme sýfýr yada eksi deðer olamaz."
+
+"Count Up"
+"Yukarý Say"
+
+"Count Down"
+"Aþaðý Say"
+
+"Circular Counter"
+"Dairesel Sayýcý"
+
+"Max value:"
+"En Yüksek Deðer:"
+
+"True if >= :"
+"Doðru Eðer >= :"
+
+"If Equals"
+"Eþitse"
+
+"If Not Equals"
+"Eþit Deðilse"
+
+"If Greater Than"
+"Büyükse"
+
+"If Greater Than or Equal To"
+"Büyük yada Eþitse"
+
+"If Less Than"
+"Küçükse"
+
+"If Less Than or Equal To"
+"Küçük yada Eþitse"
+
+"'Closed' if:"
+"'Kapalý' Eðer:"
+
+"Move"
+"Taþý"
+
+"Read A/D Converter"
+"A/D Çeviriciyi Oku"
+
+"Duty cycle var:"
+"Pals Geniþliði Deðeri:"
+
+"Frequency (Hz):"
+"Frekans (Hz):"
+
+"Set PWM Duty Cycle"
+"PWM Pals Geniþliði Deðeri"
+
+"Source:"
+"Kaynak:"
+
+"Receive from UART"
+"UART'dan bilgi al"
+
+"Send to UART"
+"UART'dan bilgi gönder"
+
+"Add"
+"Topla"
+
+"Subtract"
+"Çýkar"
+
+"Multiply"
+"Çarp"
+
+"Divide"
+"Böl"
+
+"Destination:"
+"Hedef:"
+
+"is set := :"
+"Verilen Deðer := :"
+
+"Name:"
+"Ýsim:"
+
+"Stages:"
+"Aþamalar:"
+
+"Shift Register"
+"Shift Register"
+
+"Not a reasonable size for a shift register."
+"Shift Register için kabul edilebilir deðer deðil."
+
+"String:"
+"Karakter Dizisi:"
+
+"Formatted String Over UART"
+"UART Üzerinden Biçimlendirilmiþ Karakter Dizisi"
+
+"Variable:"
+"Deðiþken:"
+
+"Make Persistent"
+"EEPROM'da Sakla"
+
+"Too many elements in subcircuit!"
+"Alt devrede çok fazla eleman var!"
+
+"Too many rungs!"
+"Satýr sayýsý (Rung) fazla!"
+
+"Error"
+"Hata"
+
+"ANSI C target does not support peripherals (UART, PWM, ADC, EEPROM). Skipping that instruction."
+"ANSI C hedef özellikleri desteklemiyor.(UART, ADC, EEPROM). Ýlgili komutlar iþlenmeyecek."
+
+"Compile successful; wrote C source code to '%s'.\r\n\r\nThis is not a complete C program. You have to provide the runtime and all the I/O routines. See the comments in the source code for information about how to do this."
+"Derleme baþarýyla tamamlandý. C kaynak kodu '%s' dosyasýna yazýldý.\r\n\r\nBu dosya tam bir C dosyasý deðildir. G/Ç rutinlerini kendiniz saðlamalýsýnýz. Dosyayý incelemeniz faydalý olacaktýr."
+
+"Cannot delete rung; program must have at least one rung."
+"Bu satýr silinemez. Programda en az bir tane satýr (Rung) olmalýdýr."
+
+"Out of memory; simplify program or choose microcontroller with more memory."
+"ÝÞlemci hafýzasý doldu; Programý kýsaltýnýz yada daha yüksek hafýzasý olan bir iþlemci seçiniz."
+
+"Must assign pins for all ADC inputs (name '%s')."
+"Tüm ADC giriþlerinin bacaklarý belirtilmelidir (ADC '%s')."
+
+"Internal limit exceeded (number of vars)"
+"Dahili sýnýr aþýldý (Deðiþken Sayýsý)"
+
+"Internal relay '%s' never assigned; add its coil somewhere."
+"'%s' dahili rölesine deðer verilmedi, Bu röle için bir bobin ekleyiniz."
+
+"Must assign pins for all I/O.\r\n\r\n'%s' is not assigned."
+"Tüm G/Ç uçlarý için bacaklar belirtilmelidir.\r\n\r\n'%s' ucuna deðer verilmemiþ."
+
+"UART in use; pins %d and %d reserved for that."
+"UART Kullanýmda; %d ve %d bacaklarý bunun için kullanýlmaktadýr. Siz kullanamazsýnýz."
+
+"PWM in use; pin %d reserved for that."
+"PWM Kullanýmda; %d bacaðý bunun için ayrýlmýþtýr. Siz kullanamazsýnýz.."
+
+"UART baud rate generator: divisor=%d actual=%.4f for %.2f%% error.\r\n\r\nThis 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\nCode will be generated anyways but serial may be unreliable or completely broken."
+"UART Hýz Hesaplayýcýsý: Bölen=%d Gerçekte=%.4f (%.2f%% hata payý ile).\r\n\r\nBu deðer çok yüksektir. Deðiþik bir deðer denemelisiniz yada kristal frekansýný sýk kullanýlan ve bu deðerlere bölünebilen bir deðer seçiniz. (Mesela 3.6864MHz, 14.7456MHz gibi).\r\n\r\nHer durumda kod oluþturulacaktýr; ancak seri iletiþim düzgün çalýþmayabilir yada tamamen durabilir."
+
+"UART baud rate generator: too slow, divisor overflows. Use a slower crystal or a faster baud rate.\r\n\r\nCode will be generated anyways but serial will likely be completely broken."
+"UART Hýz Hesaplayýcýsý: Hýz çok düþük. Bu nedenle bölen taþma yapýyor. Ya kristal frekansýný düþürünüz yada hýzý (baudrate) artýrýnýz.\r\n\r\nHer durumda kod oluþturulacaktýr; ancak seri iletiþim düzgün çalýþmayabilir yada tamamen durabilir."
+
+"Couldn't open '%s'\n"
+"'%s' dosyasý açýlamadý\n"
+
+"Timer period too short (needs faster cycle time)."
+"Zamanlayýcý peryodu çok kýsa (daha yüksek çevrim süresi gerekli)."
+
+"Timer period too long (max 32767 times cycle time); use a slower cycle time."
+"Zamanlayýcý peryodu çok kýsa(en fazla. çevrim süresinin 32767 katý olabilir); çevrim süresini düþürünüz."
+
+"Constant %d out of range: -32768 to 32767 inclusive."
+"%d sabiti aralýðýn dýþýnda: -32768 ile 32767 arasýnda olmalýdýr."
+
+"Move instruction: '%s' not a valid destination."
+"Taþýma Komutu: '%s' geçerli bir hedef adresi deðil."
+
+"Math instruction: '%s' not a valid destination."
+"Matematik Komutu: '%s'geçerli bir hedef adresi deðil."
+
+"Piecewise linear lookup table with zero elements!"
+"Parçalý doðrusal arama tablosunda deðer yok!"
+
+"x values in piecewise linear table must be strictly increasing."
+"Parçalý doðrusal arama tablosundaki x deðerleri artan sýralamalý olmalý."
+
+"Numerical problem with piecewise linear lookup table. Either make the table entries smaller, or space the points together more closely.\r\n\r\nSee the help file for details."
+"Parçalý doðrusal arama tablosunda sayýsal hata.\r\n\r\nDetaylar için yardým menüsünü inceleyiniz."
+
+"Multiple escapes (\\0-9) present in format string, not allowed."
+"Biçimli karakter dizisinde birden çok escape kodu var(\\0-9)."
+
+"Bad escape: correct form is \\xAB."
+"Yanlýþ ESC komutu: doðru þekil = \\xAB."
+
+"Bad escape '\\%c'"
+"Yanlýþ ESC komutu '\\%c'"
+
+"Variable is interpolated into formatted string, but none is specified."
+"Biçimlendirilmiþ karakter kümesine deðiþken tanýmlanmýþ ama hiç belirtilmemiþ."
+
+"No variable is interpolated into formatted string, but a variable name is specified. Include a string like '\\-3', or leave variable name blank."
+"Biçimlendirilmiþ karakter kümesine deðiþken atanmamýþ ama deðiþken ismi belirtilmiþ. Ya deðiþken ismini belirtiniz yada '\\-3' gibi bir deðer veriniz.."
+
+"Empty row; delete it or add instructions before compiling."
+"Satýr boþ; Satýrý silmeli yada komut eklemelisiniz."
+
+"Couldn't write to '%s'"
+"'%s' dosyasýna kayýt yapýlamýyor."
+
+"Unsupported op (anything ADC, PWM, UART, EEPROM) for interpretable target."
+"Komut desteklenmiyor. (ADC, PWM, UART, EEPROM ile ilgili komutlardan herhangi biri)"
+
+"Compile successful; wrote interpretable code to '%s'.\r\n\r\nYou probably have to adapt the interpreter to your application. See the documentation."
+"Derleme baþarýyla tamamlandý. Kod '%s' dosyasýna yazýldý.\r\n\r\nDerleyiciyi uygulamanýza göre deðiþtirmeniz gerekebilir. Geniþ bilgi için LDMicro belgelerine baþvurabilirsiniz."
+
+"Microcontroller '%s' not supported.\r\n\r\nDefaulting to no selected MCU."
+"'%s' desteklenen bir iþlemci deðil.\r\n\r\nÝþlemci ayarý iþlemci yok þeklinde deðiþtirilmiþtir."
+
+"File format error; perhaps this program is for a newer version of LDmicro?"
+"Dosya biçimi hatasý. Açmaya çalýþtýðýnýz dosya ya LDMicro dosyasý deðil yada yeni bir sürüm ile yazýlmýþ."
+
+"Index:"
+"Liste:"
+
+"Points:"
+"Deðer Sayýsý:"
+
+"Count:"
+"Adet:"
+
+"Edit table of ASCII values like a string"
+"ASCII deðer tablosunu karakter dizisi gibi düzenle."
+
+"Look-Up Table"
+"Arama Tablosu"
+
+"Piecewise Linear Table"
+"Parçalý Lineer Tablo"
+
+"LDmicro Error"
+"LDmicro Hatasý"
+
+"Compile Successful"
+"Derleme Baþarýlý"
+
+"digital in"
+"Dijital Giriþ"
+
+"digital out"
+"Dijital Çýkýþ"
+
+"int. relay"
+"Dahili Röle"
+
+"UART tx"
+"UART tx"
+
+"UART rx"
+"UART rx"
+
+"PWM out"
+"PWM Çýkýþý"
+
+"turn-on delay"
+"Turn-On Gecikme"
+
+"turn-off delay"
+"Turn-Off Gecikme"
+
+"retentive timer"
+"deðeri saklanan zamanlayýcý"
+
+"counter"
+"Sayýcý"
+
+"general var"
+"Genel Deðiþken"
+
+"adc input"
+"ADC Giriþi"
+
+""
+""
+
+"(not assigned)"
+"(tanýmlý deðil)"
+
+""
+""
+
+""
+""
+
+"TOF: variable cannot be used elsewhere"
+"TOF: Deðiþken baþka bir yerde kullanýlamaz"
+
+"TON: variable cannot be used elsewhere"
+"TON: Deðiþken baþka bir yerde kullanýlamaz"
+
+"RTO: variable can only be used for RES elsewhere"
+"RTO: Deðiþken sadece RES için kullanýlabilir"
+
+"Variable '%s' not assigned to, e.g. with a MOV statement, an ADD statement, etc.\r\n\r\nThis is probably a programming error; now it will always be zero."
+"'%s' deðer atanmamýþ.Örneðin, MOV, ADD komutlarý ile deðer atanabilir.\r\n\r\nMuhtemelen bu bir programlama hatasýdýr. Bu nedenle deðiþkenin deðeri 0 olarak kullanýlacaktýr."
+
+"Variable for '%s' incorrectly assigned: %s."
+"'%s' için deðiþken hatalý atanmýþ: %s."
+
+"Division by zero; halting simulation"
+"Sýfýra bölüm; Simülasyon durduruldu."
+
+"!!!too long!!!"
+"!!!çok uzun!!"
+
+"\n\nI/O ASSIGNMENT:\n\n"
+"\n\nG/Ç TANIMI:\n\n"
+
+" Name | Type | Pin\n"
+" Ýsim | Tipi | Bacak\n"
diff --git a/ldmicro/lang.cpp b/ldmicro/lang.cpp
new file mode 100644
index 0000000..1a53da9
--- /dev/null
+++ b/ldmicro/lang.cpp
@@ -0,0 +1,78 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Multiple language support. For every non-English language, we have a
+// table that maps the English strings to the translated strings. An
+// internationalized version of the program will attempt to translate any
+// string that is passed, using the appropriate (selected by a #define)
+// table. If it fails then it just returns the English.
+// Jonathan Westhues, Apr 2007
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+typedef struct LangTableTag {
+ char *from;
+ char *to;
+} LangTable;
+
+typedef struct LangTag {
+ LangTable *tab;
+ int n;
+} Lang;
+
+// These are the actual translation tables, so should be included in just
+// one place.
+#include "obj/lang-tables.h"
+
+char *_(char *in)
+{
+ Lang *l;
+
+#if defined(LDLANG_EN)
+ return in;
+#elif defined(LDLANG_DE)
+ l = &LangDe;
+#elif defined(LDLANG_FR)
+ l = &LangFr;
+#elif defined(LDLANG_ES)
+ l = &LangEs;
+#elif defined(LDLANG_IT)
+ l = &LangIt;
+#elif defined(LDLANG_TR)
+ l = &LangTr;
+#elif defined(LDLANG_PT)
+ l = &LangPt;
+#else
+# error "Unrecognized language!"
+#endif
+
+ int i;
+
+ for(i = 0; i < l->n; i++) {
+ if(strcmp(in, l->tab[i].from)==0) {
+ return l->tab[i].to;
+ }
+ }
+
+ return in;
+}
diff --git a/ldmicro/ldinterpret.c b/ldmicro/ldinterpret.c
new file mode 100644
index 0000000..7b8993a
--- /dev/null
+++ b/ldmicro/ldinterpret.c
@@ -0,0 +1,380 @@
+//-----------------------------------------------------------------------------
+// A sample interpreter for the .int files generate by LDmicro. These files
+// represent a ladder logic program for a simple 'virtual machine.' The
+// interpreter must simulate the virtual machine and for proper timing the
+// program must be run over and over, with the period specified when it was
+// compiled (in Settings -> MCU Parameters).
+//
+// This method of running the ladder logic code would be useful if you wanted
+// to embed a ladder logic interpreter inside another program. LDmicro has
+// converted all variables into addresses, for speed of execution. However,
+// the .int file includes the mapping between variable names (same names
+// that the user specifies, that are visible on the ladder diagram) and
+// addresses. You can use this to establish specially-named variables that
+// define the interface between your ladder code and the rest of your program.
+//
+// In this example, I use this mechanism to print the value of the integer
+// variable 'a' after every cycle, and to generate a square wave with period
+// 2*Tcycle on the input 'Xosc'. That is only for demonstration purposes, of
+// course.
+//
+// In a real application you would need some way to get the information in the
+// .int file into your device; this would be very application-dependent. Then
+// you would need something like the InterpretOneCycle() routine to actually
+// run the code. You can redefine the program and data memory sizes to
+// whatever you think is practical; there are no particular constraints.
+//
+// The disassembler is just for debugging, of course. Note the unintuitive
+// names for the condition ops; the INT_IFs are backwards, and the INT_ELSE
+// is actually an unconditional jump! This is because I reused the names
+// from the intermediate code that LDmicro uses, in which the if/then/else
+// constructs have not yet been resolved into (possibly conditional)
+// absolute jumps. It makes a lot of sense to me, but probably not so much
+// to you; oh well.
+//
+// Jonathan Westhues, Aug 2005
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+
+#define INTCODE_H_CONSTANTS_ONLY
+#include "intcode.h"
+
+typedef unsigned char BYTE; // 8-bit unsigned
+typedef unsigned short WORD; // 16-bit unsigned
+typedef signed short SWORD; // 16-bit signed
+
+// Some arbitrary limits on the program and data size
+#define MAX_OPS 1024
+#define MAX_VARIABLES 128
+#define MAX_INTERNAL_RELAYS 128
+
+// This data structure represents a single instruction for the 'virtual
+// machine.' The .op field gives the opcode, and the other fields give
+// arguments. I have defined all of these as 16-bit fields for generality,
+// but if you want then you can crunch them down to 8-bit fields (and
+// limit yourself to 256 of each type of variable, of course). If you
+// crunch down .op then nothing bad happens at all. If you crunch down
+// .literal then you only have 8-bit literals now (so you can't move
+// 300 into 'var'). If you crunch down .name3 then that limits your code size,
+// because that is the field used to encode the jump addresses.
+//
+// A more compact encoding is very possible if space is a problem for
+// you. You will probably need some kind of translator regardless, though,
+// to put it in whatever format you're going to pack in flash or whatever,
+// and also to pick out the name <-> address mappings for those variables
+// that you're going to use for your interface out. I will therefore leave
+// that up to you.
+typedef struct {
+ WORD op;
+ WORD name1;
+ WORD name2;
+ WORD name3;
+ SWORD literal;
+} BinOp;
+
+BinOp Program[MAX_OPS];
+SWORD Integers[MAX_VARIABLES];
+BYTE Bits[MAX_INTERNAL_RELAYS];
+
+// This are addresses (indices into Integers[] or Bits[]) used so that your
+// C code can get at some of the ladder variables, by remembering the
+// mapping between some ladder names and their addresses.
+int SpecialAddrForA;
+int SpecialAddrForXosc;
+
+//-----------------------------------------------------------------------------
+// What follows are just routines to load the program, which I represent as
+// hex bytes, one instruction per line, into memory. You don't need to
+// remember the length of the program because the last instruction is a
+// special marker (INT_END_OF_PROGRAM).
+//
+void BadFormat(void)
+{
+ fprintf(stderr, "Bad program format.\n");
+ exit(-1);
+}
+int HexDigit(int c)
+{
+ c = tolower(c);
+ if(isdigit(c)) {
+ return c - '0';
+ } else if(c >= 'a' && c <= 'f') {
+ return (c - 'a') + 10;
+ } else {
+ BadFormat();
+ }
+ return 0;
+}
+void LoadProgram(char *fileName)
+{
+ int pc;
+ FILE *f = fopen(fileName, "r");
+ char line[80];
+
+ // This is not suitable for untrusted input.
+
+ if(!f) {
+ fprintf(stderr, "couldn't open '%s'\n", f);
+ exit(-1);
+ }
+
+ if(!fgets(line, sizeof(line), f)) BadFormat();
+ if(strcmp(line, "$$LDcode\n")!=0) BadFormat();
+
+ for(pc = 0; ; pc++) {
+ char *t, i;
+ BYTE *b;
+
+ if(!fgets(line, sizeof(line), f)) BadFormat();
+ if(strcmp(line, "$$bits\n")==0) break;
+ if(strlen(line) != sizeof(BinOp)*2 + 1) BadFormat();
+
+ t = line;
+ b = (BYTE *)&Program[pc];
+
+ for(i = 0; i < sizeof(BinOp); i++) {
+ b[i] = HexDigit(t[1]) | (HexDigit(t[0]) << 4);
+ t += 2;
+ }
+ }
+
+ SpecialAddrForA = -1;
+ SpecialAddrForXosc = -1;
+ while(fgets(line, sizeof(line), f)) {
+ if(memcmp(line, "a,", 2)==0) {
+ SpecialAddrForA = atoi(line+2);
+ }
+ if(memcmp(line, "Xosc,", 5)==0) {
+ SpecialAddrForXosc = atoi(line+5);
+ }
+ if(memcmp(line, "$$cycle", 7)==0) {
+ if(atoi(line + 7) != 10*1000) {
+ fprintf(stderr, "cycle time was not 10 ms when compiled; "
+ "please fix that.\n");
+ exit(-1);
+ }
+ }
+ }
+
+ if(SpecialAddrForA < 0 || SpecialAddrForXosc < 0) {
+ fprintf(stderr, "special interface variables 'a' or 'Xosc' not "
+ "used in prog.\n");
+ exit(-1);
+ }
+
+ fclose(f);
+}
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Disassemble the program and pretty-print it. This is just for debugging,
+// and it is also the only documentation for what each op does. The bit
+// variables (internal relays or whatever) live in a separate space from the
+// integer variables; I refer to those as bits[addr] and int16s[addr]
+// respectively.
+//-----------------------------------------------------------------------------
+void Disassemble(void)
+{
+ int pc;
+ for(pc = 0; ; pc++) {
+ BinOp *p = &Program[pc];
+ printf("%03x: ", pc);
+
+ switch(Program[pc].op) {
+ case INT_SET_BIT:
+ printf("bits[%03x] := 1", p->name1);
+ break;
+
+ case INT_CLEAR_BIT:
+ printf("bits[%03x] := 0", p->name1);
+ break;
+
+ case INT_COPY_BIT_TO_BIT:
+ printf("bits[%03x] := bits[%03x]", p->name1, p->name2);
+ break;
+
+ case INT_SET_VARIABLE_TO_LITERAL:
+ printf("int16s[%03x] := %d (0x%04x)", p->name1, p->literal,
+ p->literal);
+ break;
+
+ case INT_SET_VARIABLE_TO_VARIABLE:
+ printf("int16s[%03x] := int16s[%03x]", p->name1, p->name2);
+ break;
+
+ case INT_INCREMENT_VARIABLE:
+ printf("(int16s[%03x])++", p->name1);
+ break;
+
+ {
+ char c;
+ case INT_SET_VARIABLE_ADD: c = '+'; goto arith;
+ case INT_SET_VARIABLE_SUBTRACT: c = '-'; goto arith;
+ case INT_SET_VARIABLE_MULTIPLY: c = '*'; goto arith;
+ case INT_SET_VARIABLE_DIVIDE: c = '/'; goto arith;
+arith:
+ printf("int16s[%03x] := int16s[%03x] %c int16s[%03x]",
+ p->name1, p->name2, c, p->name3);
+ break;
+ }
+
+ case INT_IF_BIT_SET:
+ printf("unless (bits[%03x] set)", p->name1);
+ goto cond;
+ case INT_IF_BIT_CLEAR:
+ printf("unless (bits[%03x] clear)", p->name1);
+ goto cond;
+ case INT_IF_VARIABLE_LES_LITERAL:
+ printf("unless (int16s[%03x] < %d)", p->name1, p->literal);
+ goto cond;
+ case INT_IF_VARIABLE_EQUALS_VARIABLE:
+ printf("unless (int16s[%03x] == int16s[%03x])", p->name1,
+ p->name2);
+ goto cond;
+ case INT_IF_VARIABLE_GRT_VARIABLE:
+ printf("unless (int16s[%03x] > int16s[%03x])", p->name1,
+ p->name2);
+ goto cond;
+cond:
+ printf(" jump %03x+1", p->name3);
+ break;
+
+ case INT_ELSE:
+ printf("jump %03x+1", p->name3);
+ break;
+
+ case INT_END_OF_PROGRAM:
+ printf("\n");
+ return;
+
+ default:
+ BadFormat();
+ break;
+ }
+ printf("\n");
+ }
+}
+
+//-----------------------------------------------------------------------------
+// This is the actual interpreter. It runs the program, and needs no state
+// other than that kept in Bits[] and Integers[]. If you specified a cycle
+// time of 10 ms when you compiled the program, then you would have to
+// call this function 100 times per second for the timing to be correct.
+//
+// The execution time of this function depends mostly on the length of the
+// program. It will be a little bit data-dependent but not very.
+//-----------------------------------------------------------------------------
+void InterpretOneCycle(void)
+{
+ int pc;
+ for(pc = 0; ; pc++) {
+ BinOp *p = &Program[pc];
+
+ switch(Program[pc].op) {
+ case INT_SET_BIT:
+ Bits[p->name1] = 1;
+ break;
+
+ case INT_CLEAR_BIT:
+ Bits[p->name1] = 0;
+ break;
+
+ case INT_COPY_BIT_TO_BIT:
+ Bits[p->name1] = Bits[p->name2];
+ break;
+
+ case INT_SET_VARIABLE_TO_LITERAL:
+ Integers[p->name1] = p->literal;
+ break;
+
+ case INT_SET_VARIABLE_TO_VARIABLE:
+ Integers[p->name1] = Integers[p->name2];
+ break;
+
+ case INT_INCREMENT_VARIABLE:
+ (Integers[p->name1])++;
+ break;
+
+ case INT_SET_VARIABLE_ADD:
+ Integers[p->name1] = Integers[p->name2] + Integers[p->name3];
+ break;
+
+ case INT_SET_VARIABLE_SUBTRACT:
+ Integers[p->name1] = Integers[p->name2] - Integers[p->name3];
+ break;
+
+ case INT_SET_VARIABLE_MULTIPLY:
+ Integers[p->name1] = Integers[p->name2] * Integers[p->name3];
+ break;
+
+ case INT_SET_VARIABLE_DIVIDE:
+ if(Integers[p->name3] != 0) {
+ Integers[p->name1] = Integers[p->name2] /
+ Integers[p->name3];
+ }
+ break;
+
+ case INT_IF_BIT_SET:
+ if(!Bits[p->name1]) pc = p->name3;
+ break;
+
+ case INT_IF_BIT_CLEAR:
+ if(Bits[p->name1]) pc = p->name3;
+ break;
+
+ case INT_IF_VARIABLE_LES_LITERAL:
+ if(!(Integers[p->name1] < p->literal)) pc = p->name3;
+ break;
+
+ case INT_IF_VARIABLE_EQUALS_VARIABLE:
+ if(!(Integers[p->name1] == Integers[p->name2])) pc = p->name3;
+ break;
+
+ case INT_IF_VARIABLE_GRT_VARIABLE:
+ if(!(Integers[p->name1] > Integers[p->name2])) pc = p->name3;
+ break;
+
+ case INT_ELSE:
+ pc = p->name3;
+ break;
+
+ case INT_END_OF_PROGRAM:
+ return;
+ }
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ int i;
+
+ if(argc != 2) {
+ fprintf(stderr, "usage: %s xxx.int\n", argv[0]);
+ return -1;
+ }
+
+ LoadProgram(argv[1]);
+ memset(Integers, 0, sizeof(Integers));
+ memset(Bits, 0, sizeof(Bits));
+
+ // 1000 cycles times 10 ms gives 10 seconds execution
+ for(i = 0; i < 1000; i++) {
+ InterpretOneCycle();
+
+ // Example for reaching in and reading a variable: just print it.
+ printf("a = %d \r", Integers[SpecialAddrForA]);
+
+ // Example for reaching in and writing a variable.
+ Bits[SpecialAddrForXosc] = !Bits[SpecialAddrForXosc];
+
+ // XXX, nonportable; replace with whatever timing functions are
+ // available on your target.
+ Sleep(10);
+ }
+
+ return 0;
+}
diff --git a/ldmicro/ldinterpret.exe b/ldmicro/ldinterpret.exe
new file mode 100644
index 0000000..5b969f1
Binary files /dev/null and b/ldmicro/ldinterpret.exe differ
diff --git a/ldmicro/ldinterpret.obj b/ldmicro/ldinterpret.obj
new file mode 100644
index 0000000..2a95bc9
Binary files /dev/null and b/ldmicro/ldinterpret.obj differ
diff --git a/ldmicro/ldmicro.cpp b/ldmicro/ldmicro.cpp
new file mode 100644
index 0000000..02d9661
--- /dev/null
+++ b/ldmicro/ldmicro.cpp
@@ -0,0 +1,1152 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// A ladder logic compiler for 8 bit micros: user draws a ladder diagram,
+// with an appropriately constrained `schematic editor,' and then we can
+// simulated it under Windows or generate PIC/AVR code that performs the
+// requested operations. This files contains the program entry point, plus
+// most of the UI logic relating to the main window.
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+#include
+#include
+
+#include "ldmicro.h"
+#include "freeze.h"
+#include "mcutable.h"
+
+HINSTANCE Instance;
+HWND MainWindow;
+HDC Hdc;
+
+// parameters used to capture the mouse when implementing our totally non-
+// general splitter control
+static HHOOK MouseHookHandle;
+static int MouseY;
+
+// For the open/save dialog boxes
+#define LDMICRO_PATTERN "LDmicro Ladder Logic Programs (*.ld)\0*.ld\0" \
+ "All files\0*\0\0"
+char CurrentSaveFile[MAX_PATH];
+static BOOL ProgramChangedNotSaved = FALSE;
+
+#define HEX_PATTERN "Intel Hex Files (*.hex)\0*.hex\0All files\0*\0\0"
+#define C_PATTERN "C Source Files (*.c)\0*.c\0All Files\0*\0\0"
+#define INTERPRETED_PATTERN \
+ "Interpretable Byte Code Files (*.int)\0*.int\0All Files\0*\0\0"
+char CurrentCompileFile[MAX_PATH];
+
+#define TXT_PATTERN "Text Files (*.txt)\0*.txt\0All files\0*\0\0"
+
+// Everything relating to the PLC's program, I/O configuration, processor
+// choice, and so on--basically everything that would be saved in the
+// project file.
+PlcProgram Prog;
+
+//-----------------------------------------------------------------------------
+// Get a filename with a common dialog box and then save the program to that
+// file and then set our default filename to that.
+//-----------------------------------------------------------------------------
+static BOOL SaveAsDialog(void)
+{
+ OPENFILENAME ofn;
+
+ memset(&ofn, 0, sizeof(ofn));
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hInstance = Instance;
+ ofn.lpstrFilter = LDMICRO_PATTERN;
+ ofn.lpstrDefExt = "ld";
+ ofn.lpstrFile = CurrentSaveFile;
+ ofn.nMaxFile = sizeof(CurrentSaveFile);
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
+
+ if(!GetSaveFileName(&ofn))
+ return FALSE;
+
+ if(!SaveProjectToFile(CurrentSaveFile)) {
+ Error(_("Couldn't write to '%s'."), CurrentSaveFile);
+ return FALSE;
+ } else {
+ ProgramChangedNotSaved = FALSE;
+ return TRUE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Get a filename with a common dialog box and then export the program as
+// an ASCII art drawing.
+//-----------------------------------------------------------------------------
+static void ExportDialog(void)
+{
+ char exportFile[MAX_PATH];
+ OPENFILENAME ofn;
+
+ exportFile[0] = '\0';
+
+ memset(&ofn, 0, sizeof(ofn));
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hInstance = Instance;
+ ofn.lpstrFilter = TXT_PATTERN;
+ ofn.lpstrFile = exportFile;
+ ofn.lpstrTitle = _("Export As Text");
+ ofn.nMaxFile = sizeof(exportFile);
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
+
+ if(!GetSaveFileName(&ofn))
+ return;
+
+ ExportDrawingAsText(exportFile);
+}
+
+//-----------------------------------------------------------------------------
+// If we already have a filename, save the program to that. Otherwise same
+// as Save As. Returns TRUE if it worked, else returns FALSE.
+//-----------------------------------------------------------------------------
+static BOOL SaveProgram(void)
+{
+ if(strlen(CurrentSaveFile)) {
+ if(!SaveProjectToFile(CurrentSaveFile)) {
+ Error(_("Couldn't write to '%s'."), CurrentSaveFile);
+ return FALSE;
+ } else {
+ ProgramChangedNotSaved = FALSE;
+ return TRUE;
+ }
+ } else {
+ return SaveAsDialog();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Compile the program to a hex file for the target micro. Get the output
+// file name if necessary, then call the micro-specific compile routines.
+//-----------------------------------------------------------------------------
+static void CompileProgram(BOOL compileAs)
+{
+ if(compileAs || strlen(CurrentCompileFile)==0) {
+ OPENFILENAME ofn;
+
+ memset(&ofn, 0, sizeof(ofn));
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hInstance = Instance;
+ ofn.lpstrTitle = _("Compile To");
+ if(Prog.mcu && Prog.mcu->whichIsa == ISA_ANSIC) {
+ ofn.lpstrFilter = C_PATTERN;
+ ofn.lpstrDefExt = "c";
+ } else if(Prog.mcu && Prog.mcu->whichIsa == ISA_INTERPRETED) {
+ ofn.lpstrFilter = INTERPRETED_PATTERN;
+ ofn.lpstrDefExt = "int";
+ } else {
+ ofn.lpstrFilter = HEX_PATTERN;
+ ofn.lpstrDefExt = "hex";
+ }
+ ofn.lpstrFile = CurrentCompileFile;
+ ofn.nMaxFile = sizeof(CurrentCompileFile);
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
+
+ if(!GetSaveFileName(&ofn))
+ return;
+
+ // hex output filename is stored in the .ld file
+ ProgramChangedNotSaved = TRUE;
+ }
+
+ if(!GenerateIntermediateCode()) return;
+
+ if(Prog.mcu == NULL) {
+ Error(_("Must choose a target microcontroller before compiling."));
+ return;
+ }
+
+ if(UartFunctionUsed() && Prog.mcu->uartNeeds.rxPin == 0) {
+ Error(_("UART function used but not supported for this micro."));
+ return;
+ }
+
+ if(PwmFunctionUsed() && Prog.mcu->pwmNeedsPin == 0) {
+ Error(_("PWM function used but not supported for this micro."));
+ return;
+ }
+
+ switch(Prog.mcu->whichIsa) {
+ case ISA_AVR: CompileAvr(CurrentCompileFile); break;
+ case ISA_PIC16: CompilePic16(CurrentCompileFile); break;
+ case ISA_ANSIC: CompileAnsiC(CurrentCompileFile); break;
+ case ISA_INTERPRETED: CompileInterpreted(CurrentCompileFile); break;
+ case ISA_ARDUINO: CompileArduino(CurrentCompileFile); break;
+
+ default: oops();
+ }
+// IntDumpListing("t.pl");
+}
+
+//-----------------------------------------------------------------------------
+// If the program has been modified then give the user the option to save it
+// or to cancel the operation they are performing. Return TRUE if they want
+// to cancel.
+//-----------------------------------------------------------------------------
+BOOL CheckSaveUserCancels(void)
+{
+ if(!ProgramChangedNotSaved) {
+ // no problem
+ return FALSE;
+ }
+
+ int r = MessageBox(MainWindow,
+ _("The program has changed since it was last saved.\r\n\r\n"
+ "Do you want to save the changes?"), "LDmicro",
+ MB_YESNOCANCEL | MB_ICONWARNING);
+ switch(r) {
+ case IDYES:
+ if(SaveProgram())
+ return FALSE;
+ else
+ return TRUE;
+
+ case IDNO:
+ return FALSE;
+
+ case IDCANCEL:
+ return TRUE;
+
+ default:
+ oops();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Load a new program from a file. If it succeeds then set our default filename
+// to that, else we end up with an empty file then.
+//-----------------------------------------------------------------------------
+static void OpenDialog(void)
+{
+ OPENFILENAME ofn;
+
+ char tempSaveFile[MAX_PATH] = "";
+
+ memset(&ofn, 0, sizeof(ofn));
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hInstance = Instance;
+ ofn.lpstrFilter = LDMICRO_PATTERN;
+ ofn.lpstrDefExt = "ld";
+ ofn.lpstrFile = tempSaveFile;
+ ofn.nMaxFile = sizeof(tempSaveFile);
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+
+ if(!GetOpenFileName(&ofn))
+ return;
+
+ if(!LoadProjectFromFile(tempSaveFile)) {
+ Error(_("Couldn't open '%s'."), tempSaveFile);
+ CurrentSaveFile[0] = '\0';
+ } else {
+ ProgramChangedNotSaved = FALSE;
+ strcpy(CurrentSaveFile, tempSaveFile);
+ UndoFlush();
+ }
+
+ GenerateIoListDontLoseSelection();
+ RefreshScrollbars();
+ UpdateMainWindowTitleBar();
+}
+
+//-----------------------------------------------------------------------------
+// Housekeeping required when the program changes: mark the program as
+// changed so that we ask if user wants to save before exiting, and update
+// the I/O list.
+//-----------------------------------------------------------------------------
+void ProgramChanged(void)
+{
+ ProgramChangedNotSaved = TRUE;
+ GenerateIoListDontLoseSelection();
+ RefreshScrollbars();
+}
+#define CHANGING_PROGRAM(x) { \
+ UndoRemember(); \
+ x; \
+ ProgramChanged(); \
+ }
+
+//-----------------------------------------------------------------------------
+// Hook that we install when the user starts dragging the `splitter,' in case
+// they drag it out of the narrow area of the drawn splitter bar. Resize
+// the listview in response to mouse move, and unhook ourselves when they
+// release the mouse button.
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK MouseHook(int code, WPARAM wParam, LPARAM lParam)
+{
+ switch(code) {
+ case HC_ACTION: {
+ MSLLHOOKSTRUCT *mhs = (MSLLHOOKSTRUCT *)lParam;
+
+ switch(wParam) {
+ case WM_MOUSEMOVE: {
+ int dy = MouseY - mhs->pt.y;
+
+ IoListHeight += dy;
+ if(IoListHeight < 50) IoListHeight = 50;
+ MouseY = mhs->pt.y;
+ MainWindowResized();
+
+ break;
+ }
+
+ case WM_LBUTTONUP:
+ UnhookWindowsHookEx(MouseHookHandle);
+ break;
+ }
+ break;
+ }
+ }
+ return CallNextHookEx(MouseHookHandle, code, wParam, lParam);
+}
+
+//-----------------------------------------------------------------------------
+// Handle a selection from the menu bar of the main window.
+//-----------------------------------------------------------------------------
+static void ProcessMenu(int code)
+{
+ if(code >= MNU_PROCESSOR_0 && code < MNU_PROCESSOR_0+NUM_SUPPORTED_MCUS) {
+ strcpy(CurrentCompileFile, "");
+ Prog.mcu = &SupportedMcus[code - MNU_PROCESSOR_0];
+ RefreshControlsToSettings();
+ return;
+ }
+ if(code == MNU_PROCESSOR_0+NUM_SUPPORTED_MCUS) {
+ Prog.mcu = NULL;
+ strcpy(CurrentCompileFile, "");
+ RefreshControlsToSettings();
+ return;
+ }
+
+ switch(code) {
+ case MNU_NEW:
+ if(CheckSaveUserCancels()) break;
+ NewProgram();
+ strcpy(CurrentSaveFile, "");
+ strcpy(CurrentCompileFile, "");
+ GenerateIoListDontLoseSelection();
+ RefreshScrollbars();
+ UpdateMainWindowTitleBar();
+ break;
+
+ case MNU_OPEN:
+ if(CheckSaveUserCancels()) break;
+ OpenDialog();
+ break;
+
+ case MNU_SAVE:
+ SaveProgram();
+ UpdateMainWindowTitleBar();
+ break;
+
+ case MNU_SAVE_AS:
+ SaveAsDialog();
+ UpdateMainWindowTitleBar();
+ break;
+
+ case MNU_EXPORT:
+ ExportDialog();
+ break;
+
+ case MNU_EXIT:
+ if(CheckSaveUserCancels()) break;
+ PostQuitMessage(0);
+ break;
+
+ case MNU_INSERT_COMMENT:
+ CHANGING_PROGRAM(AddComment(_("--add comment here--")));
+ break;
+
+ case MNU_INSERT_CONTACTS:
+ CHANGING_PROGRAM(AddContact());
+ break;
+
+ case MNU_INSERT_COIL:
+ CHANGING_PROGRAM(AddCoil());
+ break;
+
+ case MNU_INSERT_TON:
+ CHANGING_PROGRAM(AddTimer(ELEM_TON));
+ break;
+
+ case MNU_INSERT_TOF:
+ CHANGING_PROGRAM(AddTimer(ELEM_TOF));
+ break;
+
+ case MNU_INSERT_RTO:
+ CHANGING_PROGRAM(AddTimer(ELEM_RTO));
+ break;
+
+ case MNU_INSERT_CTU:
+ CHANGING_PROGRAM(AddCounter(ELEM_CTU));
+ break;
+
+ case MNU_INSERT_CTD:
+ CHANGING_PROGRAM(AddCounter(ELEM_CTD));
+ break;
+
+ case MNU_INSERT_CTC:
+ CHANGING_PROGRAM(AddCounter(ELEM_CTC));
+ break;
+
+ case MNU_INSERT_RES:
+ CHANGING_PROGRAM(AddReset());
+ break;
+
+ case MNU_INSERT_OPEN:
+ CHANGING_PROGRAM(AddEmpty(ELEM_OPEN));
+ break;
+
+ case MNU_INSERT_SHORT:
+ CHANGING_PROGRAM(AddEmpty(ELEM_SHORT));
+ break;
+
+ case MNU_INSERT_MASTER_RLY:
+ CHANGING_PROGRAM(AddMasterRelay());
+ break;
+
+ case MNU_INSERT_SHIFT_REG:
+ CHANGING_PROGRAM(AddShiftRegister());
+ break;
+
+ case MNU_INSERT_LUT:
+ CHANGING_PROGRAM(AddLookUpTable());
+ break;
+
+ case MNU_INSERT_PWL:
+ CHANGING_PROGRAM(AddPiecewiseLinear());
+ break;
+
+ case MNU_INSERT_FMTD_STR:
+ CHANGING_PROGRAM(AddFormattedString());
+ break;
+
+ case MNU_INSERT_OSR:
+ CHANGING_PROGRAM(AddEmpty(ELEM_ONE_SHOT_RISING));
+ break;
+
+ case MNU_INSERT_OSF:
+ CHANGING_PROGRAM(AddEmpty(ELEM_ONE_SHOT_FALLING));
+ break;
+
+ case MNU_INSERT_MOV:
+ CHANGING_PROGRAM(AddMove());
+ break;
+
+ case MNU_INSERT_SET_PWM:
+ CHANGING_PROGRAM(AddSetPwm());
+ break;
+
+ case MNU_INSERT_READ_ADC:
+ CHANGING_PROGRAM(AddReadAdc());
+ break;
+
+ case MNU_INSERT_UART_SEND:
+ CHANGING_PROGRAM(AddUart(ELEM_UART_SEND));
+ break;
+
+ case MNU_INSERT_UART_RECV:
+ CHANGING_PROGRAM(AddUart(ELEM_UART_RECV));
+ break;
+
+ case MNU_INSERT_PERSIST:
+ CHANGING_PROGRAM(AddPersist());
+ break;
+
+ {
+ int elem;
+ case MNU_INSERT_ADD: elem = ELEM_ADD; goto math;
+ case MNU_INSERT_SUB: elem = ELEM_SUB; goto math;
+ case MNU_INSERT_MUL: elem = ELEM_MUL; goto math;
+ case MNU_INSERT_DIV: elem = ELEM_DIV; goto math;
+math:
+ CHANGING_PROGRAM(AddMath(elem));
+ break;
+ }
+
+ {
+ int elem;
+ case MNU_INSERT_EQU: elem = ELEM_EQU; goto cmp;
+ case MNU_INSERT_NEQ: elem = ELEM_NEQ; goto cmp;
+ case MNU_INSERT_GRT: elem = ELEM_GRT; goto cmp;
+ case MNU_INSERT_GEQ: elem = ELEM_GEQ; goto cmp;
+ case MNU_INSERT_LES: elem = ELEM_LES; goto cmp;
+ case MNU_INSERT_LEQ: elem = ELEM_LEQ; goto cmp;
+cmp:
+ CHANGING_PROGRAM(AddCmp(elem));
+ break;
+ }
+
+ case MNU_MAKE_NORMAL:
+ CHANGING_PROGRAM(MakeNormalSelected());
+ break;
+
+ case MNU_NEGATE:
+ CHANGING_PROGRAM(NegateSelected());
+ break;
+
+ case MNU_MAKE_SET_ONLY:
+ CHANGING_PROGRAM(MakeSetOnlySelected());
+ break;
+
+ case MNU_MAKE_RESET_ONLY:
+ CHANGING_PROGRAM(MakeResetOnlySelected());
+ break;
+
+ case MNU_UNDO:
+ UndoUndo();
+ break;
+
+ case MNU_REDO:
+ UndoRedo();
+ break;
+
+ case MNU_INSERT_RUNG_BEFORE:
+ CHANGING_PROGRAM(InsertRung(FALSE));
+ break;
+
+ case MNU_INSERT_RUNG_AFTER:
+ CHANGING_PROGRAM(InsertRung(TRUE));
+ break;
+
+ case MNU_DELETE_RUNG:
+ CHANGING_PROGRAM(DeleteSelectedRung());
+ break;
+
+ case MNU_PUSH_RUNG_UP:
+ CHANGING_PROGRAM(PushRungUp());
+ break;
+
+ case MNU_PUSH_RUNG_DOWN:
+ CHANGING_PROGRAM(PushRungDown());
+ break;
+
+ case MNU_DELETE_ELEMENT:
+ CHANGING_PROGRAM(DeleteSelectedFromProgram());
+ break;
+
+ case MNU_MCU_SETTINGS:
+ CHANGING_PROGRAM(ShowConfDialog());
+ break;
+
+ case MNU_SIMULATION_MODE:
+ ToggleSimulationMode();
+ break;
+
+ case MNU_START_SIMULATION:
+ StartSimulation();
+ break;
+
+ case MNU_STOP_SIMULATION:
+ StopSimulation();
+ break;
+
+ case MNU_SINGLE_CYCLE:
+ SimulateOneCycle(TRUE);
+ break;
+
+ case MNU_COMPILE:
+ CompileProgram(FALSE);
+ break;
+
+ case MNU_COMPILE_AS:
+ CompileProgram(TRUE);
+ break;
+
+ case MNU_MANUAL:
+ ShowHelpDialog(FALSE);
+ break;
+
+ case MNU_ABOUT:
+ ShowHelpDialog(TRUE);
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// WndProc for MainWindow.
+//-----------------------------------------------------------------------------
+LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_ERASEBKGND:
+ break;
+
+ case WM_SETFOCUS:
+ InvalidateRect(MainWindow, NULL, FALSE);
+ break;
+
+ case WM_PAINT: {
+ PAINTSTRUCT ps;
+ Hdc = BeginPaint(hwnd, &ps);
+
+ // This draws the schematic.
+ PaintWindow();
+
+ RECT r;
+ // Fill around the scroll bars
+ if(NeedHoriz) {
+ r.top = IoListTop - ScrollHeight - 2;
+ r.bottom = IoListTop - 2;
+ FillRect(Hdc, &r, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
+ }
+ GetClientRect(MainWindow, &r);
+ r.left = r.right - ScrollWidth - 2;
+ FillRect(Hdc, &r, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
+
+ // Draw the splitter thing to grab to resize the I/O listview.
+ GetClientRect(MainWindow, &r);
+ r.top = IoListTop - 2;
+ r.bottom = IoListTop;
+ FillRect(Hdc, &r, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
+ r.top = IoListTop - 2;
+ r.bottom = IoListTop - 1;
+ FillRect(Hdc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH));
+ r.top = IoListTop;
+ r.bottom = IoListTop + 1;
+ FillRect(Hdc, &r, (HBRUSH)GetStockObject(DKGRAY_BRUSH));
+
+ EndPaint(hwnd, &ps);
+ return 1;
+ }
+
+ case WM_KEYDOWN: {
+ if(wParam == 'M') {
+ if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
+ ToggleSimulationMode();
+ break;
+ }
+ } else if(wParam == VK_TAB) {
+ SetFocus(IoList);
+ BlinkCursor(0, 0, 0, 0);
+ break;
+ } else if(wParam == VK_F1) {
+ ShowHelpDialog(FALSE);
+ break;
+ }
+
+ if(InSimulationMode) {
+ switch(wParam) {
+ case ' ':
+ SimulateOneCycle(TRUE);
+ break;
+
+ case 'R':
+ if(GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ StartSimulation();
+ break;
+
+ case 'H':
+ if(GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ StopSimulation();
+ break;
+
+ case VK_DOWN:
+ if(ScrollYOffset < ScrollYOffsetMax)
+ ScrollYOffset++;
+ RefreshScrollbars();
+ InvalidateRect(MainWindow, NULL, FALSE);
+ break;
+
+ case VK_UP:
+ if(ScrollYOffset > 0)
+ ScrollYOffset--;
+ RefreshScrollbars();
+ InvalidateRect(MainWindow, NULL, FALSE);
+ break;
+
+ case VK_LEFT:
+ ScrollXOffset -= FONT_WIDTH;
+ if(ScrollXOffset < 0) ScrollXOffset = 0;
+ RefreshScrollbars();
+ InvalidateRect(MainWindow, NULL, FALSE);
+ break;
+
+ case VK_RIGHT:
+ ScrollXOffset += FONT_WIDTH;
+ if(ScrollXOffset >= ScrollXOffsetMax)
+ ScrollXOffset = ScrollXOffsetMax;
+ RefreshScrollbars();
+ InvalidateRect(MainWindow, NULL, FALSE);
+ break;
+
+ case VK_RETURN:
+ case VK_ESCAPE:
+ ToggleSimulationMode();
+ break;
+ }
+ break;
+ }
+
+
+ switch(wParam) {
+ case VK_F5:
+ CompileProgram(FALSE);
+ break;
+
+ case VK_UP:
+ if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
+ CHANGING_PROGRAM(PushRungUp());
+ } else {
+ MoveCursorKeyboard(wParam);
+ }
+ break;
+
+ case VK_DOWN:
+ if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
+ CHANGING_PROGRAM(PushRungDown());
+ } else {
+ MoveCursorKeyboard(wParam);
+ }
+ break;
+
+ case VK_RIGHT:
+ case VK_LEFT:
+ MoveCursorKeyboard(wParam);
+ break;
+
+ case VK_RETURN:
+ CHANGING_PROGRAM(EditSelectedElement());
+ break;
+
+ case VK_DELETE:
+ if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
+ CHANGING_PROGRAM(DeleteSelectedRung());
+ } else {
+ CHANGING_PROGRAM(DeleteSelectedFromProgram());
+ }
+ break;
+
+ case VK_OEM_1:
+ CHANGING_PROGRAM(AddComment(_("--add comment here--")));
+ break;
+
+ case 'C':
+ CHANGING_PROGRAM(AddContact());
+ break;
+
+ // TODO: rather country-specific here
+ case VK_OEM_2:
+ CHANGING_PROGRAM(AddEmpty(ELEM_ONE_SHOT_RISING));
+ break;
+
+ case VK_OEM_5:
+ CHANGING_PROGRAM(AddEmpty(ELEM_ONE_SHOT_FALLING));
+ break;
+
+ case 'L':
+ CHANGING_PROGRAM(AddCoil());
+ break;
+
+ case 'R':
+ CHANGING_PROGRAM(MakeResetOnlySelected());
+ break;
+
+ case 'E':
+ if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
+ ExportDialog();
+ } else {
+ CHANGING_PROGRAM(AddReset());
+ }
+ break;
+
+ case 'S':
+ if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
+ SaveProgram();
+ UpdateMainWindowTitleBar();
+ } else {
+ CHANGING_PROGRAM(MakeSetOnlySelected());
+ }
+ break;
+
+ case 'N':
+ if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
+ if(CheckSaveUserCancels()) break;
+ if(!ProgramChangedNotSaved) {
+ int r = MessageBox(MainWindow,
+ _("Start new program?"),
+ "LDmicro", MB_YESNO | MB_DEFBUTTON2 |
+ MB_ICONQUESTION);
+ if(r == IDNO) break;
+ }
+ NewProgram();
+ strcpy(CurrentSaveFile, "");
+ strcpy(CurrentCompileFile, "");
+ GenerateIoListDontLoseSelection();
+ RefreshScrollbars();
+ UpdateMainWindowTitleBar();
+ } else {
+ CHANGING_PROGRAM(NegateSelected());
+ }
+ break;
+
+ case 'A':
+ CHANGING_PROGRAM(MakeNormalSelected());
+ break;
+
+ case 'T':
+ CHANGING_PROGRAM(AddTimer(ELEM_RTO));
+ break;
+
+ case 'O':
+ if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
+ if(CheckSaveUserCancels()) break;
+ OpenDialog();
+ } else {
+ CHANGING_PROGRAM(AddTimer(ELEM_TON));
+ }
+ break;
+
+ case 'F':
+ CHANGING_PROGRAM(AddTimer(ELEM_TOF));
+ break;
+
+ case 'U':
+ CHANGING_PROGRAM(AddCounter(ELEM_CTU));
+ break;
+
+ case 'I':
+ CHANGING_PROGRAM(AddCounter(ELEM_CTD));
+ break;
+
+ case 'J':
+ CHANGING_PROGRAM(AddCounter(ELEM_CTC));
+ break;
+
+ case 'M':
+ CHANGING_PROGRAM(AddMove());
+ break;
+
+ case 'P':
+ CHANGING_PROGRAM(AddReadAdc());
+ break;
+
+ case VK_OEM_PLUS:
+ if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
+ CHANGING_PROGRAM(AddMath(ELEM_ADD));
+ } else {
+ CHANGING_PROGRAM(AddCmp(ELEM_EQU));
+ }
+ break;
+
+ case VK_OEM_MINUS:
+ if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
+ } else {
+ CHANGING_PROGRAM(AddMath(ELEM_SUB));
+ }
+ break;
+
+ case '8':
+ if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
+ CHANGING_PROGRAM(AddMath(ELEM_MUL));
+ }
+ break;
+
+ case 'D':
+ CHANGING_PROGRAM(AddMath(ELEM_DIV));
+ break;
+
+ case VK_OEM_PERIOD:
+ if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
+ CHANGING_PROGRAM(AddCmp(ELEM_GRT));
+ } else {
+ CHANGING_PROGRAM(AddCmp(ELEM_GEQ));
+ }
+ break;
+
+ case VK_OEM_COMMA:
+ if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
+ CHANGING_PROGRAM(AddCmp(ELEM_LES));
+ } else {
+ CHANGING_PROGRAM(AddCmp(ELEM_LEQ));
+ }
+ break;
+
+ case 'V':
+ if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
+ CHANGING_PROGRAM(InsertRung(TRUE));
+ }
+ break;
+
+ case '6':
+ if(GetAsyncKeyState(VK_SHIFT) & 0x8000) {
+ CHANGING_PROGRAM(InsertRung(FALSE));
+ }
+ break;
+
+ case 'Z':
+ if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
+ UndoUndo();
+ }
+ break;
+
+ case 'Y':
+ if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
+ UndoRedo();
+ }
+ break;
+
+ default:
+ break;
+ }
+ if(wParam != VK_SHIFT && wParam != VK_CONTROL) {
+ InvalidateRect(MainWindow, NULL, FALSE);
+ }
+ break;
+ }
+
+ case WM_LBUTTONDBLCLK: {
+ int x = LOWORD(lParam);
+ int y = HIWORD(lParam);
+ if(InSimulationMode) {
+ EditElementMouseDoubleclick(x, y);
+ } else {
+ CHANGING_PROGRAM(EditElementMouseDoubleclick(x, y));
+ }
+ InvalidateRect(MainWindow, NULL, FALSE);
+ break;
+ }
+
+ case WM_LBUTTONDOWN: {
+ int x = LOWORD(lParam);
+ int y = HIWORD(lParam);
+ if((y > (IoListTop - 9)) && (y < (IoListTop + 3))) {
+ POINT pt;
+ pt.x = x; pt.y = y;
+ ClientToScreen(MainWindow, &pt);
+ MouseY = pt.y;
+ MouseHookHandle = SetWindowsHookEx(WH_MOUSE_LL,
+ (HOOKPROC)MouseHook, Instance, 0);
+ }
+ if(!InSimulationMode) MoveCursorMouseClick(x, y);
+
+ SetFocus(MainWindow);
+ InvalidateRect(MainWindow, NULL, FALSE);
+ break;
+ }
+ case WM_MOUSEMOVE: {
+ int x = LOWORD(lParam);
+ int y = HIWORD(lParam);
+
+ if((y > (IoListTop - 9)) && (y < (IoListTop + 3))) {
+ SetCursor(LoadCursor(NULL, IDC_SIZENS));
+ } else {
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+ }
+
+ break;
+ }
+ case WM_MOUSEWHEEL: {
+ if((GET_WHEEL_DELTA_WPARAM(wParam)) > 0) {
+ VscrollProc(SB_LINEUP);
+ } else {
+ VscrollProc(SB_LINEDOWN);
+ }
+ break;
+ }
+
+ case WM_SIZE:
+ MainWindowResized();
+ break;
+
+ case WM_NOTIFY: {
+ NMHDR *h = (NMHDR *)lParam;
+ if(h->hwndFrom == IoList) {
+ IoListProc(h);
+ }
+ return 0;
+ }
+ case WM_VSCROLL:
+ VscrollProc(wParam);
+ break;
+
+ case WM_HSCROLL:
+ HscrollProc(wParam);
+ break;
+
+ case WM_COMMAND:
+ ProcessMenu(LOWORD(wParam));
+ InvalidateRect(MainWindow, NULL, FALSE);
+ break;
+
+ case WM_CLOSE:
+ case WM_DESTROY:
+ if(CheckSaveUserCancels()) break;
+
+ PostQuitMessage(0);
+ return 1;
+
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Create our window class; nothing exciting.
+//-----------------------------------------------------------------------------
+static BOOL MakeWindowClass()
+{
+ WNDCLASSEX wc;
+ memset(&wc, 0, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+
+ wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC |
+ CS_DBLCLKS;
+ wc.lpfnWndProc = (WNDPROC)MainWndProc;
+ wc.hInstance = Instance;
+ wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ wc.lpszClassName = "LDmicro";
+ wc.lpszMenuName = NULL;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hIcon = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
+ IMAGE_ICON, 32, 32, 0);
+ wc.hIconSm = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
+ IMAGE_ICON, 16, 16, 0);
+
+ return RegisterClassEx(&wc);
+}
+
+//-----------------------------------------------------------------------------
+// Entry point into the program.
+//-----------------------------------------------------------------------------
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine, INT nCmdShow)
+{
+ Instance = hInstance;
+
+ MainHeap = HeapCreate(0, 1024*64, 0);
+
+ MakeWindowClass();
+ MakeDialogBoxClass();
+ HMENU top = MakeMainWindowMenus();
+
+ MainWindow = CreateWindowEx(0, "LDmicro", "",
+ WS_OVERLAPPED | WS_THICKFRAME | WS_CLIPCHILDREN | WS_MAXIMIZEBOX |
+ WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX,
+ 10, 10, 800, 600, NULL, top, Instance, NULL);
+ ThawWindowPos(MainWindow);
+ IoListHeight = 100;
+ ThawDWORD(IoListHeight);
+
+ InitCommonControls();
+ InitForDrawing();
+
+ MakeMainWindowControls();
+ MainWindowResized();
+
+ NewProgram();
+ strcpy(CurrentSaveFile, "");
+
+ // Check if we're running in non-interactive mode; in that case we should
+ // load the file, compile, and exit.
+ while(isspace(*lpCmdLine)) {
+ lpCmdLine++;
+ }
+ if(memcmp(lpCmdLine, "/c", 2)==0) {
+ RunningInBatchMode = TRUE;
+
+ char *err =
+ "Bad command line arguments: run 'ldmicro /c src.ld dest.hex'";
+
+ char *source = lpCmdLine + 2;
+ while(isspace(*source)) {
+ source++;
+ }
+ if(*source == '\0') { Error(err); exit(-1); }
+ char *dest = source;
+ while(!isspace(*dest) && *dest) {
+ dest++;
+ }
+ if(*dest == '\0') { Error(err); exit(-1); }
+ *dest = '\0'; dest++;
+ while(isspace(*dest)) {
+ dest++;
+ }
+ if(*dest == '\0') { Error(err); exit(-1); }
+ if(!LoadProjectFromFile(source)) {
+ Error("Couldn't open '%s', running non-interactively.", source);
+ exit(-1);
+ }
+ strcpy(CurrentCompileFile, dest);
+ GenerateIoList(-1);
+ CompileProgram(FALSE);
+ exit(0);
+ }
+
+ // We are running interactively, or we would already have exited. We
+ // can therefore show the window now, and otherwise set up the GUI.
+
+ ShowWindow(MainWindow, SW_SHOW);
+ SetTimer(MainWindow, TIMER_BLINK_CURSOR, 800, BlinkCursor);
+
+ if(strlen(lpCmdLine) > 0) {
+ char line[MAX_PATH];
+ if(*lpCmdLine == '"') {
+ strcpy(line, lpCmdLine+1);
+ } else {
+ strcpy(line, lpCmdLine);
+ }
+ if(strchr(line, '"')) *strchr(line, '"') = '\0';
+
+ char *s;
+ GetFullPathName(line, sizeof(CurrentSaveFile), CurrentSaveFile, &s);
+ if(!LoadProjectFromFile(CurrentSaveFile)) {
+ NewProgram();
+ Error(_("Couldn't open '%s'."), CurrentSaveFile);
+ CurrentSaveFile[0] = '\0';
+ }
+ UndoFlush();
+ }
+
+ GenerateIoListDontLoseSelection();
+ RefreshScrollbars();
+ UpdateMainWindowTitleBar();
+
+ MSG msg;
+ DWORD ret;
+ while(ret = GetMessage(&msg, NULL, 0, 0)) {
+ if(msg.hwnd == IoList && msg.message == WM_KEYDOWN) {
+ if(msg.wParam == VK_TAB) {
+ SetFocus(MainWindow);
+ continue;
+ }
+ }
+ if(msg.message == WM_KEYDOWN && msg.wParam != VK_UP &&
+ msg.wParam != VK_DOWN && msg.wParam != VK_RETURN && msg.wParam
+ != VK_SHIFT)
+ {
+ if(msg.hwnd == IoList) {
+ msg.hwnd = MainWindow;
+ SetFocus(MainWindow);
+ }
+ }
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ FreezeWindowPos(MainWindow);
+ FreezeDWORD(IoListHeight);
+
+ return 0;
+}
diff --git a/ldmicro/ldmicro.exe.manifest b/ldmicro/ldmicro.exe.manifest
new file mode 100644
index 0000000..705299b
--- /dev/null
+++ b/ldmicro/ldmicro.exe.manifest
@@ -0,0 +1,22 @@
+
+
+
+Ladder logic editor/compiler for PIC and AVR micros.
+
+
+
+
+
+
diff --git a/ldmicro/ldmicro.h b/ldmicro/ldmicro.h
new file mode 100644
index 0000000..22050da
--- /dev/null
+++ b/ldmicro/ldmicro.h
@@ -0,0 +1,760 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Constants, structures, declarations etc. for the PIC ladder logic compiler
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+
+#ifndef __LDMICRO_H
+#define __LDMICRO_H
+
+#include
+typedef signed short SWORD;
+typedef signed long SDWORD;
+
+//-----------------------------------------------
+// `Configuration options.'
+
+// The library that I use to do registry stuff.
+#define FREEZE_SUBKEY "LDMicro"
+
+// Size of the font that we will use to draw the ladder diagrams, in pixels
+#define FONT_WIDTH 7
+#define FONT_HEIGHT 13
+
+
+//-----------------------------------------------
+// Constants for the GUI. We have drop-down menus, a listview for the I/Os,
+// etc.
+
+// Menu IDs
+
+#define MNU_NEW 0x01
+#define MNU_OPEN 0x02
+#define MNU_SAVE 0x03
+#define MNU_SAVE_AS 0x04
+#define MNU_EXPORT 0x05
+#define MNU_EXIT 0x06
+
+#define MNU_UNDO 0x10
+#define MNU_REDO 0x11
+#define MNU_PUSH_RUNG_UP 0x12
+#define MNU_PUSH_RUNG_DOWN 0x13
+#define MNU_INSERT_RUNG_BEFORE 0x14
+#define MNU_INSERT_RUNG_AFTER 0x15
+#define MNU_DELETE_ELEMENT 0x16
+#define MNU_DELETE_RUNG 0x17
+
+#define MNU_INSERT_COMMENT 0x20
+#define MNU_INSERT_CONTACTS 0x21
+#define MNU_INSERT_COIL 0x22
+#define MNU_INSERT_TON 0x23
+#define MNU_INSERT_TOF 0x24
+#define MNU_INSERT_RTO 0x25
+#define MNU_INSERT_RES 0x26
+#define MNU_INSERT_OSR 0x27
+#define MNU_INSERT_OSF 0x28
+#define MNU_INSERT_CTU 0x29
+#define MNU_INSERT_CTD 0x2a
+#define MNU_INSERT_CTC 0x2b
+#define MNU_INSERT_ADD 0x2c
+#define MNU_INSERT_SUB 0x2d
+#define MNU_INSERT_MUL 0x2e
+#define MNU_INSERT_DIV 0x2f
+#define MNU_INSERT_MOV 0x30
+#define MNU_INSERT_READ_ADC 0x31
+#define MNU_INSERT_SET_PWM 0x32
+#define MNU_INSERT_UART_SEND 0x33
+#define MNU_INSERT_UART_RECV 0x34
+#define MNU_INSERT_EQU 0x35
+#define MNU_INSERT_NEQ 0x36
+#define MNU_INSERT_GRT 0x37
+#define MNU_INSERT_GEQ 0x38
+#define MNU_INSERT_LES 0x39
+#define MNU_INSERT_LEQ 0x3a
+#define MNU_INSERT_OPEN 0x3b
+#define MNU_INSERT_SHORT 0x3c
+#define MNU_INSERT_MASTER_RLY 0x3d
+#define MNU_INSERT_SHIFT_REG 0x3e
+#define MNU_INSERT_LUT 0x3f
+#define MNU_INSERT_FMTD_STR 0x40
+#define MNU_INSERT_PERSIST 0x41
+#define MNU_MAKE_NORMAL 0x42
+#define MNU_NEGATE 0x43
+#define MNU_MAKE_SET_ONLY 0x44
+#define MNU_MAKE_RESET_ONLY 0x45
+#define MNU_INSERT_PWL 0x46
+
+#define MNU_MCU_SETTINGS 0x50
+#define MNU_PROCESSOR_0 0xa0
+
+#define MNU_SIMULATION_MODE 0x60
+#define MNU_START_SIMULATION 0x61
+#define MNU_STOP_SIMULATION 0x62
+#define MNU_SINGLE_CYCLE 0x63
+
+#define MNU_COMPILE 0x70
+#define MNU_COMPILE_AS 0x71
+
+#define MNU_MANUAL 0x80
+#define MNU_ABOUT 0x81
+
+// Columns within the I/O etc. listview.
+#define LV_IO_NAME 0x00
+#define LV_IO_TYPE 0x01
+#define LV_IO_STATE 0x02
+#define LV_IO_PIN 0x03
+#define LV_IO_PORT 0x04
+
+// Timer IDs associated with the main window.
+#define TIMER_BLINK_CURSOR 1
+#define TIMER_SIMULATE 2
+
+//-----------------------------------------------
+// Data structures for the actual ladder logic. A rung on the ladder
+// is a series subcircuit. A series subcircuit contains elements or
+// parallel subcircuits. A parallel subcircuit contains elements or series
+// subcircuits. An element is a set of contacts (possibly negated) or a coil.
+
+#define MAX_ELEMENTS_IN_SUBCKT 16
+
+#define ELEM_PLACEHOLDER 0x01
+#define ELEM_SERIES_SUBCKT 0x02
+#define ELEM_PARALLEL_SUBCKT 0x03
+#define ELEM_PADDING 0x04
+#define ELEM_COMMENT 0x05
+
+#define ELEM_CONTACTS 0x10
+#define ELEM_COIL 0x11
+#define ELEM_TON 0x12
+#define ELEM_TOF 0x13
+#define ELEM_RTO 0x14
+#define ELEM_RES 0x15
+#define ELEM_ONE_SHOT_RISING 0x16
+#define ELEM_ONE_SHOT_FALLING 0x17
+#define ELEM_MOVE 0x18
+#define ELEM_ADD 0x19
+#define ELEM_SUB 0x1a
+#define ELEM_MUL 0x1b
+#define ELEM_DIV 0x1c
+#define ELEM_EQU 0x1d
+#define ELEM_NEQ 0x1e
+#define ELEM_GRT 0x1f
+#define ELEM_GEQ 0x20
+#define ELEM_LES 0x21
+#define ELEM_LEQ 0x22
+#define ELEM_CTU 0x23
+#define ELEM_CTD 0x24
+#define ELEM_CTC 0x25
+#define ELEM_SHORT 0x26
+#define ELEM_OPEN 0x27
+#define ELEM_READ_ADC 0x28
+#define ELEM_SET_PWM 0x29
+#define ELEM_UART_RECV 0x2a
+#define ELEM_UART_SEND 0x2b
+#define ELEM_MASTER_RELAY 0x2c
+#define ELEM_SHIFT_REGISTER 0x2d
+#define ELEM_LOOK_UP_TABLE 0x2e
+#define ELEM_FORMATTED_STRING 0x2f
+#define ELEM_PERSIST 0x30
+#define ELEM_PIECEWISE_LINEAR 0x31
+
+#define CASE_LEAF \
+ case ELEM_PLACEHOLDER: \
+ case ELEM_COMMENT: \
+ case ELEM_COIL: \
+ case ELEM_CONTACTS: \
+ case ELEM_TON: \
+ case ELEM_TOF: \
+ case ELEM_RTO: \
+ case ELEM_CTD: \
+ case ELEM_CTU: \
+ case ELEM_CTC: \
+ case ELEM_RES: \
+ 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: \
+ case ELEM_ADD: \
+ case ELEM_SUB: \
+ case ELEM_MUL: \
+ case ELEM_DIV: \
+ case ELEM_MOVE: \
+ case ELEM_SHORT: \
+ case ELEM_OPEN: \
+ case ELEM_READ_ADC: \
+ case ELEM_SET_PWM: \
+ case ELEM_UART_SEND: \
+ case ELEM_UART_RECV: \
+ case ELEM_MASTER_RELAY: \
+ case ELEM_SHIFT_REGISTER: \
+ case ELEM_LOOK_UP_TABLE: \
+ case ELEM_PIECEWISE_LINEAR: \
+ case ELEM_FORMATTED_STRING: \
+ case ELEM_PERSIST:
+
+#define MAX_NAME_LEN 128
+#define MAX_COMMENT_LEN 384
+#define MAX_LOOK_UP_TABLE_LEN 60
+
+typedef struct ElemSubckParallelTag ElemSubcktParallel;
+
+typedef struct ElemCommentTag {
+ char str[MAX_COMMENT_LEN];
+} ElemComment;
+
+typedef struct ElemContactsTag {
+ char name[MAX_NAME_LEN];
+ BOOL negated;
+} ElemContacts;
+
+typedef struct ElemCoilTag {
+ char name[MAX_NAME_LEN];
+ BOOL negated;
+ BOOL setOnly;
+ BOOL resetOnly;
+} ElemCoil;
+
+typedef struct ElemTimeTag {
+ char name[MAX_NAME_LEN];
+ int delay;
+} ElemTimer;
+
+typedef struct ElemResetTag {
+ char name[MAX_NAME_LEN];
+} ElemReset;
+
+typedef struct ElemMoveTag {
+ char src[MAX_NAME_LEN];
+ char dest[MAX_NAME_LEN];
+} ElemMove;
+
+typedef struct ElemMathTag {
+ char op1[MAX_NAME_LEN];
+ char op2[MAX_NAME_LEN];
+ char dest[MAX_NAME_LEN];
+} ElemMath;
+
+typedef struct ElemCmpTag {
+ char op1[MAX_NAME_LEN];
+ char op2[MAX_NAME_LEN];
+} ElemCmp;
+
+typedef struct ElemCounterTag {
+ char name[MAX_NAME_LEN];
+ int max;
+} ElemCounter;
+
+typedef struct ElemReadAdcTag {
+ char name[MAX_NAME_LEN];
+} ElemReadAdc;
+
+typedef struct ElemSetPwmTag {
+ char name[MAX_NAME_LEN];
+ int targetFreq;
+} ElemSetPwm;
+
+typedef struct ElemUartTag {
+ char name[MAX_NAME_LEN];
+} ElemUart;
+
+typedef struct ElemShiftRegisterTag {
+ char name[MAX_NAME_LEN];
+ int stages;
+} ElemShiftRegister;
+
+typedef struct ElemLookUpTableTag {
+ char dest[MAX_NAME_LEN];
+ char index[MAX_NAME_LEN];
+ int count;
+ BOOL editAsString;
+ SWORD vals[MAX_LOOK_UP_TABLE_LEN];
+} ElemLookUpTable;
+
+typedef struct ElemPiecewiseLinearTag {
+ char dest[MAX_NAME_LEN];
+ char index[MAX_NAME_LEN];
+ int count;
+ SWORD vals[MAX_LOOK_UP_TABLE_LEN];
+} ElemPiecewiseLinear;
+
+typedef struct ElemFormattedStringTag {
+ char var[MAX_NAME_LEN];
+ char string[MAX_LOOK_UP_TABLE_LEN];
+} ElemFormattedString;
+
+typedef struct ElemPerisistTag {
+ char var[MAX_NAME_LEN];
+} ElemPersist;
+
+#define SELECTED_NONE 0
+#define SELECTED_ABOVE 1
+#define SELECTED_BELOW 2
+#define SELECTED_RIGHT 3
+#define SELECTED_LEFT 4
+typedef struct ElemLeafTag {
+ int selectedState;
+ BOOL poweredAfter;
+ union {
+ ElemComment comment;
+ ElemContacts contacts;
+ ElemCoil coil;
+ ElemTimer timer;
+ ElemReset reset;
+ ElemMove move;
+ ElemMath math;
+ ElemCmp cmp;
+ ElemCounter counter;
+ ElemReadAdc readAdc;
+ ElemSetPwmTag setPwm;
+ ElemUart uart;
+ ElemShiftRegister shiftRegister;
+ ElemFormattedString fmtdStr;
+ ElemLookUpTable lookUpTable;
+ ElemPiecewiseLinear piecewiseLinear;
+ ElemPersist persist;
+ } d;
+} ElemLeaf;
+
+typedef struct ElemSubcktSeriesTag {
+ struct {
+ int which;
+ union {
+ void *any;
+ ElemSubcktParallel *parallel;
+ ElemLeaf *leaf;
+ } d;
+ } contents[MAX_ELEMENTS_IN_SUBCKT];
+ int count;
+} ElemSubcktSeries;
+
+typedef struct ElemSubckParallelTag {
+ struct {
+ int which;
+ union {
+ void *any;
+ ElemSubcktSeries *series;
+ ElemLeaf *leaf;
+ } d;
+ } contents[MAX_ELEMENTS_IN_SUBCKT];
+ int count;
+} ElemSubcktParallel;
+
+typedef struct McuIoInfoTag McuIoInfo;
+
+typedef struct PlcProgramSingleIoTag {
+ char name[MAX_NAME_LEN];
+#define IO_TYPE_PENDING 0
+
+#define IO_TYPE_DIG_INPUT 1
+#define IO_TYPE_DIG_OUTPUT 2
+#define IO_TYPE_READ_ADC 3
+#define IO_TYPE_UART_TX 4
+#define IO_TYPE_UART_RX 5
+#define IO_TYPE_PWM_OUTPUT 6
+#define IO_TYPE_INTERNAL_RELAY 7
+#define IO_TYPE_TON 8
+#define IO_TYPE_TOF 9
+#define IO_TYPE_RTO 10
+#define IO_TYPE_COUNTER 11
+#define IO_TYPE_GENERAL 12
+ int type;
+#define NO_PIN_ASSIGNED 0
+ int pin;
+} PlcProgramSingleIo;
+
+#define MAX_IO 512
+typedef struct PlcProgramTag {
+ struct {
+ PlcProgramSingleIo assignment[MAX_IO];
+ int count;
+ } io;
+ McuIoInfo *mcu;
+ int cycleTime;
+ int mcuClock;
+ int baudRate;
+
+#define MAX_RUNGS 99
+ ElemSubcktSeries *rungs[MAX_RUNGS];
+ BOOL rungPowered[MAX_RUNGS];
+ int numRungs;
+} PlcProgram;
+
+//-----------------------------------------------
+// For actually drawing the ladder logic on screen; constants that determine
+// how the boxes are laid out in the window, need to know that lots of
+// places for figuring out if a mouse click is in a box etc.
+
+// dimensions, in characters, of the area reserved for 1 leaf element
+#define POS_WIDTH 17
+#define POS_HEIGHT 3
+
+// offset from the top left of the window at which we start drawing, in pixels
+#define X_PADDING 35
+#define Y_PADDING 14
+
+typedef struct PlcCursorTag {
+ int left;
+ int top;
+ int width;
+ int height;
+} PlcCursor;
+
+//-----------------------------------------------
+// The syntax highlighting style colours; a structure for the palette.
+
+typedef struct SyntaxHighlightingColoursTag {
+ COLORREF bg; // background
+ COLORREF def; // default foreground
+ COLORREF selected; // selected element
+ COLORREF op; // `op code' (like OSR, OSF, ADD, ...)
+ COLORREF punct; // punctuation, like square or curly braces
+ COLORREF lit; // a literal number
+ COLORREF name; // the name of an item
+ COLORREF rungNum; // rung numbers
+ COLORREF comment; // user-written comment text
+
+ COLORREF bus; // the `bus' at the right and left of screen
+
+ COLORREF simBg; // background, simulation mode
+ COLORREF simRungNum; // rung number, simulation mode
+ COLORREF simOff; // de-energized element, simulation mode
+ COLORREF simOn; // energzied element, simulation mode
+ COLORREF simBusLeft; // the `bus,' can be different colours for
+ COLORREF simBusRight; // right and left of the screen
+} SyntaxHighlightingColours;
+extern SyntaxHighlightingColours HighlightColours;
+
+//-----------------------------------------------
+// Processor definitions. These tables tell us where to find the I/Os on
+// a processor, what bit in what register goes with what pin, etc. There
+// is one master SupportedMcus table, which contains entries for each
+// supported microcontroller.
+
+typedef struct McuIoPinInfoTag {
+ char port;
+ int bit;
+ int pin;
+} McuIoPinInfo;
+
+typedef struct McuAdcPinInfoTag {
+ int pin;
+ BYTE muxRegValue;
+} McuAdcPinInfo;
+
+#define ISA_AVR 0x00
+#define ISA_PIC16 0x01
+#define ISA_ANSIC 0x02
+#define ISA_INTERPRETED 0x03
+#define ISA_ARDUINO 0x04
+
+#define MAX_IO_PORTS 10
+#define MAX_RAM_SECTIONS 5
+
+typedef struct McuIoInfoTag {
+ char *mcuName;
+ char portPrefix;
+ DWORD inputRegs[MAX_IO_PORTS]; // a is 0, j is 9
+ DWORD outputRegs[MAX_IO_PORTS];
+ DWORD dirRegs[MAX_IO_PORTS];
+ DWORD flashWords;
+ struct {
+ DWORD start;
+ int len;
+ } ram[MAX_RAM_SECTIONS];
+ McuIoPinInfo *pinInfo;
+ int pinCount;
+ McuAdcPinInfo *adcInfo;
+ int adcCount;
+ int adcMax;
+ struct {
+ int rxPin;
+ int txPin;
+ } uartNeeds;
+ int pwmNeedsPin;
+ int whichIsa;
+ BOOL avrUseIjmp;
+ DWORD configurationWord;
+} McuIoInfo;
+
+#define NUM_SUPPORTED_MCUS 16
+
+
+//-----------------------------------------------
+// Function prototypes
+
+// ldmicro.cpp
+void ProgramChanged(void);
+void SetMenusEnabled(BOOL canNegate, BOOL canNormal, BOOL canResetOnly,
+ BOOL canSetOnly, BOOL canDelete, BOOL canInsertEnd, BOOL canInsertOther,
+ BOOL canPushRungDown, BOOL canPushRungUp, BOOL canInsertComment);
+void SetUndoEnabled(BOOL undoEnabled, BOOL redoEnabled);
+void RefreshScrollbars(void);
+extern HINSTANCE Instance;
+extern HWND MainWindow;
+extern HDC Hdc;
+extern PlcProgram Prog;
+extern char CurrentSaveFile[MAX_PATH];
+extern char CurrentCompileFile[MAX_PATH];
+extern McuIoInfo SupportedMcus[NUM_SUPPORTED_MCUS];
+// memory debugging, because I often get careless; ok() will check that the
+// heap used for all the program storage is not yet corrupt, and oops() if
+// it is
+void CheckHeap(char *file, int line);
+#define ok() CheckHeap(__FILE__, __LINE__)
+
+// maincontrols.cpp
+void MakeMainWindowControls(void);
+HMENU MakeMainWindowMenus(void);
+void VscrollProc(WPARAM wParam);
+void HscrollProc(WPARAM wParam);
+void GenerateIoListDontLoseSelection(void);
+void RefreshControlsToSettings(void);
+void MainWindowResized(void);
+void ToggleSimulationMode(void);
+void StopSimulation(void);
+void StartSimulation(void);
+void UpdateMainWindowTitleBar(void);
+extern int ScrollWidth;
+extern int ScrollHeight;
+extern BOOL NeedHoriz;
+extern HWND IoList;
+extern int IoListTop;
+extern int IoListHeight;
+
+// draw.cpp
+int ProgCountWidestRow(void);
+int CountHeightOfElement(int which, void *elem);
+BOOL DrawElement(int which, void *elem, int *cx, int *cy, BOOL poweredBefore);
+void DrawEndRung(int cx, int cy);
+extern int ColsAvailable;
+extern BOOL SelectionActive;
+extern BOOL ThisHighlighted;
+
+// draw_outputdev.cpp
+extern void (*DrawChars)(int, int, char *);
+void CALLBACK BlinkCursor(HWND hwnd, UINT msg, UINT_PTR id, DWORD time);
+void PaintWindow(void);
+void ExportDrawingAsText(char *file);
+void InitForDrawing(void);
+void SetUpScrollbars(BOOL *horizShown, SCROLLINFO *horiz, SCROLLINFO *vert);
+int ScreenRowsAvailable(void);
+int ScreenColsAvailable(void);
+extern HFONT FixedWidthFont;
+extern HFONT FixedWidthFontBold;
+extern int SelectedGxAfterNextPaint;
+extern int SelectedGyAfterNextPaint;
+extern BOOL ScrollSelectedIntoViewAfterNextPaint;
+extern int ScrollXOffset;
+extern int ScrollYOffset;
+extern int ScrollXOffsetMax;
+extern int ScrollYOffsetMax;
+
+// schematic.cpp
+void SelectElement(int gx, int gy, int state);
+void MoveCursorKeyboard(int keyCode);
+void MoveCursorMouseClick(int x, int y);
+BOOL MoveCursorTopLeft(void);
+void EditElementMouseDoubleclick(int x, int y);
+void EditSelectedElement(void);
+void MakeResetOnlySelected(void);
+void MakeSetOnlySelected(void);
+void MakeNormalSelected(void);
+void NegateSelected(void);
+void ForgetFromGrid(void *p);
+void ForgetEverything(void);
+void WhatCanWeDoFromCursorAndTopology(void);
+BOOL FindSelected(int *gx, int *gy);
+void MoveCursorNear(int gx, int gy);
+
+#define DISPLAY_MATRIX_X_SIZE 16
+#define DISPLAY_MATRIX_Y_SIZE 512
+extern ElemLeaf *DisplayMatrix[DISPLAY_MATRIX_X_SIZE][DISPLAY_MATRIX_Y_SIZE];
+extern int DisplayMatrixWhich[DISPLAY_MATRIX_X_SIZE][DISPLAY_MATRIX_Y_SIZE];
+extern ElemLeaf DisplayMatrixFiller;
+#define PADDING_IN_DISPLAY_MATRIX (&DisplayMatrixFiller)
+#define VALID_LEAF(x) ((x) != NULL && (x) != PADDING_IN_DISPLAY_MATRIX)
+extern ElemLeaf *Selected;
+extern int SelectedWhich;
+
+extern PlcCursor Cursor;
+extern BOOL CanInsertEnd;
+extern BOOL CanInsertOther;
+extern BOOL CanInsertComment;
+
+// circuit.cpp
+void AddTimer(int which);
+void AddCoil(void);
+void AddContact(void);
+void AddEmpty(int which);
+void AddMove(void);
+void AddMath(int which);
+void AddCmp(int which);
+void AddReset(void);
+void AddCounter(int which);
+void AddReadAdc(void);
+void AddSetPwm(void);
+void AddUart(int which);
+void AddPersist(void);
+void AddComment(char *text);
+void AddShiftRegister(void);
+void AddMasterRelay(void);
+void AddLookUpTable(void);
+void AddPiecewiseLinear(void);
+void AddFormattedString(void);
+void DeleteSelectedFromProgram(void);
+void DeleteSelectedRung(void);
+void InsertRung(BOOL afterCursor);
+int RungContainingSelected(void);
+BOOL ItemIsLastInCircuit(ElemLeaf *item);
+BOOL UartFunctionUsed(void);
+BOOL PwmFunctionUsed(void);
+void PushRungUp(void);
+void PushRungDown(void);
+void NewProgram(void);
+ElemLeaf *AllocLeaf(void);
+ElemSubcktSeries *AllocSubcktSeries(void);
+ElemSubcktParallel *AllocSubcktParallel(void);
+void FreeCircuit(int which, void *any);
+void FreeEntireProgram(void);
+void UndoUndo(void);
+void UndoRedo(void);
+void UndoRemember(void);
+void UndoFlush(void);
+BOOL CanUndo(void);
+
+// loadsave.cpp
+BOOL LoadProjectFromFile(char *filename);
+BOOL SaveProjectToFile(char *filename);
+
+// iolist.cpp
+int GenerateIoList(int prevSel);
+void SaveIoListToFile(FILE *f);
+BOOL LoadIoListFromFile(FILE *f);
+void ShowIoDialog(int item);
+void IoListProc(NMHDR *h);
+void ShowAnalogSliderPopup(char *name);
+
+// commentdialog.cpp
+void ShowCommentDialog(char *comment);
+// contactsdialog.cpp
+void ShowContactsDialog(BOOL *negated, char *name);
+// coildialog.cpp
+void ShowCoilDialog(BOOL *negated, BOOL *setOnly, BOOL *resetOnly, char *name);
+// simpledialog.cpp
+void ShowTimerDialog(int which, int *delay, char *name);
+void ShowCounterDialog(int which, int *count, char *name);
+void ShowMoveDialog(char *dest, char *src);
+void ShowReadAdcDialog(char *name);
+void ShowSetPwmDialog(char *name, int *targetFreq);
+void ShowPersistDialog(char *var);
+void ShowUartDialog(int which, char *name);
+void ShowCmpDialog(int which, char *op1, char *op2);
+void ShowMathDialog(int which, char *dest, char *op1, char *op2);
+void ShowShiftRegisterDialog(char *name, int *stages);
+void ShowFormattedStringDialog(char *var, char *string);
+void ShowLookUpTableDialog(ElemLeaf *l);
+void ShowPiecewiseLinearDialog(ElemLeaf *l);
+void ShowResetDialog(char *name);
+// confdialog.cpp
+void ShowConfDialog(void);
+// helpdialog.cpp
+void ShowHelpDialog(BOOL about);
+
+// miscutil.cpp
+#define oops() { \
+ dbp("bad at %d %s\n", __LINE__, __FILE__); \
+ Error("Internal error at line %d file '%s'\n", __LINE__, __FILE__); \
+ exit(1); \
+ }
+void dbp(char *str, ...);
+void Error(char *str, ...);
+void *CheckMalloc(size_t n);
+void CheckFree(void *p);
+extern HANDLE MainHeap;
+void StartIhex(FILE *f);
+void WriteIhex(FILE *f, BYTE b);
+void FinishIhex(FILE *f);
+char *IoTypeToString(int ioType);
+void PinNumberForIo(char *dest, PlcProgramSingleIo *io);
+HWND CreateWindowClient(DWORD exStyle, char *className, char *windowName,
+ DWORD style, int x, int y, int width, int height, HWND parent,
+ HMENU menu, HINSTANCE instance, void *param);
+void MakeDialogBoxClass(void);
+void NiceFont(HWND h);
+void FixedFont(HWND h);
+void CompileSuccessfulMessage(char *str);
+extern BOOL RunningInBatchMode;
+extern HFONT MyNiceFont;
+extern HFONT MyFixedFont;
+extern HWND OkButton;
+extern HWND CancelButton;
+extern BOOL DialogDone;
+extern BOOL DialogCancel;
+
+// lang.cpp
+char *_(char *in);
+
+// simulate.cpp
+void SimulateOneCycle(BOOL forceRefresh);
+void CALLBACK PlcCycleTimer(HWND hwnd, UINT msg, UINT_PTR id, DWORD time);
+void StartSimulationTimer(void);
+void ClearSimulationData(void);
+void DescribeForIoList(char *name, char *out);
+void SimulationToggleContact(char *name);
+void SetAdcShadow(char *name, SWORD val);
+SWORD GetAdcShadow(char *name);
+void DestroyUartSimulationWindow(void);
+void ShowUartSimulationWindow(void);
+extern BOOL InSimulationMode;
+extern BOOL SimulateRedrawAfterNextCycle;
+
+// compilecommon.cpp
+void AllocStart(void);
+DWORD AllocOctetRam(void);
+void AllocBitRam(DWORD *addr, int *bit);
+void MemForVariable(char *name, DWORD *addrl, DWORD *addrh);
+BYTE MuxForAdcVariable(char *name);
+void MemForSingleBit(char *name, BOOL forRead, DWORD *addr, int *bit);
+void MemCheckForErrorsPostCompile(void);
+void BuildDirectionRegisters(BYTE *isInput, BYTE *isOutput);
+void ComplainAboutBaudRateError(int divisor, double actual, double err);
+void ComplainAboutBaudRateOverflow(void);
+#define CompileError() longjmp(CompileErrorBuf, 1)
+extern jmp_buf CompileErrorBuf;
+
+// intcode.cpp
+void IntDumpListing(char *outFile);
+BOOL GenerateIntermediateCode(void);
+// pic16.cpp
+void CompilePic16(char *outFile);
+// avr.cpp
+void CompileAvr(char *outFile);
+// ansic.cpp
+void CompileAnsiC(char *outFile);
+// interpreted.c
+void CompileInterpreted(char *outFile);
+//Arduino.cpp
+void CompileArduino(char *outFile);
+
+#endif
diff --git a/ldmicro/ldmicro.ico b/ldmicro/ldmicro.ico
new file mode 100644
index 0000000..ed8e58d
Binary files /dev/null and b/ldmicro/ldmicro.ico differ
diff --git a/ldmicro/ldmicro.pdb b/ldmicro/ldmicro.pdb
new file mode 100644
index 0000000..ec8416e
Binary files /dev/null and b/ldmicro/ldmicro.pdb differ
diff --git a/ldmicro/ldmicro.rc b/ldmicro/ldmicro.rc
new file mode 100644
index 0000000..c3fad05
--- /dev/null
+++ b/ldmicro/ldmicro.rc
@@ -0,0 +1,6 @@
+
+// we need a manifest if we want visual styles; put in numbers since somethings a bit screwy
+// with my SDK install (I don't think I've got *.rh right)
+1 24 "ldmicro.exe.manifest"
+
+4000 ICON "ldmicro.ico"
diff --git a/ldmicro/loadsave.cpp b/ldmicro/loadsave.cpp
new file mode 100644
index 0000000..350b5cd
--- /dev/null
+++ b/ldmicro/loadsave.cpp
@@ -0,0 +1,649 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Load/save the circuit from/to a file in a nice ASCII format.
+// Jonathan Westhues, Nov 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+static ElemSubcktSeries *LoadSeriesFromFile(FILE *f);
+
+//-----------------------------------------------------------------------------
+// Check a line of text from a saved project file to determine whether it
+// contains a leaf element (coil, contacts, etc.). If so, create an element
+// for and save that in *any and *which, and return TRUE, else return FALSE.
+//-----------------------------------------------------------------------------
+static BOOL LoadLeafFromFile(char *line, void **any, int *which)
+{
+ ElemLeaf *l = AllocLeaf();
+ int x;
+
+ if(memcmp(line, "COMMENT", 7)==0) {
+ char *s = line + 8;
+ int i = 0;
+ while(*s && *s != '\n') {
+ if(*s == '\\') {
+ if(s[1] == 'n') {
+ l->d.comment.str[i++] = '\n';
+ s++;
+ } else if(s[1] == 'r') {
+ l->d.comment.str[i++] = '\r';
+ s++;
+ } else if(s[1] == '\\') {
+ l->d.comment.str[i++] = '\\';
+ s++;
+ } else {
+ // that is odd
+ }
+ } else {
+ l->d.comment.str[i++] = *s;
+ }
+ s++;
+ }
+ l->d.comment.str[i++] = '\0';
+ *which = ELEM_COMMENT;
+ } else if(sscanf(line, "CONTACTS %s %d", l->d.contacts.name,
+ &l->d.contacts.negated)==2)
+ {
+ *which = ELEM_CONTACTS;
+ } else if(sscanf(line, "COIL %s %d %d %d", l->d.coil.name,
+ &l->d.coil.negated, &l->d.coil.setOnly, &l->d.coil.resetOnly)==4)
+ {
+ *which = ELEM_COIL;
+ } else if(memcmp(line, "PLACEHOLDER", 11)==0) {
+ *which = ELEM_PLACEHOLDER;
+ } else if(memcmp(line, "SHORT", 5)==0) {
+ *which = ELEM_SHORT;
+ } else if(memcmp(line, "OPEN", 4)==0) {
+ *which = ELEM_OPEN;
+ } else if(memcmp(line, "MASTER_RELAY", 12)==0) {
+ *which = ELEM_MASTER_RELAY;
+ } else if(sscanf(line, "SHIFT_REGISTER %s %d", l->d.shiftRegister.name,
+ &(l->d.shiftRegister.stages))==2)
+ {
+ *which = ELEM_SHIFT_REGISTER;
+ } else if(memcmp(line, "OSR", 3)==0) {
+ *which = ELEM_ONE_SHOT_RISING;
+ } else if(memcmp(line, "OSF", 3)==0) {
+ *which = ELEM_ONE_SHOT_FALLING;
+ } else if((sscanf(line, "TON %s %d", l->d.timer.name,
+ &l->d.timer.delay)==2))
+ {
+ *which = ELEM_TON;
+ } else if((sscanf(line, "TOF %s %d", l->d.timer.name,
+ &l->d.timer.delay)==2))
+ {
+ *which = ELEM_TOF;
+ } else if((sscanf(line, "RTO %s %d", l->d.timer.name,
+ &l->d.timer.delay)==2))
+ {
+ *which = ELEM_RTO;
+ } else if((sscanf(line, "CTD %s %d", l->d.counter.name,
+ &l->d.counter.max)==2))
+ {
+ *which = ELEM_CTD;
+ } else if((sscanf(line, "CTU %s %d", l->d.counter.name,
+ &l->d.counter.max)==2))
+ {
+ *which = ELEM_CTU;
+ } else if((sscanf(line, "CTC %s %d", l->d.counter.name,
+ &l->d.counter.max)==2))
+ {
+ *which = ELEM_CTC;
+ } else if(sscanf(line, "RES %s", l->d.reset.name)==1) {
+ *which = ELEM_RES;
+ } else if(sscanf(line, "MOVE %s %s", l->d.move.dest, l->d.move.src)==2) {
+ *which = ELEM_MOVE;
+ } else if(sscanf(line, "ADD %s %s %s", l->d.math.dest, l->d.math.op1,
+ l->d.math.op2)==3)
+ {
+ *which = ELEM_ADD;
+ } else if(sscanf(line, "SUB %s %s %s", l->d.math.dest, l->d.math.op1,
+ l->d.math.op2)==3)
+ {
+ *which = ELEM_SUB;
+ } else if(sscanf(line, "MUL %s %s %s", l->d.math.dest, l->d.math.op1,
+ l->d.math.op2)==3)
+ {
+ *which = ELEM_MUL;
+ } else if(sscanf(line, "DIV %s %s %s", l->d.math.dest, l->d.math.op1,
+ l->d.math.op2)==3)
+ {
+ *which = ELEM_DIV;
+ } else if(sscanf(line, "EQU %s %s", l->d.cmp.op1, l->d.cmp.op2)==2) {
+ *which = ELEM_EQU;
+ } else if(sscanf(line, "NEQ %s %s", l->d.cmp.op1, l->d.cmp.op2)==2) {
+ *which = ELEM_NEQ;
+ } else if(sscanf(line, "GRT %s %s", l->d.cmp.op1, l->d.cmp.op2)==2) {
+ *which = ELEM_GRT;
+ } else if(sscanf(line, "GEQ %s %s", l->d.cmp.op1, l->d.cmp.op2)==2) {
+ *which = ELEM_GEQ;
+ } else if(sscanf(line, "LEQ %s %s", l->d.cmp.op1, l->d.cmp.op2)==2) {
+ *which = ELEM_LEQ;
+ } else if(sscanf(line, "LES %s %s", l->d.cmp.op1, l->d.cmp.op2)==2) {
+ *which = ELEM_LES;
+ } else if(sscanf(line, "READ_ADC %s", l->d.readAdc.name)==1) {
+ *which = ELEM_READ_ADC;
+ } else if(sscanf(line, "SET_PWM %s %d", l->d.setPwm.name,
+ &(l->d.setPwm.targetFreq))==2)
+ {
+ *which = ELEM_SET_PWM;
+ } else if(sscanf(line, "UART_RECV %s", l->d.uart.name)==1) {
+ *which = ELEM_UART_RECV;
+ } else if(sscanf(line, "UART_SEND %s", l->d.uart.name)==1) {
+ *which = ELEM_UART_SEND;
+ } else if(sscanf(line, "PERSIST %s", l->d.persist.var)==1) {
+ *which = ELEM_PERSIST;
+ } else if(sscanf(line, "FORMATTED_STRING %s %d", l->d.fmtdStr.var,
+ &x)==2)
+ {
+ if(strcmp(l->d.fmtdStr.var, "(none)")==0) {
+ strcpy(l->d.fmtdStr.var, "");
+ }
+
+ char *p = line;
+ int i;
+ for(i = 0; i < 3; i++) {
+ while(!isspace(*p)) p++;
+ while( isspace(*p)) p++;
+ }
+ for(i = 0; i < x; i++) {
+ l->d.fmtdStr.string[i] = atoi(p);
+ if(l->d.fmtdStr.string[i] < 32) {
+ l->d.fmtdStr.string[i] = 'X';
+ }
+ while(!isspace(*p) && *p) p++;
+ while( isspace(*p) && *p) p++;
+ }
+ l->d.fmtdStr.string[i] = '\0';
+
+ *which = ELEM_FORMATTED_STRING;
+ } else if(sscanf(line, "LOOK_UP_TABLE %s %s %d %d", l->d.lookUpTable.dest,
+ l->d.lookUpTable.index, &(l->d.lookUpTable.count),
+ &(l->d.lookUpTable.editAsString))==4)
+ {
+ char *p = line;
+ int i;
+ // First skip over the parts that we already sscanf'd.
+ for(i = 0; i < 5; i++) {
+ while((!isspace(*p)) && *p)
+ p++;
+ while(isspace(*p) && *p)
+ p++;
+ }
+ // Then copy over the look-up table entries.
+ for(i = 0; i < l->d.lookUpTable.count; i++) {
+ l->d.lookUpTable.vals[i] = atoi(p);
+ while((!isspace(*p)) && *p)
+ p++;
+ while(isspace(*p) && *p)
+ p++;
+ }
+ *which = ELEM_LOOK_UP_TABLE;
+ } else if(sscanf(line, "PIECEWISE_LINEAR %s %s %d",
+ l->d.piecewiseLinear.dest, l->d.piecewiseLinear.index,
+ &(l->d.piecewiseLinear.count))==3)
+ {
+ char *p = line;
+ int i;
+ // First skip over the parts that we already sscanf'd.
+ for(i = 0; i < 4; i++) {
+ while((!isspace(*p)) && *p)
+ p++;
+ while(isspace(*p) && *p)
+ p++;
+ }
+ // Then copy over the piecewise linear points.
+ for(i = 0; i < l->d.piecewiseLinear.count*2; i++) {
+ l->d.piecewiseLinear.vals[i] = atoi(p);
+ while((!isspace(*p)) && *p)
+ p++;
+ while(isspace(*p) && *p)
+ p++;
+ }
+ *which = ELEM_PIECEWISE_LINEAR;
+ } else {
+ // that's odd; nothing matched
+ CheckFree(l);
+ return FALSE;
+ }
+ *any = l;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// Load a parallel subcircuit from a file. We look for leaf nodes using
+// LoadLeafFromFile, which we can put directly into the parallel circuit
+// that we're building up, or series subcircuits that we can pass to
+// LoadSeriesFromFile. Returns the parallel subcircuit built up, or NULL if
+// something goes wrong.
+//-----------------------------------------------------------------------------
+static ElemSubcktParallel *LoadParallelFromFile(FILE *f)
+{
+ char line[512];
+ void *any;
+ int which;
+
+ ElemSubcktParallel *ret = AllocSubcktParallel();
+ int cnt = 0;
+
+ for(;;) {
+ if(!fgets(line, sizeof(line), f)) return NULL;
+ char *s = line;
+ while(isspace(*s)) s++;
+
+ if(strcmp(s, "SERIES\n")==0) {
+ which = ELEM_SERIES_SUBCKT;
+ any = LoadSeriesFromFile(f);
+ if(!any) return NULL;
+
+ } else if(LoadLeafFromFile(s, &any, &which)) {
+ // got it
+ } else if(strcmp(s, "END\n")==0) {
+ ret->count = cnt;
+ return ret;
+ } else {
+ return NULL;
+ }
+ ret->contents[cnt].which = which;
+ ret->contents[cnt].d.any = any;
+ cnt++;
+ if(cnt >= MAX_ELEMENTS_IN_SUBCKT) return NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Same as LoadParallelFromFile, but for a series subcircuit. Thus builds
+// a series circuit out of parallel circuits and leaf elements.
+//-----------------------------------------------------------------------------
+static ElemSubcktSeries *LoadSeriesFromFile(FILE *f)
+{
+ char line[512];
+ void *any;
+ int which;
+
+ ElemSubcktSeries *ret = AllocSubcktSeries();
+ int cnt = 0;
+
+ for(;;) {
+ if(!fgets(line, sizeof(line), f)) return NULL;
+ char *s = line;
+ while(isspace(*s)) s++;
+
+ if(strcmp(s, "PARALLEL\n")==0) {
+ which = ELEM_PARALLEL_SUBCKT;
+ any = LoadParallelFromFile(f);
+ if(!any) return NULL;
+
+ } else if(LoadLeafFromFile(s, &any, &which)) {
+ // got it
+ } else if(strcmp(s, "END\n")==0) {
+ ret->count = cnt;
+ return ret;
+ } else {
+ return NULL;
+ }
+ ret->contents[cnt].which = which;
+ ret->contents[cnt].d.any = any;
+ cnt++;
+ if(cnt >= MAX_ELEMENTS_IN_SUBCKT) return NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Load a project from a saved project description files. This describes the
+// program, the target processor, plus certain configuration settings (cycle
+// time, processor clock, etc.). Return TRUE for success, FALSE if anything
+// went wrong.
+//-----------------------------------------------------------------------------
+BOOL LoadProjectFromFile(char *filename)
+{
+ FreeEntireProgram();
+ strcpy(CurrentCompileFile, "");
+
+ FILE *f = fopen(filename, "r");
+ if(!f) return FALSE;
+
+ char line[512];
+ int crystal, cycle, baud;
+
+ while(fgets(line, sizeof(line), f)) {
+ if(strcmp(line, "IO LIST\n")==0) {
+ if(!LoadIoListFromFile(f)) {
+ fclose(f);
+ return FALSE;
+ }
+ } else if(sscanf(line, "CRYSTAL=%d", &crystal)) {
+ Prog.mcuClock = crystal;
+ } else if(sscanf(line, "CYCLE=%d", &cycle)) {
+ Prog.cycleTime = cycle;
+ } else if(sscanf(line, "BAUD=%d", &baud)) {
+ Prog.baudRate = baud;
+ } else if(memcmp(line, "COMPILED=", 9)==0) {
+ line[strlen(line)-1] = '\0';
+ strcpy(CurrentCompileFile, line+9);
+ } else if(memcmp(line, "MICRO=", 6)==0) {
+ line[strlen(line)-1] = '\0';
+ int i;
+ for(i = 0; i < NUM_SUPPORTED_MCUS; i++) {
+ if(strcmp(SupportedMcus[i].mcuName, line+6)==0) {
+ Prog.mcu = &SupportedMcus[i];
+ break;
+ }
+ }
+ if(i == NUM_SUPPORTED_MCUS) {
+ Error(_("Microcontroller '%s' not supported.\r\n\r\n"
+ "Defaulting to no selected MCU."), line+6);
+ }
+ } else if(strcmp(line, "PROGRAM\n")==0) {
+ break;
+ }
+ }
+ if(strcmp(line, "PROGRAM\n") != 0) goto failed;
+
+ int rung;
+ for(rung = 0;;) {
+ if(!fgets(line, sizeof(line), f)) break;
+ if(strcmp(line, "RUNG\n")!=0) goto failed;
+
+ Prog.rungs[rung] = LoadSeriesFromFile(f);
+ if(!Prog.rungs[rung]) goto failed;
+ rung++;
+ }
+ Prog.numRungs = rung;
+
+ fclose(f);
+ return TRUE;
+
+failed:
+ fclose(f);
+ NewProgram();
+ Error(_("File format error; perhaps this program is for a newer version "
+ "of LDmicro?"));
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Helper routine for outputting hierarchical representation of the ladder
+// logic: indent on file f, by depth*4 spaces.
+//-----------------------------------------------------------------------------
+static void Indent(FILE *f, int depth)
+{
+ int i;
+ for(i = 0; i < depth; i++) {
+ fprintf(f, " ");
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Save an element to a file. If it is a leaf, then output a single line
+// describing it and return. If it is a subcircuit, call ourselves
+// recursively (with depth+1, so that the indentation is right) to handle
+// the members of the subcircuit. Special case for depth=0: we do not
+// output the SERIES/END delimiters. This is because the root is delimited
+// by RUNG/END markers output elsewhere.
+//-----------------------------------------------------------------------------
+static void SaveElemToFile(FILE *f, int which, void *any, int depth)
+{
+ ElemLeaf *l = (ElemLeaf *)any;
+ char *s;
+
+ Indent(f, depth);
+
+ switch(which) {
+ case ELEM_PLACEHOLDER:
+ fprintf(f, "PLACEHOLDER\n");
+ break;
+
+ case ELEM_COMMENT: {
+ fprintf(f, "COMMENT ");
+ char *s = l->d.comment.str;
+ for(; *s; s++) {
+ if(*s == '\\') {
+ fprintf(f, "\\\\");
+ } else if(*s == '\n') {
+ fprintf(f, "\\n");
+ } else if(*s == '\r') {
+ fprintf(f, "\\r");
+ } else {
+ fprintf(f, "%c", *s);
+ }
+ }
+ fprintf(f, "\n");
+ break;
+ }
+ case ELEM_OPEN:
+ fprintf(f, "OPEN\n");
+ break;
+
+ case ELEM_SHORT:
+ fprintf(f, "SHORT\n");
+ break;
+
+ case ELEM_MASTER_RELAY:
+ fprintf(f, "MASTER_RELAY\n");
+ break;
+
+ case ELEM_SHIFT_REGISTER:
+ fprintf(f, "SHIFT_REGISTER %s %d\n", l->d.shiftRegister.name,
+ l->d.shiftRegister.stages);
+ break;
+
+ case ELEM_CONTACTS:
+ fprintf(f, "CONTACTS %s %d\n", l->d.contacts.name,
+ l->d.contacts.negated);
+ break;
+
+ case ELEM_COIL:
+ fprintf(f, "COIL %s %d %d %d\n", l->d.coil.name, l->d.coil.negated,
+ l->d.coil.setOnly, l->d.coil.resetOnly);
+ break;
+
+ case ELEM_TON:
+ s = "TON"; goto timer;
+ case ELEM_TOF:
+ s = "TOF"; goto timer;
+ case ELEM_RTO:
+ s = "RTO"; goto timer;
+
+timer:
+ fprintf(f, "%s %s %d\n", s, l->d.timer.name, l->d.timer.delay);
+ break;
+
+ case ELEM_CTU:
+ s = "CTU"; goto counter;
+ case ELEM_CTD:
+ s = "CTD"; goto counter;
+ case ELEM_CTC:
+ s = "CTC"; goto counter;
+
+counter:
+ fprintf(f, "%s %s %d\n", s, l->d.counter.name, l->d.counter.max);
+ break;
+
+ case ELEM_RES:
+ fprintf(f, "RES %s\n", l->d.reset.name);
+ break;
+
+ case ELEM_MOVE:
+ fprintf(f, "MOVE %s %s\n", l->d.move.dest, l->d.move.src);
+ break;
+
+ case ELEM_ADD: s = "ADD"; goto math;
+ case ELEM_SUB: s = "SUB"; goto math;
+ case ELEM_MUL: s = "MUL"; goto math;
+ case ELEM_DIV: s = "DIV"; goto math;
+math:
+ fprintf(f, "%s %s %s %s\n", s, l->d.math.dest, l->d.math.op1,
+ l->d.math.op2);
+ break;
+
+ case ELEM_EQU: s = "EQU"; goto cmp;
+ case ELEM_NEQ: s = "NEQ"; goto cmp;
+ case ELEM_GRT: s = "GRT"; goto cmp;
+ case ELEM_GEQ: s = "GEQ"; goto cmp;
+ case ELEM_LES: s = "LES"; goto cmp;
+ case ELEM_LEQ: s = "LEQ"; goto cmp;
+cmp:
+ fprintf(f, "%s %s %s\n", s, l->d.cmp.op1, l->d.cmp.op2);
+ break;
+
+ case ELEM_ONE_SHOT_RISING:
+ fprintf(f, "OSR\n");
+ break;
+
+ case ELEM_ONE_SHOT_FALLING:
+ fprintf(f, "OSF\n");
+ break;
+
+ case ELEM_READ_ADC:
+ fprintf(f, "READ_ADC %s\n", l->d.readAdc.name);
+ break;
+
+ case ELEM_SET_PWM:
+ fprintf(f, "SET_PWM %s %d\n", l->d.setPwm.name,
+ l->d.setPwm.targetFreq);
+ break;
+
+ case ELEM_UART_RECV:
+ fprintf(f, "UART_RECV %s\n", l->d.uart.name);
+ break;
+
+ case ELEM_UART_SEND:
+ fprintf(f, "UART_SEND %s\n", l->d.uart.name);
+ break;
+
+ case ELEM_PERSIST:
+ fprintf(f, "PERSIST %s\n", l->d.persist.var);
+ break;
+
+ case ELEM_FORMATTED_STRING: {
+ int i;
+ fprintf(f, "FORMATTED_STRING ");
+ if(*(l->d.fmtdStr.var)) {
+ fprintf(f, "%s", l->d.fmtdStr.var);
+ } else {
+ fprintf(f, "(none)");
+ }
+ fprintf(f, " %d", strlen(l->d.fmtdStr.string));
+ for(i = 0; i < (int)strlen(l->d.fmtdStr.string); i++) {
+ fprintf(f, " %d", l->d.fmtdStr.string[i]);
+ }
+ fprintf(f, "\n");
+ break;
+ }
+ case ELEM_LOOK_UP_TABLE: {
+ int i;
+ fprintf(f, "LOOK_UP_TABLE %s %s %d %d", l->d.lookUpTable.dest,
+ l->d.lookUpTable.index, l->d.lookUpTable.count,
+ l->d.lookUpTable.editAsString);
+ for(i = 0; i < l->d.lookUpTable.count; i++) {
+ fprintf(f, " %d", l->d.lookUpTable.vals[i]);
+ }
+ fprintf(f, "\n");
+ break;
+ }
+ case ELEM_PIECEWISE_LINEAR: {
+ int i;
+ fprintf(f, "PIECEWISE_LINEAR %s %s %d", l->d.piecewiseLinear.dest,
+ l->d.piecewiseLinear.index, l->d.piecewiseLinear.count);
+ for(i = 0; i < l->d.piecewiseLinear.count*2; i++) {
+ fprintf(f, " %d", l->d.piecewiseLinear.vals[i]);
+ }
+ fprintf(f, "\n");
+ break;
+ }
+
+ case ELEM_SERIES_SUBCKT: {
+ ElemSubcktSeries *s = (ElemSubcktSeries *)any;
+ int i;
+ if(depth == 0) {
+ fprintf(f, "RUNG\n");
+ } else {
+ fprintf(f, "SERIES\n");
+ }
+ for(i = 0; i < s->count; i++) {
+ SaveElemToFile(f, s->contents[i].which, s->contents[i].d.any,
+ depth+1);
+ }
+ Indent(f, depth);
+ fprintf(f, "END\n");
+ break;
+ }
+
+ case ELEM_PARALLEL_SUBCKT: {
+ ElemSubcktParallel *s = (ElemSubcktParallel *)any;
+ int i;
+ fprintf(f, "PARALLEL\n");
+ for(i = 0; i < s->count; i++) {
+ SaveElemToFile(f, s->contents[i].which, s->contents[i].d.any,
+ depth+1);
+ }
+ Indent(f, depth);
+ fprintf(f, "END\n");
+ break;
+ }
+
+ default:
+ oops();
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Save the program in memory to the given file. Returns TRUE for success,
+// FALSE otherwise.
+//-----------------------------------------------------------------------------
+BOOL SaveProjectToFile(char *filename)
+{
+ FILE *f = fopen(filename, "w");
+ if(!f) return FALSE;
+
+ fprintf(f, "LDmicro0.1\n");
+ if(Prog.mcu) {
+ fprintf(f, "MICRO=%s\n", Prog.mcu->mcuName);
+ }
+ fprintf(f, "CYCLE=%d\n", Prog.cycleTime);
+ fprintf(f, "CRYSTAL=%d\n", Prog.mcuClock);
+ fprintf(f, "BAUD=%d\n", Prog.baudRate);
+ if(strlen(CurrentCompileFile) > 0) {
+ fprintf(f, "COMPILED=%s\n", CurrentCompileFile);
+ }
+
+ fprintf(f, "\n");
+ // list extracted from schematic, but the pin assignments are not
+ fprintf(f, "IO LIST\n", Prog.mcuClock);
+ SaveIoListToFile(f);
+ fprintf(f, "END\n", Prog.mcuClock);
+
+ fprintf(f, "\n", Prog.mcuClock);
+ fprintf(f, "PROGRAM\n", Prog.mcuClock);
+
+ int i;
+ for(i = 0; i < Prog.numRungs; i++) {
+ SaveElemToFile(f, ELEM_SERIES_SUBCKT, Prog.rungs[i], 0);
+ }
+
+ fclose(f);
+ return TRUE;
+}
diff --git a/ldmicro/lutdialog.cpp b/ldmicro/lutdialog.cpp
new file mode 100644
index 0000000..0fd0091
--- /dev/null
+++ b/ldmicro/lutdialog.cpp
@@ -0,0 +1,565 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Dialog for entering the elements of a look-up table. I allow two formats:
+// as a simple list of integer values, or like a string. The lookup table
+// can either be a straight LUT, or one with piecewise linear interpolation
+// in between the points.
+// Jonathan Westhues, Dec 2005
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+static HWND LutDialog;
+
+static HWND AsStringCheckbox;
+static HWND CountTextbox;
+static HWND DestTextbox;
+static HWND IndexTextbox;
+static HWND Labels[3];
+
+static HWND StringTextbox;
+
+static BOOL WasAsString;
+static int WasCount;
+
+static HWND ValuesTextbox[MAX_LOOK_UP_TABLE_LEN];
+static LONG_PTR PrevValuesProc[MAX_LOOK_UP_TABLE_LEN];
+static HWND ValuesLabel[MAX_LOOK_UP_TABLE_LEN];
+
+static SWORD ValuesCache[MAX_LOOK_UP_TABLE_LEN];
+
+static LONG_PTR PrevDestProc;
+static LONG_PTR PrevIndexProc;
+static LONG_PTR PrevCountProc;
+
+//-----------------------------------------------------------------------------
+// Don't allow any characters other than 0-9 and minus in the values.
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK MyNumberProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam)
+{
+ if(msg == WM_CHAR) {
+ if(!(isdigit(wParam) || wParam == '\b' || wParam == '-')) {
+ return 0;
+ }
+ }
+
+ WNDPROC w;
+ int i;
+ for(i = 0; i < MAX_LOOK_UP_TABLE_LEN; i++) {
+ if(hwnd == ValuesTextbox[i]) {
+ w = (WNDPROC)PrevValuesProc[i];
+ break;
+ }
+ }
+ if(i == MAX_LOOK_UP_TABLE_LEN) oops();
+
+ return CallWindowProc(w, hwnd, msg, wParam, lParam);
+}
+
+//-----------------------------------------------------------------------------
+// Don't allow any characters other than 0-9 in the count.
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK MyDigitsProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam)
+{
+ if(msg == WM_CHAR) {
+ if(!(isdigit(wParam) || wParam == '\b')) {
+ return 0;
+ }
+ }
+
+ return CallWindowProc((WNDPROC)PrevCountProc, hwnd, msg, wParam, lParam);
+}
+
+//-----------------------------------------------------------------------------
+// Don't allow any characters other than A-Za-z0-9_ in the name.
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK MyNameProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam)
+{
+ if(msg == WM_CHAR) {
+ if(!(isalpha(wParam) || isdigit(wParam) || wParam == '_' ||
+ wParam == '\b'))
+ {
+ return 0;
+ }
+ }
+
+ WNDPROC w;
+ if(hwnd == DestTextbox) {
+ w = (WNDPROC)PrevDestProc;
+ } else if(hwnd == IndexTextbox) {
+ w = (WNDPROC)PrevIndexProc;
+ }
+ return CallWindowProc(w, hwnd, msg, wParam, lParam);
+}
+
+//-----------------------------------------------------------------------------
+// Make the controls that are guaranteed not to move around as the count/
+// as string settings change. This is different for the piecewise linear,
+// because in that case we should not provide a checkbox to change whether
+// the table is edited as a string or table.
+//-----------------------------------------------------------------------------
+static void MakeFixedControls(BOOL forPwl)
+{
+ Labels[0] = CreateWindowEx(0, WC_STATIC, _("Destination:"),
+ WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SS_RIGHT,
+ 0, 10, 78, 21, LutDialog, NULL, Instance, NULL);
+ NiceFont(Labels[0]);
+
+ DestTextbox = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "",
+ WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 85, 10, 120, 21, LutDialog, NULL, Instance, NULL);
+ FixedFont(DestTextbox);
+
+ Labels[1] = CreateWindowEx(0, WC_STATIC, _("Index:"),
+ WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SS_RIGHT,
+ 10, 40, 68, 21, LutDialog, NULL, Instance, NULL);
+ NiceFont(Labels[1]);
+
+ IndexTextbox = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "",
+ WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 85, 40, 120, 21, LutDialog, NULL, Instance, NULL);
+ FixedFont(IndexTextbox);
+
+ Labels[2] = CreateWindowEx(0,WC_STATIC, forPwl ? _("Points:") : _("Count:"),
+ WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SS_RIGHT,
+ 0, 70, 78, 21, LutDialog, NULL, Instance, NULL);
+ NiceFont(Labels[2]);
+
+ CountTextbox = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "",
+ WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 85, 70, 120, 21, LutDialog, NULL, Instance, NULL);
+ NiceFont(CountTextbox);
+
+ if(!forPwl) {
+ AsStringCheckbox = CreateWindowEx(0, WC_BUTTON,
+ _("Edit table of ASCII values like a string"), WS_CHILD |
+ WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_AUTOCHECKBOX,
+ 10, 100, 300, 21, LutDialog, NULL, Instance, NULL);
+ NiceFont(AsStringCheckbox);
+ }
+
+ OkButton = CreateWindowEx(0, WC_BUTTON, _("OK"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_DEFPUSHBUTTON,
+ 231, 10, 70, 23, LutDialog, NULL, Instance, NULL);
+ NiceFont(OkButton);
+
+ CancelButton = CreateWindowEx(0, WC_BUTTON, _("Cancel"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 231, 40, 70, 23, LutDialog, NULL, Instance, NULL);
+ NiceFont(CancelButton);
+
+ PrevDestProc = SetWindowLongPtr(DestTextbox, GWLP_WNDPROC,
+ (LONG_PTR)MyNameProc);
+ PrevIndexProc = SetWindowLongPtr(IndexTextbox, GWLP_WNDPROC,
+ (LONG_PTR)MyNameProc);
+ PrevCountProc = SetWindowLongPtr(CountTextbox, GWLP_WNDPROC,
+ (LONG_PTR)MyDigitsProc);
+}
+
+//-----------------------------------------------------------------------------
+// Destroy all of the controls so that we can start anew. This is necessary
+// because if the size of the LUT changes, or if the user switches from
+// table entry to string entry, we must completely reconfigure the dialog.
+//-----------------------------------------------------------------------------
+static void DestroyLutControls(void)
+{
+ if(WasAsString) {
+ // Nothing to do; we constantly update the cache from the user-
+ // specified string, because we might as well do that when we
+ // calculate the length.
+ } else {
+ int i;
+ for(i = 0; i < WasCount; i++) {
+ char buf[20];
+ SendMessage(ValuesTextbox[i], WM_GETTEXT, (WPARAM)16, (LPARAM)buf);
+ ValuesCache[i] = atoi(buf);
+ }
+ }
+
+ DestroyWindow(StringTextbox);
+
+ int i;
+ for(i = 0; i < MAX_LOOK_UP_TABLE_LEN; i++) {
+ DestroyWindow(ValuesTextbox[i]);
+ DestroyWindow(ValuesLabel[i]);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Make the controls that hold the LUT. The exact configuration of the dialog
+// will depend on (a) whether the user chose table-type or string-type entry,
+// and for table-type entry, on (b) the number of entries, and on (c)
+// whether we are editing a PWL table (list of points) or a straight LUT.
+//-----------------------------------------------------------------------------
+static void MakeLutControls(BOOL asString, int count, BOOL forPwl)
+{
+ // Remember these, so that we know from where to cache stuff if we have
+ // to destroy these textboxes and make something new later.
+ WasAsString = asString;
+ WasCount = count;
+
+ if(forPwl && asString) oops();
+
+ if(asString) {
+ char str[3*MAX_LOOK_UP_TABLE_LEN+1];
+ int i, j;
+ j = 0;
+ for(i = 0; i < count; i++) {
+ int c = ValuesCache[i];
+ if(c >= 32 && c <= 127 && c != '\\') {
+ str[j++] = c;
+ } else if(c == '\\') {
+ str[j++] = '\\';
+ str[j++] = '\\';
+ } else if(c == '\r') {
+ str[j++] = '\\';
+ str[j++] = 'r';
+ } else if(c == '\b') {
+ str[j++] = '\\';
+ str[j++] = 'b';
+ } else if(c == '\f') {
+ str[j++] = '\\';
+ str[j++] = 'f';
+ } else if(c == '\n') {
+ str[j++] = '\\';
+ str[j++] = 'n';
+ } else {
+ str[j++] = 'X';
+ }
+ }
+ str[j++] = '\0';
+ StringTextbox = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, str,
+ WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS |
+ WS_VISIBLE,
+ 10, 130, 294, 21, LutDialog, NULL, Instance, NULL);
+ FixedFont(StringTextbox);
+ SendMessage(CountTextbox, EM_SETREADONLY, (WPARAM)TRUE, 0);
+ MoveWindow(LutDialog, 100, 30, 320, 185, TRUE);
+ } else {
+ int i;
+ int base;
+ if(forPwl) {
+ base = 100;
+ } else {
+ base = 140;
+ }
+ for(i = 0; i < count; i++) {
+ int x, y;
+
+ if(i < 16) {
+ x = 10;
+ y = base+30*i;
+ } else {
+ x = 160;
+ y = base+30*(i-16);
+ }
+
+ char buf[20];
+ sprintf(buf, "%d", ValuesCache[i]);
+ ValuesTextbox[i] = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, buf,
+ WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS |
+ WS_VISIBLE,
+ x+30, y, 80, 21, LutDialog, NULL, Instance, NULL);
+ NiceFont(ValuesTextbox[i]);
+
+ if(forPwl) {
+ sprintf(buf, "%c%d:", (i & 1) ? 'y' : 'x', i/2);
+ } else {
+ sprintf(buf, "%2d:", i);
+ }
+ ValuesLabel[i] = CreateWindowEx(0, WC_STATIC, buf,
+ WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
+ x, y+3, 100, 21, LutDialog, NULL, Instance, NULL);
+ FixedFont(ValuesLabel[i]);
+
+ PrevValuesProc[i] = SetWindowLongPtr(ValuesTextbox[i],
+ GWLP_WNDPROC, (LONG_PTR)MyNumberProc);
+ }
+ if(count > 16) count = 16;
+ SendMessage(CountTextbox, EM_SETREADONLY, (WPARAM)FALSE, 0);
+
+ MoveWindow(LutDialog, 100, 30, 320, base + 30 + count*30, TRUE);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Decode a string into a look-up table; store the values in ValuesCache[],
+// and update the count checkbox (which in string mode is read-only) to
+// reflect the new length. Returns FALSE if the new string is too long, else
+// TRUE.
+//-----------------------------------------------------------------------------
+BOOL StringToValuesCache(char *str, int *c)
+{
+ int count = 0;
+ while(*str) {
+ if(*str == '\\') {
+ str++;
+ switch(*str) {
+ case 'r': ValuesCache[count++] = '\r'; break;
+ case 'n': ValuesCache[count++] = '\n'; break;
+ case 'f': ValuesCache[count++] = '\f'; break;
+ case 'b': ValuesCache[count++] = '\b'; break;
+ default: ValuesCache[count++] = *str; break;
+ }
+ } else {
+ ValuesCache[count++] = *str;
+ }
+ if(*str) {
+ str++;
+ }
+ if(count >= 32) {
+ return FALSE;
+ }
+ }
+
+ char buf[10];
+ sprintf(buf, "%d", count);
+ SendMessage(CountTextbox, WM_SETTEXT, 0, (LPARAM)(buf));
+ *c = count;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// Show the look-up table dialog. This one is nasty, mostly because there are
+// two ways to enter a look-up table: as a table, or as a string. Presumably
+// I should convert between those two representations on the fly, as the user
+// edit things, so I do.
+//-----------------------------------------------------------------------------
+void ShowLookUpTableDialog(ElemLeaf *l)
+{
+ ElemLookUpTable *t = &(l->d.lookUpTable);
+
+ // First copy over all the stuff from the leaf structure; in particular,
+ // we need our own local copy of the table entries, because it would be
+ // bad to update those in the leaf before the user clicks okay (as he
+ // might cancel).
+ int count = t->count;
+ BOOL asString = t->editAsString;
+ memset(ValuesCache, 0, sizeof(ValuesCache));
+ int i;
+ for(i = 0; i < count; i++) {
+ ValuesCache[i] = t->vals[i];
+ }
+
+ // Now create the dialog's fixed controls, plus the changing (depending
+ // on show style/entry count) controls for the initial configuration.
+ LutDialog = CreateWindowClient(0, "LDmicroDialog",
+ _("Look-Up Table"), WS_OVERLAPPED | WS_SYSMENU,
+ 100, 100, 320, 375, NULL, NULL, Instance, NULL);
+ MakeFixedControls(FALSE);
+ MakeLutControls(asString, count, FALSE);
+
+ // Set up the controls to reflect the initial configuration.
+ SendMessage(DestTextbox, WM_SETTEXT, 0, (LPARAM)(t->dest));
+ SendMessage(IndexTextbox, WM_SETTEXT, 0, (LPARAM)(t->index));
+ char buf[30];
+ sprintf(buf, "%d", t->count);
+ SendMessage(CountTextbox, WM_SETTEXT, 0, (LPARAM)buf);
+ if(asString) {
+ SendMessage(AsStringCheckbox, BM_SETCHECK, BST_CHECKED, 0);
+ }
+
+ // And show the window
+ EnableWindow(MainWindow, FALSE);
+ ShowWindow(LutDialog, TRUE);
+ SetFocus(DestTextbox);
+ SendMessage(DestTextbox, EM_SETSEL, 0, -1);
+
+ char PrevTableAsString[1024] = "";
+
+ MSG msg;
+ DWORD ret;
+ DialogDone = FALSE;
+ DialogCancel = FALSE;
+ while((ret = GetMessage(&msg, NULL, 0, 0)) && !DialogDone) {
+ if(msg.message == WM_KEYDOWN) {
+ if(msg.wParam == VK_RETURN) {
+ DialogDone = TRUE;
+ break;
+ } else if(msg.wParam == VK_ESCAPE) {
+ DialogDone = TRUE;
+ DialogCancel = TRUE;
+ break;
+ }
+ }
+
+ if(!IsDialogMessage(LutDialog, &msg)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ // Are we in table mode? In that case watch the (user-editable) count
+ // field, and use that to determine how many textboxes to show.
+ char buf[20];
+ SendMessage(CountTextbox, WM_GETTEXT, (WPARAM)16, (LPARAM)buf);
+ if(atoi(buf) != count && !asString) {
+ count = atoi(buf);
+ if(count < 0 || count > 32) {
+ count = 0;
+ SendMessage(CountTextbox, WM_SETTEXT, 0, (LPARAM)"");
+ }
+ DestroyLutControls();
+ MakeLutControls(asString, count, FALSE);
+ }
+
+ // Are we in string mode? In that case watch the string textbox,
+ // and use that to update the (read-only) count field.
+ if(asString) {
+ char scratch[1024];
+ SendMessage(StringTextbox, WM_GETTEXT, (WPARAM)sizeof(scratch),
+ (LPARAM)scratch);
+ if(strcmp(scratch, PrevTableAsString)!=0) {
+ if(StringToValuesCache(scratch, &count)) {
+ strcpy(PrevTableAsString, scratch);
+ } else {
+ // Too long; put back the old one
+ SendMessage(StringTextbox, WM_SETTEXT, 0,
+ (LPARAM)PrevTableAsString);
+ }
+ }
+ }
+
+ // Did we just change modes?
+ BOOL x = SendMessage(AsStringCheckbox, BM_GETCHECK, 0, 0)==BST_CHECKED;
+ if((x && !asString) || (!x && asString)) {
+ asString = x;
+ DestroyLutControls();
+ MakeLutControls(asString, count, FALSE);
+ }
+
+ }
+
+ if(!DialogCancel) {
+ SendMessage(DestTextbox, WM_GETTEXT, (WPARAM)16, (LPARAM)(t->dest));
+ SendMessage(IndexTextbox, WM_GETTEXT, (WPARAM)16, (LPARAM)(t->index));
+ DestroyLutControls();
+ // The call to DestroyLutControls updated ValuesCache, so just read
+ // them out of there (whichever mode we were in before).
+ int i;
+ for(i = 0; i < count; i++) {
+ t->vals[i] = ValuesCache[i];
+ }
+ t->count = count;
+ t->editAsString = asString;
+ }
+
+ EnableWindow(MainWindow, TRUE);
+ DestroyWindow(LutDialog);
+}
+
+//-----------------------------------------------------------------------------
+// Show the piecewise linear table dialog. This one can only be edited in
+// only a single format, which makes things easier than before.
+//-----------------------------------------------------------------------------
+void ShowPiecewiseLinearDialog(ElemLeaf *l)
+{
+ ElemPiecewiseLinear *t = &(l->d.piecewiseLinear);
+
+ // First copy over all the stuff from the leaf structure; in particular,
+ // we need our own local copy of the table entries, because it would be
+ // bad to update those in the leaf before the user clicks okay (as he
+ // might cancel).
+ int count = t->count;
+ memset(ValuesCache, 0, sizeof(ValuesCache));
+ int i;
+ for(i = 0; i < count*2; i++) {
+ ValuesCache[i] = t->vals[i];
+ }
+
+ // Now create the dialog's fixed controls, plus the changing (depending
+ // on show style/entry count) controls for the initial configuration.
+ LutDialog = CreateWindowClient(0, "LDmicroDialog",
+ _("Piecewise Linear Table"), WS_OVERLAPPED | WS_SYSMENU,
+ 100, 100, 320, 375, NULL, NULL, Instance, NULL);
+ MakeFixedControls(TRUE);
+ MakeLutControls(FALSE, count*2, TRUE);
+
+ // Set up the controls to reflect the initial configuration.
+ SendMessage(DestTextbox, WM_SETTEXT, 0, (LPARAM)(t->dest));
+ SendMessage(IndexTextbox, WM_SETTEXT, 0, (LPARAM)(t->index));
+ char buf[30];
+ sprintf(buf, "%d", t->count);
+ SendMessage(CountTextbox, WM_SETTEXT, 0, (LPARAM)buf);
+
+ // And show the window
+ EnableWindow(MainWindow, FALSE);
+ ShowWindow(LutDialog, TRUE);
+ SetFocus(DestTextbox);
+ SendMessage(DestTextbox, EM_SETSEL, 0, -1);
+
+ MSG msg;
+ DWORD ret;
+ DialogDone = FALSE;
+ DialogCancel = FALSE;
+ while((ret = GetMessage(&msg, NULL, 0, 0)) && !DialogDone) {
+ if(msg.message == WM_KEYDOWN) {
+ if(msg.wParam == VK_RETURN) {
+ DialogDone = TRUE;
+ break;
+ } else if(msg.wParam == VK_ESCAPE) {
+ DialogDone = TRUE;
+ DialogCancel = TRUE;
+ break;
+ }
+ }
+
+ if(!IsDialogMessage(LutDialog, &msg)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ // Watch the (user-editable) count field, and use that to
+ // determine how many textboxes to show.
+ char buf[20];
+ SendMessage(CountTextbox, WM_GETTEXT, (WPARAM)16, (LPARAM)buf);
+ if(atoi(buf) != count) {
+ count = atoi(buf);
+ if(count < 0 || count > 10) {
+ count = 0;
+ SendMessage(CountTextbox, WM_SETTEXT, 0, (LPARAM)"");
+ }
+ DestroyLutControls();
+ MakeLutControls(FALSE, count*2, TRUE);
+ }
+ }
+
+ if(!DialogCancel) {
+ SendMessage(DestTextbox, WM_GETTEXT, (WPARAM)16, (LPARAM)(t->dest));
+ SendMessage(IndexTextbox, WM_GETTEXT, (WPARAM)16, (LPARAM)(t->index));
+ DestroyLutControls();
+ // The call to DestroyLutControls updated ValuesCache, so just read
+ // them out of there.
+ int i;
+ for(i = 0; i < count*2; i++) {
+ t->vals[i] = ValuesCache[i];
+ }
+ t->count = count;
+ }
+
+ EnableWindow(MainWindow, TRUE);
+ DestroyWindow(LutDialog);
+}
diff --git a/ldmicro/maincontrols.cpp b/ldmicro/maincontrols.cpp
new file mode 100644
index 0000000..25c05f6
--- /dev/null
+++ b/ldmicro/maincontrols.cpp
@@ -0,0 +1,745 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Common controls in the main window. The main window consists of the drawing
+// area, where the ladder diagram is displayed, plus various controls for
+// scrolling, I/O list, menus.
+// Jonathan Westhues, Nov 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+#include
+#include
+#include "ldmicro.h"
+
+// scrollbars for the ladder logic area
+static HWND HorizScrollBar;
+static HWND VertScrollBar;
+int ScrollWidth;
+int ScrollHeight;
+BOOL NeedHoriz;
+
+// status bar at the bottom of the screen, to display settings
+static HWND StatusBar;
+
+// have to get back to the menus to gray/ungray, check/uncheck things
+static HMENU FileMenu;
+static HMENU EditMenu;
+static HMENU InstructionMenu;
+static HMENU ProcessorMenu;
+static HMENU SimulateMenu;
+static HMENU TopMenu;
+
+// listview used to maintain the list of I/O pins with symbolic names, plus
+// the internal relay too
+HWND IoList;
+static int IoListSelectionPoint;
+static BOOL IoListOutOfSync;
+int IoListHeight;
+int IoListTop;
+
+// whether the simulation is running in real time
+static BOOL RealTimeSimulationRunning;
+
+//-----------------------------------------------------------------------------
+// Create the standard Windows controls used in the main window: a Listview
+// for the I/O list, and a status bar for settings.
+//-----------------------------------------------------------------------------
+void MakeMainWindowControls(void)
+{
+ LVCOLUMN lvc;
+ lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
+ lvc.fmt = LVCFMT_LEFT;
+#define LV_ADD_COLUMN(hWnd, i, w, s) do { \
+ lvc.iSubItem = i; \
+ lvc.pszText = s; \
+ lvc.iOrder = 0; \
+ lvc.cx = w; \
+ ListView_InsertColumn(hWnd, i, &lvc); \
+ } while(0)
+ IoList = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, "", WS_CHILD |
+ LVS_REPORT | LVS_NOSORTHEADER | LVS_SHOWSELALWAYS | WS_TABSTOP |
+ LVS_SINGLESEL | WS_CLIPSIBLINGS,
+ 12, 25, 300, 300, MainWindow, NULL, Instance, NULL);
+ ListView_SetExtendedListViewStyle(IoList, LVS_EX_FULLROWSELECT);
+
+ int typeWidth = 85;
+ int pinWidth = 100;
+ int portWidth = 90;
+
+ LV_ADD_COLUMN(IoList, LV_IO_NAME, 250, _("Name"));
+ LV_ADD_COLUMN(IoList, LV_IO_TYPE, typeWidth, _("Type"));
+ LV_ADD_COLUMN(IoList, LV_IO_STATE, 100, _("State"));
+ LV_ADD_COLUMN(IoList, LV_IO_PIN, pinWidth, _("Pin on Processor"));
+ LV_ADD_COLUMN(IoList, LV_IO_PORT, portWidth, _("MCU Port"));
+
+ HorizScrollBar = CreateWindowEx(0, WC_SCROLLBAR, "", WS_CHILD |
+ SBS_HORZ | SBS_BOTTOMALIGN | WS_VISIBLE | WS_CLIPSIBLINGS,
+ 100, 100, 100, 100, MainWindow, NULL, Instance, NULL);
+ VertScrollBar = CreateWindowEx(0, WC_SCROLLBAR, "", WS_CHILD |
+ SBS_VERT | SBS_LEFTALIGN | WS_VISIBLE | WS_CLIPSIBLINGS,
+ 200, 100, 100, 100, MainWindow, NULL, Instance, NULL);
+ RECT scroll;
+ GetWindowRect(HorizScrollBar, &scroll);
+ ScrollHeight = scroll.bottom - scroll.top;
+ GetWindowRect(VertScrollBar, &scroll);
+ ScrollWidth = scroll.right - scroll.left;
+
+ StatusBar = CreateStatusWindow(WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
+ "LDmicro started", MainWindow, 0);
+ int edges[] = { 250, 370, -1 };
+ SendMessage(StatusBar, SB_SETPARTS, 3, (LPARAM)edges);
+
+ ShowWindow(IoList, SW_SHOW);
+}
+
+//-----------------------------------------------------------------------------
+// Set up the title bar text for the main window; indicate whether we are in
+// simulation or editing mode, and indicate the filename.
+//-----------------------------------------------------------------------------
+void UpdateMainWindowTitleBar(void)
+{
+ char line[MAX_PATH+100];
+ if(InSimulationMode) {
+ if(RealTimeSimulationRunning) {
+ strcpy(line, _("LDmicro - Simulation (Running)"));
+ } else {
+ strcpy(line, _("LDmicro - Simulation (Stopped)"));
+ }
+ } else {
+ strcpy(line, _("LDmicro - Program Editor"));
+ }
+ if(strlen(CurrentSaveFile) > 0) {
+ sprintf(line+strlen(line), " - %s", CurrentSaveFile);
+ } else {
+ strcat(line, _(" - (not yet saved)"));
+ }
+
+ SetWindowText(MainWindow, line);
+}
+
+//-----------------------------------------------------------------------------
+// Set the enabled state of the logic menu items to reflect where we are on
+// the schematic (e.g. can't insert two coils in series).
+//-----------------------------------------------------------------------------
+void SetMenusEnabled(BOOL canNegate, BOOL canNormal, BOOL canResetOnly,
+ BOOL canSetOnly, BOOL canDelete, BOOL canInsertEnd, BOOL canInsertOther,
+ BOOL canPushDown, BOOL canPushUp, BOOL canInsertComment)
+{
+ EnableMenuItem(EditMenu, MNU_PUSH_RUNG_UP,
+ canPushUp ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(EditMenu, MNU_PUSH_RUNG_DOWN,
+ canPushDown ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(EditMenu, MNU_DELETE_RUNG,
+ (Prog.numRungs > 1) ? MF_ENABLED : MF_GRAYED);
+
+ EnableMenuItem(InstructionMenu, MNU_NEGATE,
+ canNegate ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(InstructionMenu, MNU_MAKE_NORMAL,
+ canNormal ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(InstructionMenu, MNU_MAKE_RESET_ONLY,
+ canResetOnly ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(InstructionMenu, MNU_MAKE_SET_ONLY,
+ canSetOnly ? MF_ENABLED : MF_GRAYED);
+
+ EnableMenuItem(InstructionMenu, MNU_INSERT_COMMENT,
+ canInsertComment ? MF_ENABLED : MF_GRAYED);
+
+ EnableMenuItem(EditMenu, MNU_DELETE_ELEMENT,
+ canDelete ? MF_ENABLED : MF_GRAYED);
+
+ int t;
+ t = canInsertEnd ? MF_ENABLED : MF_GRAYED;
+ EnableMenuItem(InstructionMenu, MNU_INSERT_COIL, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_RES, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_MOV, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_ADD, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_SUB, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_MUL, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_DIV, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_CTC, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_PERSIST, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_READ_ADC, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_SET_PWM, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_MASTER_RLY, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_SHIFT_REG, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_LUT, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_PWL, t);
+
+ t = canInsertOther ? MF_ENABLED : MF_GRAYED;
+ EnableMenuItem(InstructionMenu, MNU_INSERT_TON, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_TOF, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_OSR, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_OSF, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_RTO, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_CONTACTS, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_CTU, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_CTD, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_EQU, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_NEQ, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_GRT, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_GEQ, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_LES, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_LEQ, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_SHORT, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_OPEN, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_UART_SEND, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_UART_RECV, t);
+ EnableMenuItem(InstructionMenu, MNU_INSERT_FMTD_STR, t);
+}
+
+//-----------------------------------------------------------------------------
+// Set the enabled state of the undo/redo menus.
+//-----------------------------------------------------------------------------
+void SetUndoEnabled(BOOL undoEnabled, BOOL redoEnabled)
+{
+ EnableMenuItem(EditMenu, MNU_UNDO, undoEnabled ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(EditMenu, MNU_REDO, redoEnabled ? MF_ENABLED : MF_GRAYED);
+}
+
+//-----------------------------------------------------------------------------
+// Create the top-level menu bar for the main window. Mostly static, but we
+// create the "select processor" menu from the list in mcutable.h dynamically.
+//-----------------------------------------------------------------------------
+HMENU MakeMainWindowMenus(void)
+{
+ HMENU settings, compile, help;
+ int i;
+
+ FileMenu = CreatePopupMenu();
+ AppendMenu(FileMenu, MF_STRING, MNU_NEW, _("&New\tCtrl+N"));
+ AppendMenu(FileMenu, MF_STRING, MNU_OPEN, _("&Open...\tCtrl+O"));
+ AppendMenu(FileMenu, MF_STRING, MNU_SAVE, _("&Save\tCtrl+S"));
+ AppendMenu(FileMenu, MF_STRING, MNU_SAVE_AS,_("Save &As..."));
+ AppendMenu(FileMenu, MF_SEPARATOR,0, "");
+ AppendMenu(FileMenu, MF_STRING, MNU_EXPORT,
+ _("&Export As Text...\tCtrl+E"));
+ AppendMenu(FileMenu, MF_SEPARATOR,0, "");
+ AppendMenu(FileMenu, MF_STRING, MNU_EXIT, _("E&xit"));
+
+ EditMenu = CreatePopupMenu();
+ AppendMenu(EditMenu, MF_STRING, MNU_UNDO, _("&Undo\tCtrl+Z"));
+ AppendMenu(EditMenu, MF_STRING, MNU_REDO, _("&Redo\tCtrl+Y"));
+ AppendMenu(EditMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(EditMenu, MF_STRING, MNU_INSERT_RUNG_BEFORE,
+ _("Insert Rung &Before\tShift+6"));
+ AppendMenu(EditMenu, MF_STRING, MNU_INSERT_RUNG_AFTER,
+ _("Insert Rung &After\tShift+V"));
+ AppendMenu(EditMenu, MF_STRING, MNU_PUSH_RUNG_UP,
+ _("Move Selected Rung &Up\tShift+Up"));
+ AppendMenu(EditMenu, MF_STRING, MNU_PUSH_RUNG_DOWN,
+ _("Move Selected Rung &Down\tShift+Down"));
+ AppendMenu(EditMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(EditMenu, MF_STRING, MNU_DELETE_ELEMENT,
+ _("&Delete Selected Element\tDel"));
+ AppendMenu(EditMenu, MF_STRING, MNU_DELETE_RUNG,
+ _("D&elete Rung\tShift+Del"));
+
+ InstructionMenu = CreatePopupMenu();
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_COMMENT,
+ _("Insert Co&mment\t;"));
+ AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_CONTACTS,
+ _("Insert &Contacts\tC"));
+ AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_OSR,
+ _("Insert OSR (One Shot Rising)\t&/"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_OSF,
+ _("Insert OSF (One Shot Falling)\t&\\"));
+ AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_TON,
+ _("Insert T&ON (Delayed Turn On)\tO"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_TOF,
+ _("Insert TO&F (Delayed Turn Off)\tF"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_RTO,
+ _("Insert R&TO (Retentive Delayed Turn On)\tT"));
+ AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_CTU,
+ _("Insert CT&U (Count Up)\tU"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_CTD,
+ _("Insert CT&D (Count Down)\tI"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_CTC,
+ _("Insert CT&C (Count Circular)\tJ"));
+ AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_EQU,
+ _("Insert EQU (Compare for Equals)\t="));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_NEQ,
+ _("Insert NEQ (Compare for Not Equals)"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_GRT,
+ _("Insert GRT (Compare for Greater Than)\t>"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_GEQ,
+ _("Insert GEQ (Compare for Greater Than or Equal)\t."));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_LES,
+ _("Insert LES (Compare for Less Than)\t<"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_LEQ,
+ _("Insert LEQ (Compare for Less Than or Equal)\t,"));
+ AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_OPEN,
+ _("Insert Open-Circuit"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_SHORT,
+ _("Insert Short-Circuit"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_MASTER_RLY,
+ _("Insert Master Control Relay"));
+ AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_COIL,
+ _("Insert Coi&l\tL"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_RES,
+ _("Insert R&ES (Counter/RTO Reset)\tE"));
+ AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_MOV,
+ _("Insert MOV (Move)\tM"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_ADD,
+ _("Insert ADD (16-bit Integer Add)\t+"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_SUB,
+ _("Insert SUB (16-bit Integer Subtract)\t-"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_MUL,
+ _("Insert MUL (16-bit Integer Multiply)\t*"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_DIV,
+ _("Insert DIV (16-bit Integer Divide)\tD"));
+ AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_SHIFT_REG,
+ _("Insert Shift Register"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_LUT,
+ _("Insert Look-Up Table"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_PWL,
+ _("Insert Piecewise Linear"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_FMTD_STR,
+ _("Insert Formatted String Over UART"));
+ AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_UART_SEND,
+ _("Insert &UART Send"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_UART_RECV,
+ _("Insert &UART Receive"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_SET_PWM,
+ _("Insert Set PWM Output"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_READ_ADC,
+ _("Insert A/D Converter Read\tP"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_INSERT_PERSIST,
+ _("Insert Make Persistent"));
+ AppendMenu(InstructionMenu, MF_SEPARATOR, 0, NULL);
+ AppendMenu(InstructionMenu, MF_STRING, MNU_MAKE_NORMAL,
+ _("Make Norm&al\tA"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_NEGATE,
+ _("Make &Negated\tN"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_MAKE_SET_ONLY,
+ _("Make &Set-Only\tS"));
+ AppendMenu(InstructionMenu, MF_STRING, MNU_MAKE_RESET_ONLY,
+ _("Make &Reset-Only\tR"));
+
+ settings = CreatePopupMenu();
+ AppendMenu(settings, MF_STRING, MNU_MCU_SETTINGS, _("&MCU Parameters..."));
+ ProcessorMenu = CreatePopupMenu();
+ for(i = 0; i < NUM_SUPPORTED_MCUS; i++) {
+ AppendMenu(ProcessorMenu, MF_STRING, MNU_PROCESSOR_0+i,
+ SupportedMcus[i].mcuName);
+ }
+ AppendMenu(ProcessorMenu, MF_STRING, MNU_PROCESSOR_0+i,
+ _("(no microcontroller)"));
+ AppendMenu(settings, MF_STRING | MF_POPUP, (UINT_PTR)ProcessorMenu,
+ _("&Microcontroller"));
+
+ SimulateMenu = CreatePopupMenu();
+ AppendMenu(SimulateMenu, MF_STRING, MNU_SIMULATION_MODE,
+ _("Si&mulation Mode\tCtrl+M"));
+ AppendMenu(SimulateMenu, MF_STRING | MF_GRAYED, MNU_START_SIMULATION,
+ _("Start &Real-Time Simulation\tCtrl+R"));
+ AppendMenu(SimulateMenu, MF_STRING | MF_GRAYED, MNU_STOP_SIMULATION,
+ _("&Halt Simulation\tCtrl+H"));
+ AppendMenu(SimulateMenu, MF_STRING | MF_GRAYED, MNU_SINGLE_CYCLE,
+ _("Single &Cycle\tSpace"));
+
+ compile = CreatePopupMenu();
+ AppendMenu(compile, MF_STRING, MNU_COMPILE, _("&Compile\tF5"));
+ AppendMenu(compile, MF_STRING, MNU_COMPILE_AS, _("Compile &As..."));
+
+ help = CreatePopupMenu();
+ AppendMenu(help, MF_STRING, MNU_MANUAL, _("&Manual...\tF1"));
+ AppendMenu(help, MF_STRING, MNU_ABOUT, _("&About..."));
+
+ TopMenu = CreateMenu();
+ AppendMenu(TopMenu, MF_STRING | MF_POPUP, (UINT_PTR)FileMenu, _("&File"));
+ AppendMenu(TopMenu, MF_STRING | MF_POPUP, (UINT_PTR)EditMenu, _("&Edit"));
+ AppendMenu(TopMenu, MF_STRING | MF_POPUP, (UINT_PTR)settings,
+ _("&Settings"));
+ AppendMenu(TopMenu, MF_STRING | MF_POPUP, (UINT_PTR)InstructionMenu,
+ _("&Instruction"));
+ AppendMenu(TopMenu, MF_STRING | MF_POPUP, (UINT_PTR)SimulateMenu,
+ _("Si&mulate"));
+ AppendMenu(TopMenu, MF_STRING | MF_POPUP, (UINT_PTR)compile,
+ _("&Compile"));
+ AppendMenu(TopMenu, MF_STRING | MF_POPUP, (UINT_PTR)help, _("&Help"));
+
+ return TopMenu;
+}
+
+//-----------------------------------------------------------------------------
+// Adjust the size and visibility of the scrollbars as necessary, either due
+// to a change in the size of the program or a change in the size of the
+// window.
+//-----------------------------------------------------------------------------
+void RefreshScrollbars(void)
+{
+ SCROLLINFO vert, horiz;
+ SetUpScrollbars(&NeedHoriz, &horiz, &vert);
+ SetScrollInfo(HorizScrollBar, SB_CTL, &horiz, TRUE);
+ SetScrollInfo(VertScrollBar, SB_CTL, &vert, TRUE);
+
+ RECT main;
+ GetClientRect(MainWindow, &main);
+
+ if(NeedHoriz) {
+ MoveWindow(HorizScrollBar, 0, IoListTop - ScrollHeight - 2,
+ main.right - ScrollWidth - 2, ScrollHeight, TRUE);
+ ShowWindow(HorizScrollBar, SW_SHOW);
+ EnableWindow(HorizScrollBar, TRUE);
+ } else {
+ ShowWindow(HorizScrollBar, SW_HIDE);
+ }
+ MoveWindow(VertScrollBar, main.right - ScrollWidth - 2, 1, ScrollWidth,
+ NeedHoriz ? (IoListTop - ScrollHeight - 4) : (IoListTop - 3), TRUE);
+
+ MoveWindow(VertScrollBar, main.right - ScrollWidth - 2, 1, ScrollWidth,
+ NeedHoriz ? (IoListTop - ScrollHeight - 4) : (IoListTop - 3), TRUE);
+
+ InvalidateRect(MainWindow, NULL, FALSE);
+}
+
+//-----------------------------------------------------------------------------
+// Respond to a WM_VSCROLL sent to the main window, presumably by the one and
+// only vertical scrollbar that it has as a child.
+//-----------------------------------------------------------------------------
+void VscrollProc(WPARAM wParam)
+{
+ int prevY = ScrollYOffset;
+ switch(LOWORD(wParam)) {
+ case SB_LINEUP:
+ case SB_PAGEUP:
+ if(ScrollYOffset > 0) {
+ ScrollYOffset--;
+ }
+ break;
+
+ case SB_LINEDOWN:
+ case SB_PAGEDOWN:
+ if(ScrollYOffset < ScrollYOffsetMax) {
+ ScrollYOffset++;
+ }
+ break;
+
+ case SB_TOP:
+ ScrollYOffset = 0;
+ break;
+
+ case SB_BOTTOM:
+ ScrollYOffset = ScrollYOffsetMax;
+ break;
+
+ case SB_THUMBTRACK:
+ case SB_THUMBPOSITION:
+ ScrollYOffset = HIWORD(wParam);
+ break;
+ }
+ if(prevY != ScrollYOffset) {
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_POS;
+ si.nPos = ScrollYOffset;
+ SetScrollInfo(VertScrollBar, SB_CTL, &si, TRUE);
+
+ InvalidateRect(MainWindow, NULL, FALSE);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Respond to a WM_HSCROLL sent to the main window, presumably by the one and
+// only horizontal scrollbar that it has as a child.
+//-----------------------------------------------------------------------------
+void HscrollProc(WPARAM wParam)
+{
+ int prevX = ScrollXOffset;
+ switch(LOWORD(wParam)) {
+ case SB_LINEUP:
+ ScrollXOffset -= FONT_WIDTH;
+ break;
+
+ case SB_PAGEUP:
+ ScrollXOffset -= POS_WIDTH*FONT_WIDTH;
+ break;
+
+ case SB_LINEDOWN:
+ ScrollXOffset += FONT_WIDTH;
+ break;
+
+ case SB_PAGEDOWN:
+ ScrollXOffset += POS_WIDTH*FONT_WIDTH;
+ break;
+
+ case SB_TOP:
+ ScrollXOffset = 0;
+ break;
+
+ case SB_BOTTOM:
+ ScrollXOffset = ScrollXOffsetMax;
+ break;
+
+ case SB_THUMBTRACK:
+ case SB_THUMBPOSITION:
+ ScrollXOffset = HIWORD(wParam);
+ break;
+ }
+
+ if(ScrollXOffset > ScrollXOffsetMax) ScrollXOffset = ScrollXOffsetMax;
+ if(ScrollXOffset < 0) ScrollXOffset = 0;
+
+ if(prevX != ScrollXOffset) {
+ SCROLLINFO si;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_POS;
+ si.nPos = ScrollXOffset;
+ SetScrollInfo(HorizScrollBar, SB_CTL, &si, TRUE);
+
+ InvalidateRect(MainWindow, NULL, FALSE);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Cause the status bar and the list view to be in sync with the actual data
+// structures describing the settings and the I/O configuration. Listview
+// does callbacks to get the strings it displays, so it just needs to know
+// how many elements to populate.
+//-----------------------------------------------------------------------------
+void RefreshControlsToSettings(void)
+{
+ int i;
+
+ if(!IoListOutOfSync) {
+ IoListSelectionPoint = -1;
+ for(i = 0; i < Prog.io.count; i++) {
+ if(ListView_GetItemState(IoList, i, LVIS_SELECTED)) {
+ IoListSelectionPoint = i;
+ break;
+ }
+ }
+ }
+
+ ListView_DeleteAllItems(IoList);
+ for(i = 0; i < Prog.io.count; i++) {
+ LVITEM lvi;
+ lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE;
+ lvi.state = lvi.stateMask = 0;
+ lvi.iItem = i;
+ lvi.iSubItem = 0;
+ lvi.pszText = LPSTR_TEXTCALLBACK;
+ lvi.lParam = i;
+
+ if(ListView_InsertItem(IoList, &lvi) < 0) oops();
+ }
+ if(IoListSelectionPoint >= 0) {
+ for(i = 0; i < Prog.io.count; i++) {
+ ListView_SetItemState(IoList, i, 0, LVIS_SELECTED);
+ }
+ ListView_SetItemState(IoList, IoListSelectionPoint, LVIS_SELECTED,
+ LVIS_SELECTED);
+ ListView_EnsureVisible(IoList, IoListSelectionPoint, FALSE);
+ }
+ IoListOutOfSync = FALSE;
+
+ if(Prog.mcu) {
+ SendMessage(StatusBar, SB_SETTEXT, 0, (LPARAM)Prog.mcu->mcuName);
+ } else {
+ SendMessage(StatusBar, SB_SETTEXT, 0, (LPARAM)_("no MCU selected"));
+ }
+ char buf[256];
+ sprintf(buf, _("cycle time %.2f ms"), (double)Prog.cycleTime/1000.0);
+ SendMessage(StatusBar, SB_SETTEXT, 1, (LPARAM)buf);
+
+ if(Prog.mcu && (Prog.mcu->whichIsa == ISA_ANSIC ||
+ Prog.mcu->whichIsa == ISA_INTERPRETED))
+ {
+ strcpy(buf, "");
+ } else {
+ sprintf(buf, _("processor clock %.4f MHz"),
+ (double)Prog.mcuClock/1000000.0);
+ }
+ SendMessage(StatusBar, SB_SETTEXT, 2, (LPARAM)buf);
+
+ for(i = 0; i < NUM_SUPPORTED_MCUS; i++) {
+ if(&SupportedMcus[i] == Prog.mcu) {
+ CheckMenuItem(ProcessorMenu, MNU_PROCESSOR_0+i, MF_CHECKED);
+ } else {
+ CheckMenuItem(ProcessorMenu, MNU_PROCESSOR_0+i, MF_UNCHECKED);
+ }
+ }
+ // `(no microcontroller)' setting
+ if(!Prog.mcu) {
+ CheckMenuItem(ProcessorMenu, MNU_PROCESSOR_0+i, MF_CHECKED);
+ } else {
+ CheckMenuItem(ProcessorMenu, MNU_PROCESSOR_0+i, MF_UNCHECKED);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Regenerate the I/O list, keeping the selection in the same place if
+// possible.
+//-----------------------------------------------------------------------------
+void GenerateIoListDontLoseSelection(void)
+{
+ int i;
+ IoListSelectionPoint = -1;
+ for(i = 0; i < Prog.io.count; i++) {
+ if(ListView_GetItemState(IoList, i, LVIS_SELECTED)) {
+ IoListSelectionPoint = i;
+ break;
+ }
+ }
+ IoListSelectionPoint = GenerateIoList(IoListSelectionPoint);
+ // can't just update the listview index; if I/O has been added then the
+ // new selection point might be out of range till we refill it
+ IoListOutOfSync = TRUE;
+ RefreshControlsToSettings();
+}
+
+//-----------------------------------------------------------------------------
+// Called when the main window has been resized. Adjust the size of the
+// status bar and the listview to reflect the new window size.
+//-----------------------------------------------------------------------------
+void MainWindowResized(void)
+{
+ RECT main;
+ GetClientRect(MainWindow, &main);
+
+ RECT status;
+ GetWindowRect(StatusBar, &status);
+ int statusHeight = status.bottom - status.top;
+
+ MoveWindow(StatusBar, 0, main.bottom - statusHeight, main.right,
+ statusHeight, TRUE);
+
+ // Make sure that the I/O list can't disappear entirely.
+ if(IoListHeight < 30) {
+ IoListHeight = 30;
+ }
+ IoListTop = main.bottom - IoListHeight - statusHeight;
+ // Make sure that we can't drag the top of the I/O list above the
+ // bottom of the menu bar, because it then becomes inaccessible.
+ if(IoListTop < 5) {
+ IoListHeight = main.bottom - statusHeight - 5;
+ IoListTop = main.bottom - IoListHeight - statusHeight;
+ }
+ MoveWindow(IoList, 0, IoListTop, main.right, IoListHeight, TRUE);
+
+ RefreshScrollbars();
+
+ InvalidateRect(MainWindow, NULL, FALSE);
+}
+
+//-----------------------------------------------------------------------------
+// Toggle whether we are in simulation mode. A lot of options are only
+// available in one mode or the other.
+//-----------------------------------------------------------------------------
+void ToggleSimulationMode(void)
+{
+ InSimulationMode = !InSimulationMode;
+
+ if(InSimulationMode) {
+ EnableMenuItem(SimulateMenu, MNU_START_SIMULATION, MF_ENABLED);
+ EnableMenuItem(SimulateMenu, MNU_SINGLE_CYCLE, MF_ENABLED);
+
+ EnableMenuItem(FileMenu, MNU_OPEN, MF_GRAYED);
+ EnableMenuItem(FileMenu, MNU_SAVE, MF_GRAYED);
+ EnableMenuItem(FileMenu, MNU_SAVE_AS, MF_GRAYED);
+ EnableMenuItem(FileMenu, MNU_NEW, MF_GRAYED);
+ EnableMenuItem(FileMenu, MNU_EXPORT, MF_GRAYED);
+
+ EnableMenuItem(TopMenu, 1, MF_GRAYED | MF_BYPOSITION);
+ EnableMenuItem(TopMenu, 2, MF_GRAYED | MF_BYPOSITION);
+ EnableMenuItem(TopMenu, 3, MF_GRAYED | MF_BYPOSITION);
+ EnableMenuItem(TopMenu, 5, MF_GRAYED | MF_BYPOSITION);
+
+ CheckMenuItem(SimulateMenu, MNU_SIMULATION_MODE, MF_CHECKED);
+
+ ClearSimulationData();
+ // Recheck InSimulationMode, because there could have been a compile
+ // error, which would have kicked us out of simulation mode.
+ if(UartFunctionUsed() && InSimulationMode) {
+ ShowUartSimulationWindow();
+ }
+ } else {
+ RealTimeSimulationRunning = FALSE;
+ KillTimer(MainWindow, TIMER_SIMULATE);
+
+ EnableMenuItem(SimulateMenu, MNU_START_SIMULATION, MF_GRAYED);
+ EnableMenuItem(SimulateMenu, MNU_STOP_SIMULATION, MF_GRAYED);
+ EnableMenuItem(SimulateMenu, MNU_SINGLE_CYCLE, MF_GRAYED);
+
+ EnableMenuItem(FileMenu, MNU_OPEN, MF_ENABLED);
+ EnableMenuItem(FileMenu, MNU_SAVE, MF_ENABLED);
+ EnableMenuItem(FileMenu, MNU_SAVE_AS, MF_ENABLED);
+ EnableMenuItem(FileMenu, MNU_NEW, MF_ENABLED);
+ EnableMenuItem(FileMenu, MNU_EXPORT, MF_ENABLED);
+
+ EnableMenuItem(TopMenu, 1, MF_ENABLED | MF_BYPOSITION);
+ EnableMenuItem(TopMenu, 2, MF_ENABLED | MF_BYPOSITION);
+ EnableMenuItem(TopMenu, 3, MF_ENABLED | MF_BYPOSITION);
+ EnableMenuItem(TopMenu, 5, MF_ENABLED | MF_BYPOSITION);
+
+ CheckMenuItem(SimulateMenu, MNU_SIMULATION_MODE, MF_UNCHECKED);
+
+ if(UartFunctionUsed()) {
+ DestroyUartSimulationWindow();
+ }
+ }
+
+ UpdateMainWindowTitleBar();
+
+ DrawMenuBar(MainWindow);
+ InvalidateRect(MainWindow, NULL, FALSE);
+ ListView_RedrawItems(IoList, 0, Prog.io.count - 1);
+}
+
+//-----------------------------------------------------------------------------
+// Start real-time simulation. Have to update the controls grayed status
+// to reflect this.
+//-----------------------------------------------------------------------------
+void StartSimulation(void)
+{
+ RealTimeSimulationRunning = TRUE;
+
+ EnableMenuItem(SimulateMenu, MNU_START_SIMULATION, MF_GRAYED);
+ EnableMenuItem(SimulateMenu, MNU_STOP_SIMULATION, MF_ENABLED);
+ StartSimulationTimer();
+
+ UpdateMainWindowTitleBar();
+}
+
+//-----------------------------------------------------------------------------
+// Stop real-time simulation. Have to update the controls grayed status
+// to reflect this.
+//-----------------------------------------------------------------------------
+void StopSimulation(void)
+{
+ RealTimeSimulationRunning = FALSE;
+
+ EnableMenuItem(SimulateMenu, MNU_START_SIMULATION, MF_ENABLED);
+ EnableMenuItem(SimulateMenu, MNU_STOP_SIMULATION, MF_GRAYED);
+ KillTimer(MainWindow, TIMER_SIMULATE);
+
+ UpdateMainWindowTitleBar();
+}
diff --git a/ldmicro/make.bat b/ldmicro/make.bat
new file mode 100644
index 0000000..2b4df61
--- /dev/null
+++ b/ldmicro/make.bat
@@ -0,0 +1 @@
+@nmake D=LDLANG_EN %*
diff --git a/ldmicro/makeall.pl b/ldmicro/makeall.pl
new file mode 100644
index 0000000..4b92999
--- /dev/null
+++ b/ldmicro/makeall.pl
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+sub SYS { system($_[0]); }
+
+SYS("rm -rf build");
+SYS("mkdir build");
+
+for $f qw(DE ES FR IT PT TR) {
+ SYS("nmake clean");
+ SYS("nmake D=LDLANG_$f");
+ $fl = lc($f);
+ SYS("copy ldmicro.exe build\\ldmicro-$fl.exe");
+}
+
+SYS("nmake clean");
+SYS("nmake D=LDLANG_EN");
+SYS("copy ldmicro.exe build\\ldmicro.exe");
diff --git a/ldmicro/manual-de.txt b/ldmicro/manual-de.txt
new file mode 100644
index 0000000..a4e2c7b
--- /dev/null
+++ b/ldmicro/manual-de.txt
@@ -0,0 +1,1075 @@
+
+EINFÜHRUNG
+===========
+
+LDmicro erzeugt einen systemspezifischen Code für einige Microchip PIC16
+und Atmel AVR Mikroprozessoren. Üblicherweise wird die Software für diese
+Prozessoren in Programmsprachen, wie Assembler, C oder BASIC geschrieben.
+Ein Programm, welches in einer dieser Sprachen abgefasst ist, enthält
+eine Anweisungsliste. Auch sind die diese Sprachen sehr leistungsfähig
+und besonders gut geeignet für die Architektur dieser Prozessoren,
+welche diese Anweisungsliste intern abarbeiten.
+
+Programme für speicherprogrammierbare Steuerungen (SPS) andererseits,
+werden oftmals im Kontaktplan (KOP = ladder logic) geschrieben.
+Ein einfaches Programm, könnte wie folgt aussehen:
+
+ || ||
+ || Xbutton1 Tdon Rchatter Yred ||
+ 1 ||-------]/[---------[TON 1.000 s]-+-------]/[--------------( )-------||
+ || | ||
+ || Xbutton2 Tdof | ||
+ ||-------]/[---------[TOF 2.000 s]-+ ||
+ || ||
+ || ||
+ || ||
+ || Rchatter Ton Tneu Rchatter ||
+ 2 ||-------]/[---------[TON 1.000 s]----[TOF 1.000 s]---------( )-------||
+ || ||
+ || ||
+ || ||
+ ||------[END]---------------------------------------------------------||
+ || ||
+ || ||
+
+ (TON ist eine Anzugsverzögerung, TOF eine Abfallverzögerung.
+ Die --] [-- Anweisungen bedeuten Eingänge, die sich ähnlich, wie Relais-
+ kontakte verhalten. Die --( )-- Anweisungen bedeuten Ausgänge, die sich
+ ähnlich, wie Relaisspulen verhalten. Viele gute Bezugsquellen werden für
+ KOP im Internet oder sonst wo angeboten; Einzelheiten zu dieser speziellen
+ Ausführung werden weiter unten angegeben.)
+
+Einige Unterschiede sind jedoch offensichtlich:
+
+* Das Programm wird in einem grafischen Format dargestellt
+ und nicht mit einer aus Anweisungen bestehenden Textliste. Viele
+ Anwender werden dies zunächst als besser verständlich auffassen.
+
+* Diese Programme erscheinen wie einfachste Schaltpläne, mit
+ Relaiskontakten (Eingängen) and Spulen (Ausgängen). Dies ist recht
+ intuitiv für Programmierer, die über Kenntnisse der Theorie von
+ Elektroschaltplänen verfügen.
+
+* Der ‘ladder logic compiler’ übernimmt was wo berechnet wird.
+ Es ist nicht notwendig einen Code zu schreiben, um zu errechnen, wann
+ der Status (Zustand) der Ausgänge neu bestimmt werden muss, z.B. auf
+ Grund einer Änderung eines Eingangs oder Timers. Auch braucht man die
+ Reihenfolge der Berechnungen nicht anzugeben; die SPS-Hilfsprogramme
+ übernehmen dies.
+
+LDmicro kompiliert ‘ladder logic’ (KOP) in PIC16- oder AVR-Code.
+Die folgenden Prozessoren werden unterstützt:
+
+ * PIC16F877
+ * PIC16F628
+ * PIC16F876 (ungetestet)
+ * PIC16F88 (ungetestet)
+ * PIC16F819 (ungetestet)
+ * PIC16F887 (ungetestet)
+ * PIC16F886 (ungetestet)
+ * ATmega8 (ungetestet)
+ * ATmega16 (ungetestet)
+ * ATmega32 (ungetestet)
+ * ATmega128
+ * ATmega64
+ * ATmega162 (ungetestet)
+
+Es wäre einfach noch weitere AVR- oder PIC16-Prozessoren zu unterstützen,
+aber ich habe keine Möglichkeit diese zu testen. Falls Sie einen
+bestimmten benötigen, so nehmen Sie Kontakt mit mir auf und ich werde
+sehen, was ich tun kann.
+
+Mit LDmicro können Sie ein Kontaktplan-Programm zeichnen bzw. entwickeln.
+Auch können Sie dies in Realzeit mit Ihrem Computer simulieren. Wenn
+Sie dann überzeugt sind, dass Ihr Programm korrekt ist, so können
+Sie die Pins, entsprechend dem Programm als Ein- oder Ausgänge, dem
+Mikroprozessor zuweisen. Nach der Zuweisung der Pins können Sie den PIC-
+oder AVR-Code für Ihr Programm kompilieren. Der Compiler erzeugt eine
+Hex-Datei, mit dem Sie dann Ihren Mikroprozessor programmieren. Dies
+ist mit jedem PIC/AVR-Programmer möglich.
+
+LDmicro wurde entworfen, um in etwa mit den meisten kommerziellen
+SPS-Systemen ähnlich zu sein. Es gibt einige Ausnahmen und viele Dinge
+sind ohnehin kein Standard in der Industrie. Lesen Sie aufmerksam die
+Beschreibung jeder Anweisung, auch wenn Ihnen diese vertraut erscheint.
+Dieses Dokument setzt ein Grundwissen an Kontaktplan-Programmierung
+und der Struktur von SPS-Software voraus (wie: der Ausführungszyklus,
+Eingänge lesen, rechnen und Ausgänge setzen).
+
+
+WEITERE ZIELE
+=============
+
+Es ist auch möglich einen ANSI C - Code zu erzeugen. Diesen können
+Sie dann für jeden Prozessor verwenden, für den Sie einen C-Compiler
+besitzen. Sie sind dann aber selbst verantwortlich, den Ablauf zu
+bestimmen. Das heißt, LDmicro erzeugt nur ein Stammprogramm für einen
+Funktions- SPS-Zyklus. Sie müssen den SPS-Zyklus bei jedem Durchlauf
+aufrufen und auch die Ausführung (Implementierung) der E/A-Funktionen,
+die der SPS-Zyklus abruft (wie: lesen/schreiben, digitaler Eingang usw.).
+Für mehr Einzelheiten: Siehe die Kommentare in dem erzeugten Quellcode.
+
+Ganz zuletzt kann LDmicro auch für eine virtuelle Maschine einen
+prozessor-unabhängigen Byte-Code erzeugen, welche mit der KOP-Kodierung
+(ladder logic) laufen soll. Ich habe eine Beispiel-Anwendung des
+VM/Interpreters vorgesehen, in ziemlich gutem C geschrieben. Dieses
+Anwendungsziel wird halbwegs auf jeder Plattform funktionieren, so lange
+Sie Ihre eigene VM vorsehen. Dies könnte für solche Anwendungen nützlich
+sein, für die Sie KOP (ladder logic) als Datentransfer-Sprache verwenden
+möchten, um ein größeres Programm anzupassen. Für weitere Einzelheiten:
+Siehe die Kommentare in dem Beispiel-Interpreter.
+
+
+OPTIONEN DER BEFEHLSZEILEN
+==========================
+
+ldmicro.exe läuft normalerweise ohne eine Befehlszeilen-Option.
+Das heißt, dass Sie nur ein Tastenkürzel zu dem Programm benötigen
+oder es auf dem Desktop abspeichern und dann auf das Symbol (die Ikone)
+doppelklicken, um es laufen zu lassen. Danach können Sie alles ausführen,
+was das GUI (Graphical User Interface) zulässt.
+
+Wenn man an LDmicro einen alleinstehenden Dateinamen in der Befehlszeile
+vergeben hat (z. B. ‘ldmicro.exe asd.ld’), wird LDmicro versuchen ‘asd.ld’
+zu öffnen, falls diese existiert. Dies bedeutet, dass man ldmicro.exe
+mit .ld Dateien verbinden kann, sodass dies automatisch abläuft, wenn
+man auf eine .ld Datei doppelklickt.
+
+Wenn man an LDmicro das Argument in der Befehlszeile in folgender Form
+vergeben hat: ‘ldmicro.exe /c src.ld dest.hex’, so wird es versuchen
+‘src.ld’ zu kompilieren und unter ‘dest.hex’ abzuspeichern. LDmicro endet
+nach dem Kompilieren, unabhängig davon, ob die Kompilierung erfolgreich
+war oder nicht. Alle Meldungen werden auf der Konsole ausgegeben. Dieser
+Modus ist hilfreich, wenn man LDmicro von der Befehlszeile laufen
+aus lässt.
+
+
+GRUNDLAGEN
+==========
+
+Wenn Sie LDmicro ohne Argumente aufrufen, so beginnt es als ein leeres
+Programm. Wenn Sie LDmicro mit dem Namen eines ‘ladder’ (KOP)-Programms
+(z.B. xxx.ld) in der Befehlszeile öffnen, dann wird es versuchen dieses
+Programm am Anfang zu laden.
+
+LDmicro verwendet sein eigenes internes Format für das Programm und
+man kann kein logisches Zeichen aus einem anderen (Fremd-)Programm
+importieren.
+
+Falls Sie nicht ein schon vorhandenes Programm laden, dann wird Ihnen
+ein Programm mit einem leeren Netzwerk geliefert. In dieses können Sie
+einen Befehl einfügen; z. B. könnten Sie auch eine Reihe von Kontakten
+einfügen (Anweisung -> Kontakte Einfügen), die zunächst mit ‘Xneu’
+bezeichnet werden. ‘X’ bedeutet, dass der Kontakt auf einen Eingang
+des Mikroprozessors festgelegt ist. Diesen Pin können Sie später zuweisen,
+nachdem Sie den Mikroprozessor gewählt haben und die Kontakte
+umbenannt haben. Der erste Buchstabe zeigt an, um welche Art Objekt es
+sich handelt. Zum Beispiel:
+
+ * XName -- Auf einen Eingang des Mikroprozessors festgelegt
+ * YName -- Auf einen Ausgang des Mikroprozessors festgelegt
+ * RName -- Merker: Ein Bit im Speicher (Internes Relais)
+ * TName -- Ein Timer; Anzugs- oder Abfallverzögerung
+ * CName -- Ein Zähler, Aufwärts- oder Abwärtszähler
+ * AName -- Eine Ganzzahl, von einem A/D-Wandler eingelesen
+ * Name -- Eine Allzweck-Variable als Ganzzahl
+
+Wählen Sie den Rest des Namens, sodass dieser beschreibt, was das Objekt
+bewirkt und das dieser auch einmalig im Programm ist. Der gleiche Name
+bezieht sich immer auf das gleiche Objekt im Programm. Es wäre zum
+Beispiel falsch eine Anzugsverzögerung (TON) ‘TVerzög’ zu nennen und im
+selben Programm eine Abfallverzögerung ‘TVerzög’ (TOF), weil jeder Zähler
+(oder Timer) seinen eigenen Speicher benötigt. Andererseits wäre es
+korrekt einen „Speichernden Timer“ (RTO) ‘TVerzög’ zu nennen und eine
+entsprechende Rücksetz-Anweisung (RES) = ‘TVerzög’, weil in diesem
+Fall beide Befehle dem gleichen Timer gelten.
+
+Die Namen von Variablen können aus Buchstaben, Zahlen und Unter-
+strichen (_) bestehen. Der Name einer Variablen darf nicht mit einer
+Nummer beginnen. Die Namen von Variablen sind fallabhängig.
+
+Ein Befehl für eine gewöhnliche Variable (MOV, ADD, EQU, usw.), kann
+mit Variablen mit jedem Namen arbeiten. Das bedeutet, dass diese Zugang
+zu den Timer- und Zähler-Akkumulatoren haben. Das kann manchmal recht
+hilfreich sein; zum Beispiel kann man damit prüfen, ob die Zählung eines
+Timers in einem bestimmten Bereich liegt.
+
+Die Variablen sind immer 16-Bit Ganzzahlen. Das heißt sie können von
+-32768 bis 32767 reichen. Die Variablen werden immer als vorzeichen-
+behaftet behandelt. Sie können auch Buchstaben als Dezimalzahlen festlegen
+(0, 1234, -56). Auch können Sie ASCII-Zeichenwerte (‘A’, ‘z’) festlegen,
+indem Sie die Zeichen in „Auslassungszeichen“ einfügen. Sie können
+ein ASCII-Zeichen an den meisten Stellen verwenden, an denen Sie eine
+Dezimalzahl verwenden können.
+
+Am unteren Ende der Maske (Bildanzeige) sehen Sie eine Liste aller
+Objekte (Anweisungen, Befehle) des Programms. Diese Liste wird vom
+Programm automatisch erzeugt; es besteht somit keine Notwendigkeit diese
+von Hand auf dem Laufenden zu halten. Die meisten Objekte benötigen
+keine Konfiguration. ‘XName’, ‘YName’, und ‘AName’ Objekte allerdings,
+müssen einem Pin des Mikroprozessors zugeordnet werden. Wählen Sie zuerst
+welcher Prozessor verwendet wird (Voreinstellungen -> Prozessor). Danach
+legen Sie Ihre E/A Pins fest, indem Sie in der Liste auf diese jeweils
+doppelklicken.
+
+Sie können das Programm verändern, indem Sie Anweisungen (Befehle)
+einfügen oder löschen. Die Schreibmarke (cursor)im Programm blinkt,
+um die momentan gewählte Anweisung und den Einfügungspunkt anzuzeigen.
+Falls diese nicht blinkt, so drücken Sie den oder klicken
+Sie auf eine Anweisung. Jetzt können Sie die momentane Anweisung löschen
+oder eine neue Anweisung einfügen; links oder rechts (in Reihenschaltung)
+oder über oder unter (in Parallelschaltung) mit der gewählten Anweisung.
+Einige Handhabungen sind nicht erlaubt, so zum Beispiel weitere
+Anweisungen rechts von einer Spule.
+
+Das Programm beginnt mit nur einem Netzwerk. Sie können mehr Netzwerke
+hinzufügen, indem Sie ‘Netzwerk Einfügen Davor/Danach’ im Programm-Menü
+wählen. Den gleichen Effekt könnten Sie erzielen, indem Sie viele
+komplizierte parallele Unterschaltungen in einem einzigen Netzwerk
+unterbringen. Es ist aber übersichtlicher, mehrere Netzwerke zu verwenden.
+
+Wenn Sie Ihr Programm fertig geschrieben haben, so können Sie dieses
+mit der Simulation testen. Danach können Sie es in eine Hex-Datei für
+den zugedachten Mikroprozessor kompilieren.
+
+
+SIMULATION
+==========
+
+Um den Simulationsbetrieb einzugeben, wählen Sie ‘Simulieren ->
+Simulationsbetrieb’ oder drücken Sie . Das Programm wird
+im Simulationsbetrieb unterschiedlich dargestellt. Es gibt keine
+Schreibmarke (cursor) mehr. Die „erregten“ Anweisungen erscheinen hellrot,
+die „nicht erregten“ erscheinen grau. Drücken Sie die Leertaste, um das
+SPS-Programm nur einen einzelnen Zyklus durchlaufen zu lassen. Wählen
+Sie für einen kontinuierlichen Umlauf in Echtzeit ‘Simulieren -> Start
+Echtzeit-Simulation’ oder drücken Sie . Die Maske (Bildanzeige)
+des Programms wird jetzt in Echtzeit, entsprechend der Änderungen des
+Status (des Zustands) des Programms aktualisiert.
+
+Sie können den Status (Zustand) eines Eingangs im Programm einstellen,
+indem Sie auf den jeweiligen auf der Liste am unteren Ende der
+Maske (Bildanzeige) doppelklicken oder auf die jeweilige ‘XName’
+Kontakt-Anweisung im Programm. Wenn Sie den Status (Zustand) eines
+Eingangs-Pins ändern, so wird diese Änderung nicht unmittelbar in
+der Maske (Bildanzeige) wiedergegeben, sondern erst wenn sich die
+SPS im zyklischen Umlauf befindet. Das geschieht automatisch wenn das
+SPS-Programm in Echtzeit-Simulation läuft, oder wenn Sie die Leertaste
+drücken.
+
+
+KOMPILIEREN ZUM SYSTEMSPEZIFISCHEN CODE
+=======================================
+
+Letztlich ist es dann nur sinnvoll eine .hex Datei zu erzeugen, mit
+der Sie Ihren Mikroprozessor programmieren können. Zunächst müssen
+Sie die Teilenummer des Mikroprozessors im Menü ‘Voreinstellungen ->
+Prozessor’ wählen. Danach müssen jedem ‘XName’ oder ‘YName’ Objekt
+einen E/A-Pin zuweisen. Tun Sie dies, indem auf den Namen des Objekts
+doppelklicken, welcher sich in der Liste ganz unten in der Maske
+(Bildanzeige) befindet. Ein Dialogfenster wird dann erscheinen und Sie
+können daraufhin einen noch nicht vergebenen Pin von der Liste aussuchen.
+
+Als nächstes müssen Sie die Zykluszeit wählen, mit der Sie das
+Programm laufen lassen wollen, auch müssen Sie dem Compiler mitteilen
+mit welcher Taktgeschwindigkeit der Prozessor arbeiten soll. Diese
+Einstellungen werden im Menü ‘Voreinstellungen -> Prozessor Parameter...’
+vorgenommen. Üblicherweise sollten Sie die Zykluszeit nicht ändern,
+denn diese ist auf 10ms voreingestellt, dies ist ein guter Wert für
+die meisten Anwendungen. Tippen Sie die Frequenz des Quarzes (oder des
+Keramik-Resonators) ein, mit der Sie den Prozessor betreiben wollen und
+klicken auf Okay.
+
+Jetzt können Sie einen Code von Ihrem Programm erzeugen. Wählen Sie
+‘Kompilieren -> Kompilieren’ oder ‘Kompilieren -> Kompilieren unter...’,
+falls Sie vorher Ihr Programm schon kompiliert haben und einen neuen Namen
+für die Ausgangsdatei vergeben wollen. Wenn Ihr Programm fehlerfrei ist,
+wird LDmicro eine Intel IHEX Datei erzeugen, mit der sich Ihr Prozessor
+programmieren lässt.
+
+Verwenden Sie hierzu irgendeine Programmier Soft- und Hardware, die Sie
+besitzen, um die Hex-Datei in den Mikroprozessor zu laden. Beachten Sie
+die Einstellungen für die Konfigurationsbits (fuses)! Bei den PIC16
+Prozessoren sind diese Konfigurationsbits bereits in der Hex-Datei
+enthalten. Die meisten Programmiersoftwares schauen automatisch nach
+diesen. Für die AVR-Prozessoren müssen Sie die Konfigurationsbits von
+Hand einstellen.
+
+
+ANWEISUNGS-VERZEICHNIS
+======================
+
+> KONTAKT, SCHLIESSER XName RName YName
+ ----] [---- ----] [---- ----] [----
+
+Wenn ein ‘unwahres’ Signal diese Anweisung erreicht, so ist das
+Ausgangssignal ‘unwahr’. Wenn ein ‘wahres’ Signal diese Anweisung
+erreicht, so ist das Ausgangssignal ‘wahr’. Dies nur, falls der
+vorliegende Eingangspin, Ausgangspin oder eines Merkers (Hilfsrelais)
+‘wahr’ ist, anderenfalls ist es unwahr. Diese Anweisung fragt den Status
+(Zustand) eines Eingangspins, Ausgangspins oder Merkers (Hilfsrelais) ab.
+
+
+> KONTAKT, ÖFFNER XName RName YName
+ ----]/[---- ----]/[---- ----]/[----
+
+Wenn ein ‘unwahres’ Signal diese Anweisung erreicht, so ist das
+Ausgangssignal ‘unwahr’. Wenn ein ‘wahres’ Signal diese Anweisung
+erreicht, so ist das Ausgangssignal ‘wahr’. Dies nur, falls der
+vorliegende Eingangspin, Ausgangspin oder der Merker (= internes
+Hilfsrelais) ‘unwahr’ ist, anderenfalls ist es ‘unwahr’. Diese Anweisung
+fragt den Status (Zustand) eines Eingangspins, Ausgangspins oder Merkers
+(Hilfsrelais) ab. Dies ist das Gegenteil eines Schließers.
+
+
+> SPULE, NORMAL (MERKER,AUSGANG) RName YName
+ ----( )---- ----( )----
+
+Wenn ein ‘unwahres’ Signal diese Anweisung erreicht, so wird der
+vorliegende Merker (Hilfsrelais) oder Ausgangspin nicht angesteuert. Wenn
+ein ‘wahres’ Signal diese Anweisung erreicht, so wird der vorliegende
+Merker (Hilfsrelais) oder Ausgangspin angesteuert. Es ist nicht sinnvoll
+dieser Spule eine Eingangsvariable zuzuweisen. Diese Anweisung muss
+ganz rechts im Netzwerk stehen.
+
+
+> SPULE, NEGIERT (MERKER,AUSGANG) RName YName
+ ----(/)---- ----(/)----
+
+Wenn ein ‘wahres’ Signal diese Anweisung erreicht, so wird der vorliegende
+Merker (Hilfsrelais)oder Ausgangspin nicht angesteuert. Wenn ein
+‘unwahres’ Signal diese Anweisung erreicht, so wird der vorliegende Merker
+(Hilfsrelais) oder Ausgangspin angesteuert. Es ist nicht sinnvoll dieser
+Spule eine Eingangsvariable zuzuweisen. Dies ist das Gegenteil einer
+normalen Spule. Diese Anweisung muss im Netzwerk ganz rechts stehen.
+
+
+> SPULE, SETZEN RName YName
+ ----(S)---- ----(S)----
+
+Wenn ein ‘wahres’ Signal diese Anweisung erreicht, so wird der vorliegende
+Merker (Hilfsrelais)oder Ausgangspin auf ‘wahr’ gesetzt. Anderenfalls
+bleibt der Status (Zustand) des Merkers (Hilfsrelais) oder Ausgangspins
+unverändert. Diese Anweisung kann nur den Status (Zustand) einer Spule
+von ‘unwahr’ nach ‘wahr’ verändern, insofern wird diese üblicherweise in
+einer Kombination mit einer Rücksetz-Anweisung für eine Spule verwendet.
+Diese Anweisung muss ganz rechts im Netzwerk stehen.
+
+
+> SPULE, RÜCKSETZEN RName YName
+ ----(R)---- ----(R)----
+
+Wenn ein ‘wahres’ Signal diese Anweisung erreicht, so wird der vorliegende
+Merker (Hilfsrelais) oder Ausgangspin rückgesetzt. Anderenfalls bleibt der
+Status (Zustand) des Merkers (Hilfsrelais) oder Ausgangspins unverändert.
+Diese Anweisung kann nur den Status (Zustand) einer Spule von ‘wahr’ nach
+‘unwahr’ verändern, insofern wird diese üblicherweise in einer Kombination
+mit einer Setz-Anweisung für eine Spule verwendet. Diese Anweisung muss
+ganz rechts im Netzwerk stehen.
+
+
+> ANZUGSVERZÖGERUNG Tdon
+ -[TON 1.000 s]-
+
+Wenn ein Signal diese Anweisung erreicht, welches seinen Status
+(Zustand) von ‘unwahr’ nach ‘wahr’ ändert, so bleibt das Ausgangssignal
+für 1,000 s ‘unwahr’, dann wird es ‘wahr’. Wenn ein Signal diese
+Anweisung erreicht, welches seinen Status (Zustand) von ‘wahr’ nach
+‘unwahr’ ändert, so wird das Ausgangssignal sofort ‘unwahr’. Der Timer
+wird jedes Mal rückgesetzt (bzw. auf Null gesetzt), wenn der Eingang
+‘unwahr’ wird. Der Eingang muss für 1000 aufeinanderfolgende Millisekunden
+‘wahr’ bleiben, bevor auch der Ausgang ‘wahr’ wird. Die Verzögerung
+ist konfigurierbar.
+
+Die ‘TName’ Variable zählt, in der Einheit der jeweiligen Zykluszeit,
+von Null ab hoch. Der Ausgang der TON-Anweisung wird wahr, wenn die
+Zählervariable größer oder gleich der vorliegenden Verzögerung ist.
+Es möglich die Zählervariable an einer anderen Stelle im Programm zu
+bearbeiten, zum Beispiel mit einer TRANSFER-Anweisung (MOV).
+
+
+> ABFALLVERZÖGERUNG Tdoff
+ -[TOF 1.000 s]-
+
+Wenn ein Signal diese Anweisung erreicht, welches seinen Status
+(Zustand) von ‘wahr’ nach ‘unwahr’ ändert, so bleibt das Ausgangssignal
+für 1,000 s ‘wahr’, dann wird es ‘unwahr’. Wenn ein Signal diese
+Anweisung erreicht, welches seinen Status (Zustand) von ‘unwahr’ nach
+‘wahr’ ändert, so wird das Ausgangssignal sofort ‘wahr’. Der Timer wird
+jedes Mal rückgesetzt (bzw. auf Null gesetzt), wenn der Eingang ‘unwahr’
+wird. Der Eingang muss für 1000 aufeinanderfolgende Millisekunden ‘unwahr’
+bleiben, bevor auch der Ausgang ‘unwahr’ wird. Die Verzögerung ist
+konfigurierbar.
+
+Die ‘TName’ Variable zählt, in der Einheit der jeweiligen Zykluszeit,
+von Null ab hoch. Der Ausgang der TOF Anweisung wird wahr, wenn die
+Zählervariable größer oder gleich der vorliegenden Verzögerung ist.
+Es möglich die Zählervariable an einer anderen Stelle im Programm zu
+bearbeiten, zum Beispiel mit einer TRANSFER-Anweisung (MOV).
+
+
+> SPEICHERNDER TIMER Trto
+ -[RTO 1.000 s]-
+
+Diese Anweisung zeichnet auf, wie lange sein Eingang ‘wahr’ gewesen
+ist. Wenn der Eingang für mindestens 1.000 s ‘wahr’ gewesen ist, dann
+wird der Ausgang ‘wahr’. Andernfalls ist er ‘unwahr’. Der Eingang muss
+für 1000 aufeinanderfolgende Millisekunden ‘wahr’ gewesen sein; wenn
+der Eingang für 0,6 s ‘wahr’ war, dann ‘unwahr’ für 2,0 s und danach für
+0,4 s wieder ‘wahr’, so wird sein Ausgang ‘wahr’. Nachdem der Ausgang
+‘wahr’ wurde, so bleibt er ‘wahr’, selbst wenn der Eingang ‘unwahr’
+wird, so lange der Eingang für länger als 1.000 s ‘wahr’ gewesen ist.
+Der Timer muss deshalb von Hand mit Hilfe der Rücksetz-Anweisung
+rückgesetzt (auf Null gesetzt) werden.
+
+Die ‘TName’ Variable zählt, in der Einheit der jeweiligen Zykluszeit,
+von Null ab hoch. Der Ausgang der RTO-Anweisung wird wahr, wenn die
+Zählervariable größer oder gleich der vorliegenden Verzögerung ist.
+Es möglich die Zählervariable an einer anderen Stelle im Programm zu
+bearbeiten, zum Beispiel mit einer TRANSFER-Anweisung (MOV).
+
+
+> RÜCKSETZEN Trto Citems
+ ----{RES}---- ----{RES}----
+
+Diese Anweisung rücksetzt einen Timer oder Zähler. TON oder TOF Timer
+werden automatisch rückgesetzt, wenn ihr Eingang ‘wahr’ oder ‘unwahr’
+wird, somit ist die RES-Anweisung für diese Timer nicht erforderlich. RTO
+Timer und CTU/CTD Zähler werden nicht automatisch rückgesetzt, somit
+müssen diese von Hand mit Hilfe der RES-Anweisung rückgesetzt (auf Null)
+werden. Wenn der Eingang ‘wahr’ ist, so wird der Timer oder Zähler
+rückgesetzt; wenn der Eingang ‘unwahr’ ist, so erfolgt keine Aktion.
+Diese Anweisung muss ganz rechts im Netzwerk stehen.
+
+ _
+> ONE-SHOT RISING, STEIGENDE FLANKE --[OSR_/ ]--
+
+Diese Anweisung wird normalerweise ‘unwahr’ ausgewiesen. Wenn der Eingang
+der Anweisung während des momentanen Zyklus ‘wahr’ ist und während des
+vorgehenden ‘unwahr’ war, so wird der Ausgang ‘wahr’. Daher erzeugt diese
+Anweisung bei jeder steigenden Flanke einen Impuls für einen Zyklus.
+Diese Anweisung ist hilfreich, wenn Sie Ereignisse an der steigenden
+Flanke eines Signals auslösen wollen.
+
+ _
+> ONE-SHOT FALLING, FALLENDE FLANKE --[OSF \_ ]--
+
+Diese Anweisung wird normalerweise ‘unwahr’ ausgewiesen. Wenn der Eingang
+der Anweisung während des momentanen Zyklus ‘unwahr’ ist und während des
+vorgehenden ‘wahr’ war, so wird der Ausgang ‘wahr’. Daher erzeugt diese
+Anweisung bei jeder fallenden Flanke einen Impuls für einen Zyklus.
+Diese Anweisung ist hilfreich, wenn Sie Ereignisse an der fallenden
+Flanke eines Signals auslösen wollen.
+
+
+> BRÜCKE, ÖFFNUNG ----+----+---- ----+ +----
+
+Der Eingangszustand einer Brücke ist immer gleich seinem Ausgangszustand.
+Der Ausgangszustands einer Öffnung ist immer ‘unwahr’. Diese Anweisungen
+sind bei der Fehlerbehebung (debugging) besonders hilfreich.
+
+
+> MASTER CONTROL RELAIS -{MASTER RLY}-
+
+
+Im Normalfall ist der Anfang (die linke Stromschiene) von jedem Netzwerk
+‘wahr’. Wenn eine ‘Master Control Relais’ Anweisung ausgeführt wird dessen
+Eingang ‘unwahr’ ist, so werden die Anfänge (die linke Stromschiene)
+aller folgenden Netzwerke ‘unwahr’. Das setzt sich fort bis die nächste
+‘Master Control Relais’ Anweisung erreicht wird (unabhängig von dem
+Anfangszustand dieser Anweisung). Diese Anweisungen müssen daher als Paar
+verwendet werden: Eine (vielleicht abhängige), um den „gegebenenfalls
+gesperrten“ Abschnitt zu starten und eine weitere, um diesen zu beenden.
+
+
+> TRANSFER, MOV {destvar := } {Tret := }
+ -{ 123 MOV}- -{ srcvar MOV}-
+
+Wenn der Eingang dieser Anweisung ‘wahr’ ist, so setzt diese die
+vorliegende Zielvariable gleich der vorliegenden Quellvariablen
+oder Konstanten. Wenn der Eingang dieser Anweisung ‘unwahr’ ist, so
+geschieht nichts. Mit der TRANSFER-Anweisung (MOV) können Sie jede
+Variable zuweisen; dies schließt Timer und Zähler Statusvariablen ein,
+welche mit einem vorgestellten ‘T’ oder ‘C’ unterschieden werden. Eine
+Anweisung zum Beispiel, die eine ‘0’ in einen ‘TBewahrend’ transferiert,
+ist äquivalent mit einer RES-Anweisung für diesen Timer. Diese Anweisung
+muss ganz rechts im Netzwerk stehen.
+
+
+> ARITHMETISCHE OPERATIONEN {ADD kay :=} {SUB Ccnt :=}
+ -{ 'a' + 10 }- -{ Ccnt - 10 }-
+
+> {MUL dest :=} {DIV dv := }
+ -{ var * -990 }- -{ dv / -10000}-
+
+Wenn der Eingang einer dieser Anweisungen ‘wahr’ ist, so setzt diese
+die vorliegende Zielvariable gleich dem vorliegenden arithmetischem
+Ausdruck. Die Operanden können entweder Variabelen (einschließlich Timer-
+und Zählervariabelen) oder Konstanten sein. Diese Anweisungen verwenden
+16-Bitzeichen Mathematik. Beachten Sie, dass das Ergebnis jeden Zyklus
+ausgewertet wird, wenn der Eingangszustand ‘wahr’ ist. Falls Sie eine
+Variable inkrementieren oder dekrementieren (d.h., wenn die Zielvariable
+ebenfalls einer der Operanden ist), dann wollen Sie dies vermutlich
+nicht; normalerweise würden Sie einen Impuls (one-shot) verwenden,
+sodass die Variable nur bei einer steigenden oder fallenden Flanke des
+Eingangszustands ausgewertet wird. Dividieren kürzt: D.h. 8 / 3 = 2.
+Diese Anweisungen müssen ganz rechts im Netzwerk stehen.
+
+
+> VERGLEICHEN [var ==] [var >] [1 >=]
+ -[ var2 ]- -[ 1 ]- -[ Ton]-
+
+> [var /=] [-4 < ] [1 <=]
+ -[ var2 ]- -[ vartwo]- -[ Cup]-
+
+Wenn der Eingang dieser Anweisung ‘unwahr’ ist, so ist der Ausgang
+auch ‘unwahr’. Wenn der Eingang dieser Anweisung ‘wahr’ ist, dann ist
+Ausgang ‘wahr’; dies aber nur, wenn die vorliegende Bedingung ‘wahr’
+ist. Diese Anweisungen können zum Vergleichen verwendet werden, wie:
+Auf gleich, auf größer als, auf größer als oder gleich, auf ungleich,
+auf kleiner als, auf kleiner als oder gleich, eine Variable mit einer
+Variablen oder eine Variable mit einer 16-Bitzeichen-Konstanten.
+
+
+> ZÄHLER CName CName
+ --[CTU >=5]-- --[CTD >=5]—
+
+Ein Zähler inkrementiert (CTU, aufwärtszählen) oder dekrementiert
+(CTD, abwärtszählen) die bezogene Zählung bei jeder steigenden Flanke
+des Eingangszustands des Netzwerks (d.h. der Eingangszustand des
+Netzwerks geht von ‘unwahr’ auf ‘wahr’ über). Der Ausgangszustand des
+Zählers ist ‘wahr’, wenn die Zähler- variable ist größer oder gleich 5
+und andernfalls ‘unwahr’. Der Ausgangszustand des Netzwerks kann ‘wahr’
+sein, selbst wenn der Eingangszustand ‘unwahr’ ist; das hängt lediglich
+von Zählervariablen ab. Sie können einer CTU- und CTD-Anweisung den
+gleichen Namen zuteilen, um den gleichen Zähler zu inkrementieren und
+dekrementieren. Die RES-Anweisung kann einen Zähler rücksetzen oder auch
+eine gewöhnliche Variablen-Operation mit der Zählervariablen ausführen.
+
+
+> ZIRKULIERENDER ZÄHLER CName
+ --{CTC 0:7}--
+
+Ein zirkulierender Zähler arbeitet wie ein normaler CTU-Zähler, außer
+nach der Erreichung seiner Obergrenze, rücksetzt er seine Zählervariable
+auf Null. Zum Beispiel würde der oben gezeigte Zähler, wie folgt zählen:
+0, 1, 2, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2,.... Dies ist
+hilfreich in Kombination mit bedingten Anweisungen der Variablen‘CName’;
+Sie können dies als eine Folgeschaltung verwenden. CTC-Zähler takten
+mit der aufsteigenden Flanke der Eingangsbedingung des Netzwerks.
+Diese Anweisung muss ganz rechts im Netzwerk stehen.
+
+
+> SCHIEBEREGISTER {SHIFT REG }
+ -{ reg0..3 }-
+
+Ein Schieberegister besteht aus einer Reihe von Variablen. So bestünde
+zum Beispiel ein Schieberegister aus den Variablen ‘reg0’, ‘reg1’,
+‘reg2’, and ‘reg3’. Der Eingang des Schieberegisters ist ‘reg0’. Bei
+jeder steigenden Flanke der Eingansbedingung des Netzwerks, schiebt das
+Schieberegister nach rechts. Dies bedeutet es wie folgt zuweist: ‘reg3’
+nach ‘reg2’, ‘reg2’ nach ‘reg1’ und ‘reg1’ nach ‘reg0’. ‘reg0’ bleibt
+unverändert. Ein großes Schieberegister kann leicht viel Speicherplatz
+belegen. Diese Anweisung muss ganz rechts im Netzwerk stehen.
+
+
+> NACHSCHLAG-TABELLE {dest := }
+ -{ LUT[i] }-
+
+Eine Nachschlag-Tabelle ist eine Anordnung von n Werten. Wenn die
+Eingangsbedingung des Netzwerks ‘wahr’ ist, so wird die Ganzzahl-Variable
+‘dest’ mit dem Eintrag in der Nachschlag-Tabelle gleichgesetzt, der der
+Ganzzahl-Variablen ‘i’ entspricht. Das Verzeichnis beginnt bei Null,
+insofern muss sich ‘i’ zwischen 0 und (n-1) befinden. Das Verhalten
+dieser Anweisung ist undefiniert, wenn sich die Werte des Verzeichnisses
+außerhalb dieses Bereichs befinden.
+
+
+> NÄHERUNGS-LINEAR-TABELLE {yvar :=}
+ -{PWL[xvar] }-
+
+Dies ist eine gute Methode für die Näherungslösung einer komplizierten
+Funktion oder Kurve. Sie könnte zum Beispiel hilfreich sein, wenn Sie
+versuchen eine Eichkurve zu verwenden, um die rohe Ausgangsspannung
+eines Fühlers in günstigere Einheiten zu wandeln.
+
+Angenommen Sie versuchen eine Näherungslösung für eine Funktion zu finden,
+die eine Eingangs-Ganzzahlvariable ‘x’ in Ausgangs-Ganzzahlvariable ‘y’
+wandelt. Einige Punkte der Funktion sind Ihnen bekannt; so würden Sie
+z.B. die folgenden kennen:
+
+ f(0) = 2
+ f(5) = 10
+ f(10) = 50
+ f(100) = 100
+
+Dies bedeutet, dass sich die Punkte
+
+ (x0, y0) = ( 0, 2)
+ (x1, y1) = ( 5, 10)
+ (x2, y2) = ( 10, 50)
+ (x3, y3) = (100, 100)
+
+in dieser Kurve befinden. Diese 4 Punkte können Sie in die Tabelle der
+‘Näherungs-Linear’-Anweisung eintragen. Die ‘Näherungs-Linear’-Anweisung
+wird dann auf den Wert von ‘xvar’ schauen und legt den Wert von ‘yvar’
+fest. Sie stellt ‘yvar’ so ein, dass die ‘Näherungs-Linear’-Kurve sich
+durch alle Punkte bewegt, die Sie vorgegeben haben. Wenn Sie z.B. für
+‘xvar’ = 10 vorgegeben haben, dann stellt die Anweisung ‘yvar’ auf gleich
+50 ein.
+
+Falls Sie dieser Anweisung einen Wert ‘xvar’ zuweisen, der zwischen zwei
+Werten von ‘x’ liegt, denen Sie Punkte zugeordnet haben, dann stellt die
+Anweisung ‘yvar’ so ein, dass (‘xvar’, ‘yvar’) in der geraden Linie liegt;
+diejenige die, die zwei Punkte in der Tabelle verbindet. Z.B. erzeugt
+‘xvar’ = 55 bei ‘yvar’ = 75. Die beiden Punkte in der Tabelle sind (10,
+50) und (100, 100). 55 liegt auf halbem Weg zwischen 10 und 100 und 75
+liegt auf halbem Weg zwischen 50 und 100, somit liegt (55, 75) auf der
+Linie, die diese zwei Punkte verbindet.
+
+Die Punkte müssen in aufsteigender Reihenfolge der x-Koordinaten
+angegeben werden. Einige mathematische Operationen, erforderlich für
+bestimmte Nachschlag-Tabellen mit 16-Bit-Mathematik, kann man ggf. nicht
+ausführen. In diesem Falle gibt LDmicro eine Warnmeldung aus. So würde
+z.B. die folgende Nachschlag-Tabelle eine Fehlermeldung hervorrufen:
+
+ (x0, y0) = ( 0, 0)
+ (x1, y1) = (300, 300)
+
+Sie können diesen Fehler beheben, indem sie den Abstand zwischen den
+Punkten kleiner machen. So ist zum Beispiel die nächste Tabelle äquivalent
+zur vorhergehenden, ruft aber keine Fehlermeldung hervor.
+
+ (x0, y0) = ( 0, 2)
+ (x1, y1) = (150, 150)
+ (x2, y2) = (300, 300)
+
+Es wird kaum einmal notwendig sein, mehr als fünf oder sechs Punkte
+zu verwenden. Falls Sie mehr Punkte hinzufügen, so vergrößert dies
+Ihren Code und verlangsamt die Ausführung. Falls Sie für ‘xvar’ einen
+Wert vergeben, der größer ist, als die größte x-Koordinate der Tabelle
+oder kleiner, als die kleinste x-Koordinate in der Tabelle, so ist das
+Verhalten der Anweisung undefiniert. Diese Anweisung muss ganz rechts
+im Netzwerk stehen.
+
+
+> A/D-WANDLER EINLESEN AName
+ --{READ ADC}--
+
+LDmicro kann einen Code erzeugen, der ermöglicht, die A/D-Wandler
+zu verwenden, die in manchen Mikroprozessoren vorgesehen sind.
+Wenn der Eingangszustand dieser Anweisung ‘wahr’ ist, dann wird eine
+Einzellesung von dem A/D-Wandler entnommen und in der Variablen ‘AName’
+gespeichert. Diese Variable kann anschließend mit einer gewöhnlichen
+Ganzzahlvariablen bearbeitet werden (wie: Kleiner als, größer als,
+arithmetisch usw.). Weisen Sie ‘Axxx’ in der gleichen Weise einen Pin
+zu, wie Sie einen Pin für einen digitalen Ein- oder Ausgang vergeben
+würden, indem auf diesen in der Liste unten in der Maske (Bildanzeige)
+doppelklicken. Wenn der Eingangszustand dieses Netzwerks ‘unwahr’ ist,
+so wird die Variable ‘AName’ unverändert belassen.
+
+Für alle derzeitig unterstützten Prozessoren gilt: Eine 0 Volt Lesung
+am Eingang des A/D-Wandlers entspricht 0. Eine Lesung gleich der
+Versorgungsspannung (bzw. Referenzspannung) entspricht 1023. Falls Sie
+AVR-Prozessoren verwenden, so verbinden Sie AREF mit Vdd. (Siehe Atmel
+Datenblatt, dort wird eine Induktivität von 100µH empfohlen). Sie können
+arithmetische Operationen verwenden, um einen günstigeren Maßstabfaktor
+festzulegen, aber beachten Sie, dass das Programm nur Ganzzahl-Arithmetik
+vorsieht. Allgemein sind nicht alle Pins als A/D-Wandler verwendbar. Die
+Software gestattet Ihnen nicht, einen Pin zuzuweisen, der kein A/D
+bzw. analoger Eingang ist. Diese Anweisung muss ganz rechts im Netzwerk
+stehen.
+
+
+> PULSWEITEN MODULATIONSZYKLUS FESTLEGEN duty_cycle
+ -{PWM 32.8 kHz}-
+
+LDmicro kann einen Code erzeugen, der ermöglicht, die PWM-Peripherie
+zu verwenden, die in manchen Mikroprozessoren vorgesehen ist. Wenn die
+Eingangsbedingung dieser Anweisung ‘wahr’ ist, so wird der Zyklus der
+PWM-Peripherie mit dem Wert der Variablen ‘duty cycle’ gleichgesetzt. Der
+‘duty cycle’ muss eine Zahl zwischen 0 und 100 sein. 0 entspricht immer
+‘low’ und 100 entsprechend immer ‘high’. (Wenn Sie damit vertraut sind,
+wie die PWM-Peripherie funktioniert, so bemerken Sie, dass dies bedeutet,
+dass LDmicro die ‘duty cycle’-Variable automatisch prozentual zu den
+PWM-Taktintervallen skaliert [= den Maßstabfaktor festlegt].)
+
+Sie können die PWM-Zielfrequenz in Hz definieren. Es kann vorkommen, dass
+die angegebene Frequenz nicht genau erreicht wird, das hängt davon ab,
+wie sich diese innerhalb der Taktfrequenz des Prozessors einteilt. LDmicro
+wählt dann die nächst erreichbare Frequenz; falls der Fehler zu groß ist,
+so wird eine Warnung ausgegeben. Höhere Geschwindigkeiten können die
+Auflösung beeinträchtigen.
+
+Diese Anweisung muss ganz rechts im Netzwerk stehen. Die ‘ladder
+logic’-Laufzeit verbraucht (schon) einen Timer, um die Zykluszeit
+zu messen. Dies bedeutet, dass die PWM nur bei den Mikroprozessoren
+verfügbar ist, bei denen mindestens zwei geeignete Timer vorhanden sind.
+PWM verwendet den PIN CCP2 (nicht CCP1) bei den PIC16-Prozessoren und
+OC2 (nicht OC1A) bei den AVR-Prozessoren.
+
+
+> REMANENT MACHEN saved_var
+ --{PERSIST}--
+
+Wenn der Eingangszustand dieser Anweisung ‘wahr’ ist, so bewirkt
+dies, dass eine angegebene Ganzzahl-Variable automatisch im EEPROM
+gespeichert wird. Dies bedeutet, dass ihr Wert bestehen bleiben wird,
+auch wenn der Prozessor seine Versorgungsspannung verliert. Es ist
+nicht notwendig, die Variable an klarer Stelle im EEPROM zu speichern,
+dies geschieht automatisch, so oft sich der Wert der Variablen
+ändert. Bei Spannungswiederkehr wird die Variable automatisch vom
+EEPROM zurückgespeichert. Falls eine Variable, die häufig ihren Wert
+ändert, remanent (dauerhaft) gemacht wird, so könnte Ihr Prozessor sehr
+rasch verschleißen, weil dieser lediglich für eine begrenzte Anzahl von
+Schreibbefehlen konstruiert ist (~100 000). Wenn der Eingangszustand des
+Netzwerks ‘unwahr’ ist, so geschieht nichts. Diese Anweisung muss ganz
+rechts im Netzwerk stehen.
+
+
+> UART (SERIELL) EMPFANGEN var
+ --{UART RECV}--
+
+LDmicro kann einen Code erzeugen, der ermöglicht UART zu verwenden,
+welcher in manchen Mikroprozessoren vorgesehen ist.
+Bei AVR-Prozessoren mir mehrfachem UART, wird nur UART1 (nicht UART0)
+unterstützt. Konfigurieren Sie die Baudrate, indem Sie ‘Voreinstellungen
+-> Prozessor-Parameter’ verwenden. Bestimmte Baudraten werden mit
+bestimmten Quarzfrequenzen nicht erreichbar sein. In diesem Fall gibt
+LDmicro eine Warnmeldung.
+
+Wenn der Eingangszustand dieser Anweisung ‘unwahr’ ist, so geschieht
+nichts. Wenn der Eingangszustand ‘wahr’ ist, so versucht diese Anweisung
+ein einzelnes Schriftzeichen vom UART-Eingang zu empfangen. Wenn
+kein Schriftzeichen eingelesen wird, dann ist der Ausgangszustand
+‘unwahr’. Wenn ein ASCII-Zeichen eingelesen wird, so wird sein Wert in
+‘var’ abgespeichert und der Ausgangszustand wird für einen einzelnen
+Zyklus ‘wahr’.
+
+
+> UART (SERIELL) SENDEN var
+ --{UART SEND}--
+
+LDmicro kann einen Code erzeugen, der ermöglicht UART zu verwenden,
+welcher in manchen Mikroprozessoren vorgesehen ist.
+Bei AVR-Prozessoren mir mehrfachem UART, wird nur UART1 (nicht UART0)
+unterstützt. Konfigurieren Sie die Baudrate, indem Sie ‘Voreinstellungen
+-> Prozessor-Parameter’ verwenden. Bestimmte Baudraten werden mit
+bestimmten Quarzfrequenzen nicht erreichbar sein. In diesem Fall gibt
+LDmicro eine Warnmeldung.
+
+Wenn der Eingangszustand dieser Anweisung ‘unwahr’ ist, so geschieht
+nichts. Wenn der Eingangszustand ‘wahr’ ist, so schreibt diese
+Anweisung ein einzelnes Schriftzeichen zum UART. Der ASCII-Wert des
+Schriftzeichens, welches gesendet werden soll, muss vorher in ‘var’
+abgespeichert worden sein. Der Ausgangszustand dieses Netzwerks ist
+‘wahr’, wenn UART beschäftigt ist (gerade dabei ein Schriftzeichen zu
+übermitteln) und andernfalls ‘unwahr’.
+
+Denken Sie daran, dass einige Zeit zum Senden von Schriftzeichen
+beansprucht wird. Überprüfen Sie den Ausgangszustand dieser Anweisung,
+sodass das erste Schriftzeichen bereits übermittelt wurde, bevor Sie
+versuchen ein zweites Schriftzeichen zu übermitteln. Oder verwenden Sie
+einen Timer, um eine Verzögerung zwischen die Schriftzeichen fügen. Sie
+dürfen den Eingangszustand dieser Anweisung nur dann auf ‘wahr’ setzen
+(bzw. ein Schriftzeichen übermitteln), wenn der Ausgangszustand ‘unwahr’
+ist (bzw. UART unbeschäftigt ist).
+
+Untersuchen Sie die “Formatierte Zeichenfolge”-Anweisung, bevor Sie
+diese Anweisung verwenden. Die “Formatierte Zeichenfolge”- Anweisung
+ist viel einfacher in der Anwendung und fast sicher fähig, das zu tun,
+was Sie beabsichtigen.
+
+
+> FORMATIERTE ZEICHENFOLGE ÜBER UART var
+ -{"Druck: \3\r\n"}-
+
+LDmicro kann einen Code erzeugen, der ermöglicht UART zu verwenden,
+welcher in manchen Mikroprozessoren vorgesehen ist.
+Bei AVR-Prozessoren mir mehrfachem UART, wird nur UART1 (nicht UART0)
+unterstützt. Konfigurieren Sie die Baudrate, indem Sie ‘Voreinstellungen
+-> Prozessor-Parameter’ verwenden. Bestimmte Baudraten werden mit
+bestimmten Quarzfrequenzen nicht erreichbar sein. In diesem Fall gibt
+LDmicro eine Warnmeldung.
+
+Wenn der Eingangszustand des Netzwerks für diese Anweisung von ‘unwahr’
+auf ‘wahr’ übergeht, so beginnt diese eine vollständige Zeichenfolge
+über den seriellen Anschluss zu senden. Wenn die Zeichenfolge die
+besondere Reihenfolge ‘\3’ enthält, dann wird diese Folge durch den Wert
+von ‘var’ ersetzt, welcher automatisch in eine Zeichenfolge gewandelt
+wird. Die Variable wird formatiert, sodass diese exakt 3 Schriftzeichen
+übernimmt. Falls die Variable zum Beispiel gleich 35 ist, dann wird die
+exakte ausgegebene Zeichenfolge, wie folgt aussehen: ‘Druck: 35\r\n’
+(beachten Sie das zusätzliche Freizeichen). Wenn stattdessen die Variable
+gleich 1432 ist, so wäre das Verhalten der Anweisung undefiniert,
+weil 1432 mehr als drei Stellen hat. In diesem Fall wäre es notwendig
+stattdessen ‘\4’ zu verwenden.
+
+Falls die Variable negativ ist, so verwenden Sie stattdessen ‘\-3d’
+(oder ‘\-4d’). LDmicro wird hierdurch veranlasst eine vorgestellte
+Freistelle für positive Zahlen und ein vorgestelltes Minuszeichen für
+negative Zahlen auszugeben.
+
+Falls mehrere “Formatierte Zeichenfolge”-Anweisungen zugleich ausgegeben
+werden (oder wenn eine neue Zeichenfolge ausgegeben wird bevor die
+vorherige vollendet ist), oder auch wenn diese mit UART TX Anweisungen
+vermischt, so ist das Verhalten undefiniert.
+
+Es ist auch möglich diese Anweisung für eine feste Zeichenfolge zu
+verwenden, die über den seriellen Anschluss gesendet wird, ohne den Wert
+einer Ganzzahlvariablen in den Text zu interpolieren. In diesem Fall
+fügen Sie einfach diese spezielle Steuerungsfolge nicht ein.
+
+Verwenden Sie ‘\\’ für einen zeichengetreuen verkehrten Schrägstrich.
+Zusätzlich zur Steuerungsfolge für die Interpolierung einer Ganzzahl-
+Variablen, sind die folgenden Steuerungszeichen erhältlich:
+
+ * \r -- carriage return Zeilenschaltung
+ * \n -- new line Zeilenwechsel
+ * \f -- form feed Formularvorschub
+ * \b -- backspace Rücksetzen
+ * \xAB -- character with ASCII value 0xAB (hex)
+ -- Schriftzeichen mit ASCII-Wert 0xAB (hex)
+
+Der Ausgangszustand des Netzwerks dieser Anweisung ist ‘wahr’, während
+diese Daten überträgt, ansonsten ‘unwahr’. Diese Anweisung benötigt eine
+große Menge des Programmspeichers, insofern sollte sie sparsam verwendet
+werden. Die gegenwärtige Umsetzung ist nicht besonders effizient, aber
+eine bessere würde Änderungen an sämtlichen Ausläufern des Programms
+benötigen.
+
+
+EIN HINWEIS ZUR VERWENDUNG DER MATHEMATIK
+=========================================
+
+Denken Sie daran, dass LDmicro nur 16-Bit mathematische Operationen
+ausführt. Dies bedeutet, dass das Endresultat jeder Berechnung,
+die Sie vornehmen, eine Ganzzahl zwischen -32768 und 32767 sein muss.
+Dies bedeutet auch, dass die Zwischenergebnisse Ihrer Berechnungen alle
+in diesem Bereich liegen müssen.
+
+Wollen wir zum Beispiel annehmen, dass Sie folgendes berechnen möchten
+y = (1/x) * 1200, in der x zwischen 1 und 20 liegt.
+Dann liegt y zwischen 1200 und 60, was in eine 16-Bit Ganzzahl passt,
+so wäre es zumindest theoretisch möglich diese Berechnung auszuführen.
+Es gibt zwei Möglichkeiten, wie Sie dies codieren könnten: Sie können
+die Reziproke (Kehrwert) ausführen and dann multiplizieren:
+
+ || {DIV temp :=} ||
+ ||---------{ 1 / x }----------||
+ || ||
+ || {MUL y := } ||
+ ||----------{ temp * 1200}----------||
+ || ||
+
+Oder Sie könnten einfach die Division in einem Schritt direkt vornehmen.
+
+ || {DIV y :=} ||
+ ||-----------{ 1200 / x }-----------||
+
+
+Mathematisch sind die zwei äquivalent; aber wenn Sie diese ausprobieren,
+so werden Sie herausfinden, dass die erste ein falsches Ergebnis von
+y = 0 liefert. Dies geschieht, weil die Variable einen Unterlauf
+[= resultatabhängige Kommaverschiebung] ergibt. So sei zum Beispiel x = 3,
+(1 / x) = 0.333, dies ist aber keine Ganzzahl; die Divisionsoperation
+nähert dies, als 'temp = 0'. Dann ist y = temp * 1200 = 0. Im zweiten
+Fall gibt es kein Zwischenergebnis, welches einen Unterlauf [= resultats-
+abhängige Kommaverschiebung] ergibt, somit funktioniert dann alles.
+
+Falls Sie Probleme bei Ihren mathematischen Operationen erkennen,
+dann überprüfen Sie die Zwischenergebnisse auf Unterlauf [eine
+resultatabhängige Kommaverschiebung] (oder auch auf Überlauf, der dann
+im Programm in Umlauf kommt; wie zum Beispiel 32767 + 1 = -32768).
+Wann immer möglich, wählen Sie Einheiten, deren Werte in einem Bereich
+von -100 bis 100 liegen.
+
+Falls Sie eine Variable um einen bestimmten Faktor vergrößern müssen, tun
+Sie dies, indem Sie eine Multiplikation und eine Division verwenden. Um
+zum Beispiel y = 1.8 * x zu vergrößern, berechnen Sie y = (9/5) * x,
+(was dasselbe ist, weil 1,8 = 9/5 ist), und codieren Sie dies als
+y = (9 * x)/5, indem Sie die Multiplikation zuerst ausführen.
+
+ || {MUL temp :=} ||
+ ||---------{ x * 9 }----------||
+ || ||
+ || {DIV y :=} ||
+ ||-----------{ temp / 5 }-----------||
+
+
+Dies funktioniert mit allen x < (32767 / 9), oder x < 3640. Bei höheren
+Werten würde die Variable ‘temp’ überfließen. Für x gibt es eine
+ähnliche Untergrenze.
+
+
+KODIER-STIL
+===========
+
+Ich gestatte mehrere Spulen in Parallelschaltung in einem einzigen
+Netzwerk unterzubringen. Das bedeutet, sie können ein Netzwerk, wie
+folgt schreiben:
+
+ || Xa Ya ||
+ 1 ||-------] [--------------( )-------||
+ || ||
+ || Xb Yb ||
+ ||-------] [------+-------( )-------||
+ || | ||
+ || | Yc ||
+ || +-------( )-------||
+ || ||
+
+Anstatt diesem:
+
+ || Xa Ya ||
+ 1 ||-------] [--------------( )-------||
+ || ||
+ || ||
+ || ||
+ || ||
+ || Xb Yb ||
+ 2 ||-------] [--------------( )-------||
+ || ||
+ || ||
+ || ||
+ || ||
+ || Xb Yc ||
+ 3 ||-------] [--------------( )-------||
+ || ||
+
+Rein theoretisch bedeutet das, dass Sie irgendein Programm, als ein
+gigantisches Netzwerk, schreiben könnten. Und es bestünde überhaupt
+keine Notwendigkeit mehrere Netzwerke zu verwenden. In der Praxis ist
+dies aber eine schlechte Idee, denn wenn Netzwerke komplexer werden, so
+werden sie auch schwieriger zu editieren, ohne Löschen und neu Schreiben
+von Anweisungen.
+
+Jedoch, ist es manchmal ein guter Einfall, verwandte Logik in einem
+einzelnen Netzwerk zusammenzufassen. Dies erzeugt einen beinahe
+identischen Code, als ob sie getrennte Netzwerke entworfen hätten, es
+zeigt aber, dass diese Anweisungen (Logik) verwandt ist, wenn man diese
+im Netzwerk-Diagramm betrachtet.
+
+ * * *
+
+Im Allgemeinen hält man es für eine schlechte Form, den Code in einer
+solchen Weise zu schreiben, dass sein Ergebnis von einer Folge von
+Netzwerken abhängt. So zum Beispiel ist der folgende Code nicht besonders
+gut, falls ‘xa’ und ‘xb’ jemals ‘wahr’ würden.
+
+ || Xa {v := } ||
+ 1 ||-------] [--------{ 12 MOV}--||
+ || ||
+ || Xb {v := } ||
+ ||-------] [--------{ 23 MOV}--||
+ || ||
+ || ||
+ || ||
+ || ||
+ || [v >] Yc ||
+ 2 ||------[ 15]-------------( )-------||
+ || ||
+
+Ich werde diese Regel brechen und indem ich dies so mache, entwerfe ich
+einen Code-Abschnitt, der erheblich kompakter ist. Hier zum Beispiel,
+zeige ich auf, wie ich eine 4-Bit binäre Größe von ‘xb3:0’ in eine
+Ganzzahl wandeln würde.
+
+ || {v := } ||
+ 3 ||-----------------------------------{ 0 MOV}--||
+ || ||
+ || Xb0 {ADD v :=} ||
+ ||-------] [------------------{ v + 1 }-----------||
+ || ||
+ || Xb1 {ADD v :=} ||
+ ||-------] [------------------{ v + 2 }-----------||
+ || ||
+ || Xb2 {ADD v :=} ||
+ ||-------] [------------------{ v + 4 }-----------||
+ || ||
+ || Xb3 {ADD v :=} ||
+ ||-------] [------------------{ v + 8 }-----------||
+ || ||
+
+Falls die TRANSFER-Anweisung (MOV) an das untere Ende des Netzwerks
+gebracht würde, anstatt auf das obere, so würde der Wert von ‘v’, an
+anderer Stelle im Programm gelesen, gleich Null sein. Das Ergebnis dieses
+Codes hängt daher von der Reihenfolge ab, in welcher die Anweisungen
+ausgewertet werden. Im Hinblick darauf, wie hinderlich es wäre, diesen
+Code auf eine andere Weise zu schreiben, nehme ich dies so hin.
+
+
+BUGS
+====
+
+LDmicro erzeugt keinen sehr effizienten Code; es ist langsam in der
+Ausführung und geht verschwenderisch mit dem Flash- und RAM-Speicher
+um. Trotzdem kann ein mittelgroßer PIC- oder AVR-Prozessor alles, was
+eine kleine SPS kann, somit stört dies mich nicht besonders.
+
+Die maximale Länge der Variabelen-Bezeichnungen (-Namen) ist sehr
+begrenzt. Dies ist so, weil diese so gut in das KOP-Programm (ladder)
+passen. Somit sehe ich keine gute Lösung für diese Angelegenheit.
+
+Falls Ihr Programm zu groß für die Zeit-, Programmspeicher- oder
+Datenspeicher-Beschränkungen des Prozessors ist, den Sie gewählt haben,
+so erhalten Sie keine Fehlermeldung. Es wird einfach irgendwo anders alles
+vermasseln. (Anmerkung: Das AVR STK500 gibt hierzu Fehlermeldungen aus.)
+
+Unsorgfältiges Programmieren bei den Datei Öffnen/Abspeichern-Routinen
+führen wahrscheinlich zu der Möglichkeit eines Absturzes oder es wird
+ein willkürlicher Code erzeugt, der eine beschädigte oder bösartige .ld
+Datei ergibt.
+
+Bitte berichten Sie zusätzliche Bugs oder richten Sie Anfragen für neue
+Programm-Bestandteile an den Autor (in Englisch).
+
+Thanks to:
+ * Marcelo Solano, for reporting a UI bug under Win98
+ * Serge V. Polubarjev, for not only noticing that RA3:0 on the
+ PIC16F628 didn't work but also telling me how to fix it
+ * Maxim Ibragimov, for reporting and diagnosing major problems
+ with the till-then-untested ATmega16 and ATmega162 targets
+ * Bill Kishonti, for reporting that the simulator crashed when the
+ ladder logic program divided by zero
+ * Mohamed Tayae, for reporting that persistent variables were broken
+ on the PIC16F628
+ * David Rothwell, for reporting several user interface bugs and a
+ problem with the "Export as Text" function
+
+Particular thanks to Heinz Ullrich Noell, for this translation (of both
+the manual and the program's user interface) into German.
+
+
+COPYING, AND DISCLAIMER
+=======================
+
+DO NOT USE CODE GENERATED BY LDMICRO IN APPLICATIONS WHERE SOFTWARE
+FAILURE COULD RESULT IN DANGER TO HUMAN LIFE OR DAMAGE TO PROPERTY. THE
+AUTHOR ASSUMES NO LIABILITY FOR ANY DAMAGES RESULTING FROM THE OPERATION
+OF LDMICRO OR CODE GENERATED BY LDMICRO.
+
+This program 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.
+
+This program 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 this program. If not, see .
+
+
+Jonathan Westhues
+
+Rijswijk -- Dec 2004
+Waterloo ON -- Jun, Jul 2005
+Cambridge MA -- Sep, Dec 2005
+ Feb, Mar 2006
+
+Email: user jwesthues, at host cq.cx
+
+
diff --git a/ldmicro/manual-fr.txt b/ldmicro/manual-fr.txt
new file mode 100644
index 0000000..57dc52e
--- /dev/null
+++ b/ldmicro/manual-fr.txt
@@ -0,0 +1,1017 @@
+INTRODUCTION
+============
+
+LDmicro génére du code natif pour certains microcontroleurs Microchip
+PIC16F et Atmel AVR. Usuellement les programmes de developpement pour ces
+microcontrolleurs sont écrits dans des langages comme l'assembleur , le
+C ou le Basic. Un programme qui utilise un de ces langages est une suite
+de commandes. Ces programmes sont puissants et adaptés à l'architecture
+des processeurs, qui de façon interne exécutent une liste d'instructions.
+
+Les API (Automates Programmables Industriels, PLC en anglais, SPS en
+allemand) utilisent une autre voie et sont programmés en Langage à
+Contacts (ou LADDER). Un programme simple est représenté comme ceci :
+
+
+ || ||
+ || Xbutton1 Tdon Rchatter Yred ||
+ 1 ||-------]/[---------[TON 1.000 s]-+-------]/[--------------( )-------||
+ || | ||
+ || Xbutton2 Tdof | ||
+ ||-------]/[---------[TOF 2.000 s]-+ ||
+ || ||
+ || ||
+ || ||
+ || Rchatter Ton Tnew Rchatter ||
+ 2 ||-------]/[---------[TON 1.000 s]----[TOF 1.000 s]---------( )-------||
+ || ||
+ || ||
+ || ||
+ ||------[END]---------------------------------------------------------||
+ || ||
+ || ||
+
+(TON est une tempo travail; TOF est une tempo repos. les commandes --] [--
+représentent des Entrées, qui peuvent être des contacts de relais. Les
+commandes --( )-- sont des Sorties, qui peuvent représenter des bobines de
+relais. Beaucoup de références de programmes de langage à contacts (LADDER)
+existent sur Internet et sont à quelques détails près, identiques à
+l'implémentation représentée ci-dessus.
+
+Un certain nombre de différences apparaissent entre les programmes en
+langage évolués ( C, Basic, Etc..) et les programmes pour API:
+
+ * Le programme est représenté dans un format graphique, et non
+ comme une liste de commandes en format texte. Beaucoup de personnes
+ trouve cela plus facile à comprendre.
+
+ * Au niveau de base, le programme apparait comme un diagramme
+ de circuit avec des contacts de relais (Entrées) et des bobines
+ (Sorties). Ceci est intuitif pour les programmeurs qui connaissent
+ la théorie des circuits électriques.
+
+ * Le compilateur de langage à contacts vérifie tout ceci lors
+ de la compilation. Vous n'avez pas à écrire de code quand une
+ Sortie est remplacée et est remise en Entrée ou si une temporisation
+ est modifiée, vous n'avez pas non plus à spécifier l'ordre où les
+ calculs doivent être effectués. L'outil API (PLC) s'occupe de cela
+ pour vous.
+
+
+LDmicro compile le langage à contact (ladder) en code pour PIC16F ou
+AVR. Les processeurs suivants sont supportés:
+
+ * PIC16F877
+ * PIC16F628
+ * PIC16F876 (non testé)
+ * PIC16F88 (non testé)
+ * PIC16F819 (non testé)
+ * PIC16F887 (non testé)
+ * PIC16F886 (non testé)
+ * ATmega128
+ * ATmega64
+ * ATmega162 (non testé)
+ * ATmega32 (non testé)
+ * ATmega16 (non testé)
+ * ATmega8 (non testé)
+
+Il doit être facile de supporter d'autres PIC ou AVR, mais je n'est
+aucun moyen pour les tester. Si vous en voulez un en particulier faites
+moi parvenir votre demande et je verrai ce que je peux faire.
+
+En utilisant LDmicro, vous dessinez un diagramme à contacts pour votre
+programme. Vous pouvez simuler le fonctionnement logique en temps réel sur
+votre PC. Quand vous êtes convaincu que le fonctionnement est correct,
+vous pouvez affecter les broches du microcontroleur pour les Entrées et
+Sorties, ensuite vous compilez votre programmeen code AVR ou PIC. Le
+fichier de sortie du compilateur est un fichier .HEX que vous devrez
+mettre dans le microcontroleur en utilisant un programmateur pour PIC
+ou AVR.
+
+
+LDmicro est conçu pour être similaire à la majorité des API commerciaux.
+Il y a quelques exceptions, et une partie des possibilités n'est
+pas standard avec le matériel industriel. Lire attentivement la
+description de chaque instruction même si elle parait familière. Ce
+document considère que vous avez une connaisance de base du langage à
+contact et de la structure des logiciels pour automates programmables.
+Cycle d'exécution : Lecture des Entrées -> Calculs -> Ecriture des Sorties
+
+
+CIBLES ADDITIONNELLES
+=====================
+
+Il est aussi possible de générer du code ANSI C . Vous pouvez utiliser
+ceci pour n'importe quel processeur dont vous avez un compilateur C,
+mais le runtime est de votre responsabilité. LDmicro gérére uniquement
+le source pour le cycle de l'API. Vous êtes responsable de l'appel de
+chaque séquence du cycle et de l'implémentation de toutes les Entrées
+/ Sorties (Lecture/Ecriture des Entrées digitales, etc ...). Voir les
+commentaires dans le code source pour plus de détails.
+
+Finalement, LDmicro peut générer un code byte indépendant du processeur
+pour une machine virtuelle prévue pour faire fonctionner ce type de code.
+J'ai prévu un exemple simple d'implémentation d'un interpréteur /VM
+écrit en code C le plus portable possible. La cible fonctionne juste sur
+quelques plateformes ou vous pouvez prévoir votre VM. Ceci peut être utile
+pour des applications ou vous pouvez utiliser le languages à contacts
+comme du langage script pour customiser un programme important. Voir
+les commentaires dans l'exemple pour les détails.
+
+
+OPTIONS LIGNE DE COMMANDE
+=========================
+
+LDmicro.exe fonctionne normallement sans options de ligne de commande.
+Vous pouvez faire un raccourci vers le programme et le sauvegarder sur
+l'écran , il suffit alors de faire un double clic pour le faire démarrer
+et vous vous retrouvez ainsi dans l'interface utilisateur.
+
+Si un nom de fichier est passé en ligne de de commande de LDmicro, (ex:
+`ldmicro.exe asd.ld'), alors LDmicro va essayer d'ouvrir `asd.ld', si
+il existe. Une erreur se produira si `asd.ld' n'existe pas. Vous avez
+la possibilité d'associer LDmicro avec les fichiers d'extention .ld.
+Ceci permet à LDmicro de démarrer automatiquement lors d'un double clic
+sur un fichier xxx.ld.
+
+Si les arguments de la ligne de commande sont passés sous la forme:
+`ldmicro.exe /c src.ld dest.hex', LDmicro compilera le programme`src.ld',
+et sauvegardera le fichier compilé sous`dest.hex'. Après compilation
+LDmicro se termine, que la compilation soit correcte ou pas. Aucun
+message n'est affiché sur la console. Ce mode est pratique uniquement
+lorsque vous exécutez LDmicro en ligne de commande.
+
+
+BASES
+=====
+
+Si vous exécutez LDmicro sans arguments de ligne de commande, il démarre
+avec un programme vide. Si vous démarrer avec le nom d'un programme
+langage à contacts (xxx.ld) en ligne de commande, il va essayer de
+charger le programme au démarrage. LDmicro utilise son format interne
+pour le programme , il ne peut pas importer de programmes édités par
+d'autres outils.
+
+Si vous ne chargez pas un programme existant, LDmicro démarre en insérant
+une ligne vide. Vous pouvez ajouter les instructions pour votre programme:
+par exemple ajouter un jeu de contacts (Instruction -> Insérer Contact)
+qui sera nommé `Xnew'. `X' désigne un contact qui peut être lié à une
+broche d'entrée du microcontroleur, vous pouvez affecter la broche pour
+ce contact plus tard après avoir choisi le microcontroleur et renommé
+les contacts. La première lettre indique de quel type de composants il
+s'agit par exemple :
+
+ * Xnom -- Relié à une broche d'entrée du microcontroleur
+ * Ynom -- Relié à une broche de sortie du microcontroleur
+ * Rnom -- `Relais interne': un bit en mémoire
+ * Tnom -- Temporisation; Tempo travail, tempo repos, ou totalisatrice
+ * Cnom -- Compteur, Compteur ou décompteur
+ * Anom -- Un entier lu sur un comvertisseur A/D
+ * nom -- Variable générique (Entier : Integer)
+
+Choisir le reste du nom pour décrire l'utilisation de ce que fait cet
+objet et qui doit être unique dans tout le programme. Un même nom doit
+toujours se référer au même objet dans le programme en entier.Par
+exemple , vous aurez une erreur si vous utilisez une tempo travail
+(TON) appellée TDelai et une tempo repos (TOF) appellée aussi TDelai
+dans le même programme, le comptage effectué par ces tempo utilisera le
+même emplacement en mémoire, mais il est acceptable d'avoir une tempo
+sauvegardée (RTO) Tdelai même nom avec une instruction de RES, dans ce
+cas l'instruction fonctionne avec le même timer.
+
+Les noms de variables peuvent être des lettres, chiffres ou le
+caractère _. Un nom de variable ne doit pas commencer par un chiffre.
+Les noms de variables sont sensibles à la casse (majuscule/minuscules).
+
+Les instructions de manipulation de variables (MOV, ADD, EQU,
+etc.) peuvent travailler avec des variables de n'importe quel nom. Elles
+peuvent avoir accès aux accumulateurs des temporisations ou des
+compteurs. Cela peut quelquefois être très utile, par exemple si vous
+voulez contrôler la valeur d'un compteur ou d'une temporisation dans
+une ligne particulière.
+
+Les variables sont toujours des entiers 16 bits. Leur valeur peut
+donc être comprise entre -32768 et 32767 inclus. Les variables sont
+toujours signées. Vous pouvez les spécifier de façon littérale comme
+des nombres décimaux normaux (0, 1234, -56), vous pouvez aussi les
+spécifier en caractères ASCII ('A', 'z') en mettant le caractère entre
+des guillemets simples. Vous pouvez utiliser un caractère ASCII dans la
+majorité des endroits où vous pouvez utiliser les nombres décimaux.
+
+En bas de l'écran, vous pouvez voir la liste de tous les objets
+utilisés dans votre programme. La liste est automatiquement générée
+à partir du programme. La majorité des objets ne necessitent aucune
+configuration. Seuls : les objets `Xnom', `Ynom', and `Anom' doivent être
+affectés à une broche du micro La première chose à faire est de choisir
+la microcontroleur utilisé : Paramères -> Microcontroleur ensuite vous
+affectez les broches en faisant un double clic dans la liste.
+
+Vous pouvez modifier le programme en insérant ou supprimant des
+instructions. Le curseur clignote dans la programme pour indiquer
+l'instruction courante sélectionnée et le point d'insertion. S'il ne
+clignote pas pressez ou cliquer sur une instruction, ou vous
+pouvez insérer une nouvelle instruction à la droite ou à la gauche
+(en série avec), ou au dessous ou au dessus (en parallèle avec) de
+l'instruction sélectionnée. Quelques opérations ne sont pas permises ,
+par exemple aucune instruction permise à droite de la bobine.
+
+Le programme démarre avec uniquement une ligne. Vous pouvez ajouter
+plusieurs lignes en sélectionnant Insertion -> Ligne avant ou après
+dans le menu. Vous pouvez faire un circuit complexe en plaçant plusieurs
+branches en parallèle ou en série avec une ligne, mais il est plus clair
+de faire plusieurs lignes.
+
+Une fois votre programme écrit, vous pouvez le tester par simulation,
+et le compiler dans un fichier HEX pour le microcontroleur de destination.
+
+SIMULATION
+==========
+
+Pour entrer dans la mode simulation choisir Simulation -> Simuler
+ou presser le programme est affiché différemment en mode
+simulation. Les instructions activées sont affichées en rouge vif, les
+instructions qui ne le sont pas sont affichées en grisé. Appuyer sur la
+barre d'espace pour démarrer l'API pour 1 cycle. Pour faire fonctionner
+continuellement en temps réel choisir Simulation ->Démarrer la simulation
+en temps réel ou presser L'affichage du programme est mise à
+jour en temps réel en fonction des changements d'état des entrées.
+
+Vous pouvez valider l'état des entrées du programme en faisant un
+double clic sur l'entrée dans la liste au bas de l'écran, ou sur le
+contact `Xnom' de l'instruction dans le programme, pour avoir le reflet
+automatiquement de la validation d'une entrée dans le programme, il
+faut que le programme soit en cycle.(le démarrer par ou barre
+d'espace pour un seul cycle).
+
+COMPILER EN CODE NATIF
+======================
+
+Le point final est de générer un fichier .HEX qui sera programmé dans le
+microcontroleur que vous avez choisi par Paramètres -> Microcontroleur
+Vous devez affecter les broches d'entrées sorties pour chaque 'Xnom'
+et 'Ynom'. Vous pouvez faire cela en faisant un double clic sur la nom
+de l'objet dans la liste au bas de l'écran. Une boite de dialogue vous
+demande de choisir une des broches non affectées dans la liste.
+
+Vous devez aussi choisir la temps de cycle que voulez utiliser pour
+votre application, vous devez aussi choisir la fréquence d'horloge du
+processeur. Faire Paramètres -> Paramètres MCU dans le menu. En général,
+le temps de cycle peut être laissé à la valeur par défaut (10 ms) qui est
+une bonne valeur pour la majorité des applications. Indiquer la fréquence
+du quartz utilisé (ou du résonateur céramique ou autres..) et cliquer OK.
+
+Maintenant vous pouvez créer le fichier pour intégrer dans le
+microcontroleur. Choisir Compilation -> Compiler, ou compiler sous...
+Si vous avez précédemment compilé votre programme, vous pouvez spécifier
+un nom différent de fichier de sortie. Si votre programme ne comporte
+pas d'erreur (lié à la structure du programme), LDmicro génére un fichier
+IHEX prêt à être programmé dans le chip.
+
+Utilisez votre logiciel et matériel de programmation habituel pour
+charger le fichier HEX dans la microcontroleur. Vérifiez et validez
+les bits de configuration (fuses), pour les processeurs PIC16Fxxx ces
+bits sont inclus dans le fichier HEX, et la majorité des logiciels de
+programmation les valident automatiquement, pour les processeurs AVR ,
+vous devez le faire manuellement.
+
+REFERENCE DES INSTRUCTIONS
+==========================
+
+> CONTACT, NORMALLEMENT OUVERT Xnom Rnom Ynom
+ ----] [---- ----] [---- ----] [----
+
+ Si le signal arrivant à cette instruction est FAUX (0) le signal
+ de sortie est aussi faux (0), s'il est vrai , il sera aussi vrai
+ en sortie si et uniquement si la broche d'Entrée ou de Sortie
+ ou de Relais interne est vraie, sinon l'instruction sera fausse.
+ Cette instruction peut vérifier l'état d'une broche d'entrée, d'une
+ broche de sortie ou d'un relais interne
+
+
+> CONTACT, NORMALLEMENT FERME Xnom Rnom Ynom
+ ----]/[---- ----]/[---- ----]/[----
+
+ Si le signal arrivant à cette instruction est FAUX (0) le signal
+ de sortie est vrai (1), s'il est vrai , il sera faux en sortie .
+ Cette instruction peut vérifier l'état d'une broche d'entrée, d'une
+ broche de sortie ou d'un relais interne. Fonctionne en opposition
+ par rapport au contact normallement ouvert.
+
+
+> BOBINE, NORMALE Rnom Ynom
+ ----( )---- ----( )----
+
+ Si le signal arrivant à cette instruction est faux, alors le relais
+ interne ou la broche de sortie est faux (mise à zéro). Si le signal
+ arrivant à cette instruction est vrai(1), alors le relais interne ou
+ la broche de sortie est validée (mise à 1). Il n'est pas important
+ d'affecter une variable à une bobine.
+ Cette instruction est placée le plus à droite dans une séquence.
+
+
+> BOBINE, INVERSE Rnom Ynom
+ ----(/)---- ----(/)----
+
+ Si le signal arrivant à cette instruction est vrai, alors le relais
+ interne ou la broche de sortie est faux (mise à zéro). Si le signal
+ arrivant à cette instruction est faux(0), alors le relais interne ou
+ la broche de sortie est validée (mise à 1). Il n'est pas important
+ d'affecter une variable à une bobine.
+ Cette instruction est placée le plus à droite dans une séquence.
+
+
+> BOBINE, ACCROCHAGE Rnom Ynom
+ ----(S)---- ----(S)----
+
+ Si le signal arrivant à cette instruction est vrai, alors le
+ relais interne ou la broche de sortie est validée (mise à 1). Cette
+ instruction permet de changer l'état d'un relais ou d'une sortie :
+ uniquement passe à vrai, ou reste vrai si elle était déjà à 1,
+ elle est typiquement utilisée en combinaison avec une Bobine REMISE
+ A ZERO.
+ Cette instruction est placée le plus à droite dans une séquence.
+
+
+> BOBINE, REMISE A ZERO Rnom Ynom
+ ----(R)---- ----(R)----
+
+ Si le signal arrivant à cette instruction est vrai, alors le relais
+ interne ou la sortie est mise à zéro (0), si elle était déjà à 0,
+ il n'y a aucun changement, cette instruction change l'état d'une
+ sortie uniquement si elle était à 1, cette instruction fonctionne en
+ combinaison avec l'instruction ci-dessus Bobine à ACCROCHAGE.
+ Cette instruction est placée le plus à droite dans une séquence.
+
+
+> TEMPORISATION TRAVAIL Tdon
+ -[TON 1.000 s]-
+
+ Quand la signal arrivant à cette instruction passe de faux à vrai
+ (0 à 1), le signal de sortie attend 1.000 seconde avant de passer
+ à 1. Quand le signal de commande de cette instruction passe ZERO,
+ le signal de sortie passe immédiatement à zéro. La tempo est remise
+ à zéro à chaque fois que l'entrée repasse à zéro. L'entrée doit être
+ maintenue vraie à 1 pendant au moins 1000 millisecondes consécutives
+ avant que la sortie ne devienne vraie. le délai est configurable.
+
+ La variable `Tnom' compte depuis zéro en unités de temps de scan.
+ L'instruction Ton devient vraie en sortie quand la variable du
+ compteur est plus grande ou égale au delai fixé. Il est possible
+ de manipuler la variable du compteur en dehors, par exemple par une
+ instruction MOVE.
+
+
+> TEMPORISATION REPOS Tdoff
+ -[TOF 1.000 s]-
+
+ Quand le signal qui arrive à l'instruction passe de l'état vrai
+ (1) à l'état faux (0), la sortie attend 1.000 s avant de dévenir
+ faux (0) Quand le signal arrivant à l'instruction passe de l'état
+ faux à l'état vrai, le signal passe à vrai immédiatement. La
+ temporisation est remise à zéro à chaque fois que l'entrée devient
+ fausse. L'entrée doit être maintenue à l'état faux pendant au moins
+ 1000 ms consécutives avant que la sortie ne passe à l'état faux. La
+ temporisation est configurable.
+
+ La variable `Tname' compte depuis zéro en unités de temps de scan.
+ L'instruction Ton devient vraie en sortie quand la variable du
+ compteur est plus grande ou égale au delai fixé. Il est possible
+ de manipuler la variable du compteur en dehors, par exemple par une
+ instruction MOVE.
+
+
+> TEMPORISATION TOTALISATRICE Trto
+ -[RTO 1.000 s]-
+
+ Cette instruction prend en compte le temps que l'entrée a été à l'état
+ vrai (1). Si l'entrée a été vraie pendant au moins 1.000s la sortie
+ devient vraie (1).L'entrée n'a pas besoin d'être vraie pendant 1000 ms
+ consécutives. Si l'entrée est vraie pendant 0.6 seconde puis fausse
+ pendant 2.0 secondes et ensuite vraie pendant 0.4 seconde, la sortie
+ va devenir vraie. Après être passé à l'état vrai, la sortie reste
+ vraie quelque soit la commande de l'instruction. La temporisation
+ doit être remise à zéro par une instruction de RES (reset).
+
+ La variable `Tnom' compte depuis zéro en unités de temps de scan.
+ L'instruction Ton devient vraie en sortie quand la variable du
+ compteur est plus grande ou égale au delai fixé. Il est possible
+ de manipuler la variable du compteur en dehors, par exemple par une
+ instruction MOVE.
+
+
+> RES Remise à Zéro Trto Citems
+ ----{RES}---- ----{RES}----
+
+ Cette instruction fait un remise à zéro d'une temporisation ou d'un
+ compteur. Les tempos TON et TOF sont automatiquement remisent à zéro
+ lorsque leurs entrées deviennent respectivement fausses ou vraies,
+ RES n'est pas donc pas nécessaire pour ces tempos. Les tempos RTO
+ et les compteurs décompteurs CTU / CTD ne sont pas remis à zéro
+ automatiquement, il faut donc utiliser cette instruction. Lorsque
+ l'entrée est vraie , le compteur ou la temporisation est remis à
+ zéro. Si l'entrée reste à zéro, aucune action n'est prise.
+ Cette instruction est placée le plus à droite dans une séquence.
+
+
+> FRONT MONTANT _
+ --[OSR_/ ]--
+
+ La sortie de cette instruction est normallement fausse. Si
+ l'instruction d'entrée est vraie pendant ce scan et qu'elle était
+ fausse pendant le scan précédent alors la sortie devient vraie. Elle
+ génére une impulsion à chaque front montant du signal d'entrée. Cette
+ instruction est utile si vous voulez intercepter le front montant
+ du signal.
+
+
+> FRONT DESCENDANT _
+ --[OSF \_]--
+
+ La sortie de cette instruction est normallement fausse. Si
+ l'instruction d'entrée est fausse (0) pendant ce scan et qu'elle
+ était vraie (1) pendant le scan précédent alors la sortie devient
+ vraie. Elle génére une impulsion à chaque front descendant du signal
+ d'entrée. Cette instruction est utile si vous voulez intercepter le
+ front descendant d'un signal.
+
+
+> COURT CIRCUIT (SHUNT), CIRCUIT OUVERT
+ ----+----+---- ----+ +----
+
+ Une instruction shunt donne en sortie une condition qui est toujours
+ égale à la condition d'entrée. Une instruction Circuit Ouvert donne
+ toujours une valeur fausse en sortie.
+ Ces instructions sont en général utilisées en phase de test.
+
+
+> RELAIS DE CONTROLE MAITRE
+ -{MASTER RLY}-
+
+ Par défaut, la condition d'entrée d'une ligne est toujours vraie. Si
+ une instruction Relais de contrôle maitre est exécutée avec une
+ valeur d'entrée fausse, alors toutes les lignes suivantes deviendront
+ fausses. Ceci va continuer jusqu'à la rencontre de la prochaine
+ instruction relais de contrôle maitre qui annule l'instruction de
+ départ. Ces instructions doivent toujours être utilisées par paires:
+ une pour commencer (qui peut être sous condition) qui commence la
+ partie déactivée et une pour la terminer.
+
+
+> MOUVOIR {destvar := } {Tret := }
+ -{ 123 MOV}- -{ srcvar MOV}-
+
+ Lorsque l'entrée de cette instruction est vraie, elle va mettre la
+ variable de destination à une valeur égale à la variable source ou à
+ la constante source. Quand l'entrée de cette instruction est fausse
+ rien ne se passe. Vous pouvez affecter n'importe quelle variable
+ à une instruction de déplacement, ceci inclu l'état de variables
+ compteurs ou temporisateurs qui se distinguent par l'entête T ou
+ C. Par exemple mettre 0 dans Tsauvegardé équivaut à faire une RES
+ de la temporisation. Cette instruction doit être complétement à
+ droite dans une séquence.
+
+
+> OPERATIONS ARITHMETIQUES {ADD kay :=} {SUB Ccnt :=}
+ -{ 'a' + 10 }- -{ Ccnt - 10 }-
+
+> {MUL dest :=} {DIV dv := }
+ -{ var * -990 }- -{ dv / -10000}-
+
+ Quand l'entrée de cette instruction est vraie, elle place en
+ destination la variable égale à l'expression calculée. Les opérandes
+ peuvent être des variables (en incluant les variables compteurs et
+ tempos) ou des constantes. Ces instructions utilisent des valeurs 16
+ bits signées. Il faut se souvenir que le résultat est évalué à chaque
+ cycle tant que la condition d'entrée est vraie. Si vous incrémentez
+ ou décrémentez une variable (si la variable de destination est
+ aussi une des opérandes), le résultat ne sera pas celui escompté,
+ il faut utiliser typiquement un front montant ou descendant de la
+ condition d'entrée qui ne sera évalué qu'une seule fois. La valeur
+ est tronquée à la valeur entière. Cette instruction doit être
+ complétement à droite dans une séquence.
+
+
+> COMPARER [var ==] [var >] [1 >=]
+ -[ var2 ]- -[ 1 ]- -[ Ton]-
+
+> [var /=] [-4 < ] [1 <=]
+ -[ var2 ]- -[ vartwo]- -[ Cup]-
+
+ Si l'entrée de cette instruction est fausse alors la sortie est
+ fausse. Si l'entrée est vraie, alors la sortie sera vraie si et
+ uniquement si la condition de sortie est vraie. Cette instruction
+ est utilisée pour comparer (Egalité, plus grand que,plus grand ou
+ égal à, inégal, plus petit que, plus petit ou égal à) une variable à
+ une autre variable, ou pour comparer une variable avec une constante
+ 16 bits signée.
+
+
+> COMPTEUR DECOMPTEUR Cnom Cnom
+ --[CTU >=5]-- --[CTD >=5]--
+
+ Un compteur incrémente ( Compteur CTU, count up) ou décrémente
+ (Décompteur CTD, count down) une variable à chaque front montant de
+ la ligne en condition d'entrée (CAD quand la signal passe de l'état
+ 0 à l'état 1. La condition de sortie du compteur est vraie si la
+ variable du compteur est égale ou plus grande que 5 (dans l'exemple),
+ et faux sinon. La condition de sortie de la ligne peut être vraie,
+ même si la condition d'entrée est fausse, cela dépend uniquement de la
+ valeur de la variable du compteur. Vous pouvez avoir un compteur ou
+ un décompteur avec le même nom, qui vont incréménter ou decrémenter
+ une variable. L'instruction Remise à Zéro permet de resetter un
+ compteur (remettre à zéro), il est possible de modifier par des
+ opérations les variables des compteurs décompteurs.
+
+
+> COMPTEUR CYCLIQUE Cnom
+ --{CTC 0:7}--
+
+ Un compteur cyclique fonctionne comme un compteur normal
+ CTU, exception faite, lorsque le compteur arrive à sa
+ limite supérieure, la variable du compteur revient à 0. dans
+ l'exemple la valeur du compteur évolue de la façon suivante :
+ 0,1,2,4,5,6,7,0,1,2,3,4,5,6,7,0,1,3,4,5,etc. Ceci est très pratique
+ en conbinaison avec des intructions conditionnelles sur la variable
+ Cnom. On peut utiliser ceci comme un séquenceur, l'horloge du compteur
+ CTC est validée par la condition d'entrée associée à une instruction
+ de front montant.
+ Cette instruction doit être complétement à droite dans une séquence.
+
+
+> REGISTRE A DECALAGE {SHIFT REG }
+ -{ reg0..3 }-
+
+ Un registre à décalage est associé avec un jeu de variables. Le
+ registre à décalage de l'exemple donné est associé avec les
+ variables`reg0', `reg1', `reg2', and `reg3'. L'entrée du registre à
+ décalage est `reg0'. A chaque front montant de la condition d'entrée
+ de la ligne, le registre à décalage va décaler d'une position à
+ droite. Ce qui donne `reg3 := reg2', `reg2 := reg1'. et `reg1 :=
+ reg0'.`reg0' est à gauche sans changement. Un registre à décalage
+ de plusieurs éléments peut consommer beaucoup de place en mémoire.
+ Cette instruction doit être complétement à droite dans une séquence.
+
+
+> TABLEAU INDEXE {dest := }
+ -{ LUT[i] }-
+
+ Un tableau indexé et un groupe ordonné de n valeurs Quand la condition
+ d'entrée est vraie, la variable entière `dest' est mise à la valeur
+ correspondand à l'index i du tableau. L'index est compris entre 0 et
+ (n-1). Le comportement de cette instruction est indéfini si l'index
+ est en dehors du tableau
+ Cette instruction doit être complétement à droite dans une séquence.
+
+
+> TABLEAU ELEMENTS LINEAIRES {yvar := }
+ -{ PWL[xvar] }-
+
+ C'est une bonne méthode pour évaluer de façon approximative une
+ fonction compliquée ou une courbe. Très pratique par exemple pour
+ appliquer une courbe de calibration pour linéariser tension de sortie
+ d'un capteur dans une unité convenable.
+
+ Supposez que vous essayez de faire une fonction pour convertir une
+ variable d'entrée entière, x, en une variable de sortie entière, y,
+ vous connaissez la fonction en différents points, par exemple vous
+ connaissez :
+
+ f(0) = 2
+ f(5) = 10
+ f(10) = 50
+ f(100) = 100
+
+ Ceci donne les points
+
+ (x0, y0) = ( 0, 2)
+ (x1, y1) = ( 5, 10)
+ (x2, y2) = ( 10, 50)
+ (x3, y3) = (100, 100)
+
+ liés à cette courbe. Vous pouvez entrer ces 4 points dans un
+ tableau associé à l'instruction tableau d'éléments linéaires. Cette
+ instruction regarde la valeur de xvar et fixe la valeur de yvar
+ correspondante. Par exemple si vous mettez xvar = 10 , l'instruction
+ validera yvar = 50.
+
+ Si vous mettez une instruction avec une valeur xvar entre deux valeurs
+ de x du tableau (et par conséquence aussi de yvar). Une moyenne
+ proportionnelle entre les deux valeurs , précédente et suivante de
+ xvar et de la valeur liée yvar, est effectuée. Par exemple xvar =
+ 55 donne en sortie yvar = 75 Les deux points xvar (10.50) et yvar
+ (50,75) , 55 est la moyenne entre 10 et 100 et 75 est la moyenne
+ entre 50 et 100, donc (55,75) sont liés ensemble par une ligne de
+ connection qui connecte ces deux points.
+
+ Ces points doivent être spécifiés dans l'ordre ascendant des
+ coordonnées x. Il peut être impossible de faire certaines opérations
+ mathématiques nécessaires pour certains tableaux, utilisant des
+ entiers 16 bits. Dans ce LDmicro va provoquer une alarme. Ce tableau
+ va provoquer une erreur :
+
+ (x0, y0) = ( 0, 0)
+ (x1, y1) = (300, 300)
+
+ Vous pouvez supprimer ces erreurs en diminuant l'écart entre les
+ points du tableau, par exemple ce tableau est équivalent à celui ci
+ dessus , mais ne provoque pas d'erreur:
+
+ (x0, y0) = ( 0, 0)
+ (x1, y1) = (150, 150)
+ (x2, y2) = (300, 300)
+
+ Il n'est pratiquement jamais nécessaire d'utiliser plus de 5 ou
+ 6 points. Ajouter des points augmente la taille du code et diminue
+ sa vitesse d'exécution. Le comportement, si vous passez une valeur
+ à xvar plus grande que la plus grande valeur du tableau , ou plus
+ petit que la plus petite valeur, est indéfini.
+ Cette instruction doit être complétement à droite dans une séquence.
+
+
+> LECTURE CONVERTISSEUR A/D Anom
+ --{READ ADC}--
+
+ LDmicro peut générer du code pour utiliser les convertisseurs A/D
+ contenus dans certains microcontroleurs. Si la condition d'entrée
+ de l'instruction est vraie, alors une acquisition du convertisseur
+ A/D est éffectuée et stockée dans la variable Anom. Cette variable
+ peut être par la suite traitée comme les autres variables par les
+ différentes opérations arithmétiques ou autres. Vous devez affecter
+ une broche du micro à la variable Anom de la même façon que pour
+ les entrées et sorties digitales par un double clic dans la list
+ affichée au bas de l'écran. Si la condition d'entrée de la séquence
+ est fausse la variable Anom reste inchangée.
+
+ Pour tous les circuits supportés actuellement, 0 volt en entrée
+ correspond à une lecture ADC de 0, et une valeur égale à VDD (la
+ tension d'alimentation ) correspond à une lecture ADC de 1023. Si
+ vous utilisez un circuit AVR, vous devez connecter Aref à VDD.
+
+ Vous pouvez utiliser les opérations arithmétiques pour mettre à
+ l'échelle les lectures effectuées dans l'unité qui vous est la plus
+ appropriée. Mais souvenez vous que tous les calculs sont faits en
+ utilisant les entiers.
+
+ En général, toutes les broches ne sont pas utilisables pour les
+ lecture de convertisseur A/D. Le logiciel ne vous permet pas
+ d'affecter une broche non A/D pour une entrée convertisseur.
+ Cette instruction doit être complétement à droite dans une séquence.
+
+
+> FIXER RAPPORT CYCLE PWM duty_cycle
+ -{PWM 32.8 kHz}-
+
+ LDmicro peut générer du code pour utiliser les périphériques PWM
+ contenus dans certains microcontroleurs. Si la condition d'entrée
+ de cette instruction est vraie, le rapport de cycle du périphérique
+ PWM va être fixé à la valeur de la variable Rapport de cycle PWM.
+ Le rapport de cycle est un nombre compris entre 0 (toujours au
+ niveau bas) et 100 (toujours au niveau haut). Si vous connaissez la
+ manière dont les périphériques fonctionnent vous noterez que LDmicro
+ met automatiquement à l'échelle la variable du rapport de cycle en
+ pourcentage de la période d'horloge PWM.
+
+ Vous pouvez spécifier la fréquence de sortie PWM, en Hertz. Le
+ fréquence que vous spécifiez peut ne pas être exactement accomplie, en
+ fonction des divisions de la fréquence d'horloge du microcontroleur,
+ LDmicro va choisir une fréquence approchée. Si l'erreur est trop
+ importante, il vous avertit.Les vitesses rapides sont au détriment
+ de la résolution. Cette instruction doit être complétement à droite
+ dans une séquence.
+
+ Le runtime du language à contacts consomme un timers (du micro) pour
+ le temps de cycle, ce qui fait que le PWM est uniquement possible
+ avec des microcontroleurs ayant au moins deux timers utilisables.
+ PWM utilise CCP2 (pas CCP1) sur les PIC16F et OC2(pas OC1) sur les
+ Atmel AVR.
+
+> METTRE PERSISTANT saved_var
+ --{PERSIST}--
+
+ Quand la condition d'entrée de cette instruction est vraie, la
+ variable entière spécifiée va être automatiquement sauvegardée en
+ EEPROM, ce qui fait que cette valeur persiste même après une coupure
+ de l'alimentation du micro. Il n'y a pas à spécifier ou elle doit
+ être sauvegardée en EEPROM, ceci est fait automatiquement, jusqu'a
+ ce quelle change à nouveau. La variable est automatiquement chargée
+ à partir de l'EEPROM suite à un reset à la mise sous tension.
+
+ Si une variables, mise persistante, change fréquemment, l'EEPROM de
+ votre micro peut être détruite très rapidement, Le nombre de cycles
+ d'écriture dans l'EEPROM est limité à environ 100 000 cycles Quand
+ la condition est fausse, rien n'apparait. Cette instruction doit
+ être complétement à droite dans une séquence.
+
+
+> RECEPTION UART (SERIE) var
+ --{UART RECV}--
+
+ LDmicro peut générer du code pour utiliser l'UART, existant dans
+ certains microcontroleurs. Sur les AVR, avec de multiples UART,
+ uniquement l'UART1 est utilisable (pas l'UART0). Configurer la
+ vitesse en utilisant -> Paramètres -> Paramètres MCU. Toutes les
+ vitesses de liaison ne sont pas utilisables avec tous les quartz de
+ toutyes les fréquences. Ldmicro vous avertit dans ce cas.
+
+ Si la condition d'entrée de cette instruction est fausse, rien ne se
+ passe. Si la condition d'entrée est vraie, elle essaie de recevoir
+ un caractère en provenance de l'UART. Si aucun caractère n'est lu
+ alors la condition de sortie devient fausse. Si un caractère est
+ lu la valeur ASCII est stockée dans 'var' et la condition de sortie
+ est vraie pour un seul cycle API.
+
+
+> EMMISION UART (SERIE) var
+ --{UART SEND}--
+
+ LDmicro peut générer du code pour utiliser l'UART, existant dans
+ certains microcontroleurs. Sur les AVR, avec de multiples UART,
+ uniquement l'UART1 est utilisable (pas l'UART0). Configurer la
+ vitesse en utilisant -> Paramètres -> Paramètres MCU. Toutes les
+ vitesses de liaison ne sont pas utilisables avec tous les quartz
+ de toutyes les fréquences. Ldmicro vous avertit dans ce cas.
+
+ Si la condition d'entrée de cette instruction est fausse, rien ne
+ se passe. Si la condition d'entrée est vraie alors cette instruction
+ écrit un seul caractère vers l'UART. La valeur ASCII du caractère à
+ transmettre doit avoir été stocké dans 'var' par avance. La condition
+ de sortie de la séquence est vraie, si l'UART est occupée (en cours
+ de transmission d'un caractère), et fausse sinon.
+
+ Rappelez vous que les caractères mettent un certain temps pour être
+ transmis. Vérifiez la condition de sortie de cette instruction pour
+ vous assurer que le premier caractère à bien été transmis, avant
+ d'essayer d'envoyer un second caractère, ou utiliser une temporisation
+ pour insérer un délai entre caractères; Vous devez uniquement vérifier
+ que la condition d'entrée est vraie (essayez de transmettre un
+ caractère) quand la condition de sortie est fausse(UART non occuppée).
+
+ Regardez l'instruction Chaine formattée(juste après) avant d'utiliser
+ cette instruction, elle est plus simple à utiliser, et dans la
+ majorité des cas, est capable de faire ce dont on a besoin.
+
+
+> CHAINE FORMATTEE SUR UART var
+ -{"Pression: \3\r\n"}-
+
+ LDmicro peut générer du code pour utiliser l'UART, existant dans
+ certains microcontroleurs. Sur les AVR, avec de multiples UART,
+ uniquement l'UART1 est utilisable (pas l'UART0). Configurer la
+ vitesse en utilisant -> Paramètres -> Paramètres MCU. Toutes les
+ vitesses de liaison ne sont pas utilisables avec tous les quartz
+ de toutyes les fréquences. Ldmicro vous avertit dans ce cas.
+
+ Quand la condition d'entrée de cette instruction passe de faux à vrai,
+ elle commence à envoyer une chaine compléte vers le port série. Si
+ la chaine comporte la séquence spéciale '3', alors cette séquence
+ va être remplacée par la valeur de 'var', qui est automatiquement
+ converti en une chaine. La variable va être formattée pour prendre
+ exactement trois caractères; par exemple si var = 35, la chaine
+ exacte qui va être "imprimée" sera 'Pression: 35\r\n' (notez les
+ espaces supplémentaires). Si au lieu de cela var = 1432 , la sortie
+ est indéfinie parceque 1432 comporte plus de 3 digits. Dans ce cas
+ vous devez nécessairement utiliser '\4' à la place.
+
+ Si la variable peut être négative, alors utilisez '\-3d' (ou `\-4d'
+ etc.) à la place. Ceci oblige LDmicro à imprimer un espace d'entête
+ pour les nombres positifs et un signe moins d'entête pour les chiffres
+ négatifs.
+
+ Si de multiples instructions de chaines formattées sont activées au
+ même moment (ou si une est activée avant de finir la précédente)
+ ou si ces instructions sont imbriquées avec des instructions TX
+ (transmission), le comportement est indéfini.
+
+ Il est aussi possible d'utiliser cette instruction pour sortie une
+ chaine fixe, sans l'intervention d'une variable en valeur entière
+ dans le texte qui est envoyée sur la ligne série. Dans ce cas,
+ simplement ne pas inclure de séquence spéciale Escape.
+
+ Utiliser `\\' pour l'anti-slash. en addition à une séquence escape
+ pour intervenir sue une variables entière, les caractères de
+ contrôles suivants sont utilisables :
+
+ * \r -- retour en début de ligne
+ * \n -- nouvelle ligne
+ * \f -- saut de page
+ * \b -- retour arrière
+ * \xAB -- caractère avec valeur ASCII 0xAB (hex)
+
+ La condition de sortie de cette instruction est vraie quand elle
+ transmet des données, sinon elle est fausse. Cette instruction
+ consomme une grande quantité de mémoire, elle doit être utilisée
+ avec modération. L'implémentation présente n'est pas efficace, mais
+ une meilleure implémentation demanderait une modification de tout
+ le reste.
+
+NOTE CONCERNANT LES MATHS
+=========================
+
+Souvenez vous que LDmicro travaille uniquement en mathématiques entiers
+16 bits. Ce qui fait que le résultat final de même que tous les calculs
+mêmes intermédiaires seront compris entre -32768 et 32767.
+
+Par exemple, si vous voulez calculer y = (1/x)*1200,ou x est compris
+entre 1 et 20, ce qui donne un résultat entre 1200 et 60,le résultat est
+bien à l'intérieur d'un entier 16 bits, il doit donc être théoriquement
+possible de faire le calcul. Nous pouvons faire le calcul de deux façons
+d'abord faire 1/x et ensuite la multiplication:
+
+ || {DIV temp :=} ||
+ ||---------{ 1 / x }----------||
+ || ||
+ || {MUL y := } ||
+ ||----------{ temp * 1200}----------||
+ || ||
+
+Ou uniquement faire simplement la division en une seule ligne :
+
+ || {DIV y :=} ||
+ ||-----------{ 1200 / x }-----------||
+
+Mathématiquement c'est identique, la première donne y = 0 , c'est à dire
+une mauvais résultat, si nous prenons par exemple x = 3 , 1/x = 0.333 mais
+comme il s'agit d'un entier, la valeur pour Temp est de 0 et 0*1200 = 0
+donc résultat faux.Dans le deuxième cas, il n'y a pas de résultat
+intermédiaire et le résultat est correct dans tous les cas.
+
+Si vous trouvez un problème avec vos calculs mathématiques, vérifiez les
+résultats intermédiaires, si il n'y a pas de dépassement de capacités par
+rapports aux valeurs entières. (par exemple 32767 + 1 = -32768). Quand
+cela est possible essayer de choisir des valeurs entre -100 et 100.
+
+Vous pouvez utiliser un certain facteur de multiplication ou division pour
+mettre à l'echelle les variables lors de calculs par exemple : pour mettre
+mettre à l'echelle y = 1.8*x , il est possible de faire y =(9/5)*x
+(9/5 = 1.8) et coder ainsi y = (9*x)/5 en faisant d'abord la multiplication
+
+ || {MUL temp :=} ||
+ ||---------{ x * 9 }----------||
+ || ||
+ || {DIV y :=} ||
+ ||-----------{ temp / 5 }-----------||
+
+Ceci fonctionne tant que x < (32767 / 9), or x < 3640. Pour les grandes
+valeurs de x,la variable `temp' va se mettre en dépassement. Ceci est
+similaire vers la limite basse de x.
+
+
+STYLE DE CODIFICATION
+=====================
+
+Il est permis d'avoir plusieurs bobines en parallèle, contrôlées par
+une simple ligne comme ci-dessous :
+
+ || Xa Ya ||
+ 1 ||-------] [--------------( )-------||
+ || ||
+ || Xb Yb ||
+ ||-------] [------+-------( )-------||
+ || | ||
+ || | Yc ||
+ || +-------( )-------||
+ || ||
+
+à la place de ceci :
+
+ || Xa Ya ||
+ 1 ||-------] [--------------( )-------||
+ || ||
+ || ||
+ || ||
+ || ||
+ || Xb Yb ||
+ 2 ||-------] [--------------( )-------||
+ || ||
+ || ||
+ || ||
+ || ||
+ || Xb Yc ||
+ 3 ||-------] [--------------( )-------||
+ || ||
+
+Il est permis théoriquement d'écrire un programme avec une séquence très
+importante et de ne pas utiliser plusieurs lignes pour la faire. En
+pratique c'est une mauvaise idée, à cause de la compléxité que cela
+peut engendrer et plus difficile à éditer sans effacer et redessiner un
+certain nombre d'éléments de logique.
+
+Néanmoins, c'est une bonne idée de regrouper différents éléments d'un bloc
+logique dans une seule séquence. Le code généré est identique dans les
+deux cas, et vous pouvez voir ce que fait la séquence dans le diagramme
+à contacts.
+
+ * * *
+
+En général, il est considéré comme mauvais d'écrire du code dont la
+sortie dépend de l'ordre d'exécution. Exemple : ce code n'est pas très
+bon lorsque xa et xb sont vrai en même temps :
+
+ || Xa {v := } ||
+ 1 ||-------] [--------{ 12 MOV}--||
+ || ||
+ || Xb {v := } ||
+ ||-------] [--------{ 23 MOV}--||
+ || ||
+ || ||
+ || ||
+ || ||
+ || [v >] Yc ||
+ 2 ||------[ 15]-------------( )-------||
+ || ||
+
+Ci-dessous un exemple pour convertir 4 bits Xb3:0 en un entier :
+
+ || {v := } ||
+ 3 ||-----------------------------------{ 0 MOV}--||
+ || ||
+ || Xb0 {ADD v :=} ||
+ ||-------] [------------------{ v + 1 }-----------||
+ || ||
+ || Xb1 {ADD v :=} ||
+ ||-------] [------------------{ v + 2 }-----------||
+ || ||
+ || Xb2 {ADD v :=} ||
+ ||-------] [------------------{ v + 4 }-----------||
+ || ||
+ || Xb3 {ADD v :=} ||
+ ||-------] [------------------{ v + 8 }-----------||
+ || ||
+
+Si l'instruction MOV est déplacée en dessous des instructions ADD, la
+valeur de la variable v, quand elle est lue autrepart dans le programme,
+serait toujours 0. La sortie du code dépend alors de l'ordre d'évaluations
+des instructions. Ce serait possible de modifier ce code pour éviter cela,
+mais le code deviendrait très encombrant.
+
+DEFAUTS
+=======
+
+LDmicro ne génére pas un code très efficace; il est lent à exécuter et
+il est gourmand en Flash et RAM. Un PIC milieu de gamme ou un AVR peut
+tout de même faire ce qu'un petit automate peut faire.
+
+La longueur maximum des noms de variables est très limitée, ceci pour
+être intégrée correctement dans le diagramme logique, je ne vois pas de
+solution satisfaisante pour solutionner ce problème.
+
+Si votre programme est trop important, vitesse exécution, mémoire
+programme ou contraintes de mémoire de données pour le processeur que vous
+avez choisi, il n'indiquera probablement pas d'erreur. Il se blocquera
+simplement quelque part.
+
+Si vous êtes programmeur négligent dans les sauvegardes et les
+chargements, il est possible qu'un crash se produise ou exécute un code
+arbitraire corrompu ou un mauvais fichier .ld .
+
+SVP, faire un rapport sur les bogues et les demandes de modifications.
+
+Thanks to:
+ * Marcelo Solano, for reporting a UI bug under Win98
+ * Serge V. Polubarjev, for not only noticing that RA3:0 on the
+ PIC16F628 didn't work but also telling me how to fix it
+ * Maxim Ibragimov, for reporting and diagnosing major problems
+ with the till-then-untested ATmega16 and ATmega162 targets
+ * Bill Kishonti, for reporting that the simulator crashed when the
+ ladder logic program divided by zero
+ * Mohamed Tayae, for reporting that persistent variables were broken
+ on the PIC16F628
+ * David Rothwell, for reporting several user interface bugs and a
+ problem with the "Export as Text" function
+
+Particular thanks to Marcel Vaufleury, for this translation (of both
+the manual and the program's user interface) into French.
+
+
+COPYING, AND DISCLAIMER
+=======================
+
+DO NOT USE CODE GENERATED BY LDMICRO IN APPLICATIONS WHERE SOFTWARE
+FAILURE COULD RESULT IN DANGER TO HUMAN LIFE OR DAMAGE TO PROPERTY. THE
+AUTHOR ASSUMES NO LIABILITY FOR ANY DAMAGES RESULTING FROM THE OPERATION
+OF LDMICRO OR CODE GENERATED BY LDMICRO.
+
+This program 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.
+
+This program 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 this program. If not, see .
+
+
+Jonathan Westhues
+
+Rijswijk -- Dec 2004
+Waterloo ON -- Jun, Jul 2005
+Cambridge MA -- Sep, Dec 2005
+ Feb, Mar 2006
+
+Email: user jwesthues, at host cq.cx
+
+
diff --git a/ldmicro/manual-tr.txt b/ldmicro/manual-tr.txt
new file mode 100644
index 0000000..87a8bec
--- /dev/null
+++ b/ldmicro/manual-tr.txt
@@ -0,0 +1,790 @@
+
+KULLANIM KÝTAPÇIÐI
+==================
+LDMicro desteklenen MicroChip PIC16 ve Atmel AVR mikrokontrolcüler için
+gerekli kodu üretir. Bu iþ için kullanýlabilecek deðiþik programlar vardýr.
+Örneðin BASIC, C, assembler gibi. Bu programlar kendi dillerinde yazýlmýþ
+programlarý iþlemcilerde çalýþabilecek dosyalar haline getirirler.
+
+PLC'de kullanýlan dillerden biri ladder diyagramýdýr. Aþaðýda LDMicro ile
+yazýlmýþ basit bir program görülmektedir.
+
+ || ||
+ || Xbutton1 Tdon Rchatter Yred ||
+ 1 ||-------]/[---------[TON 1.000 s]-+-------]/[--------------( )-------||
+ || | ||
+ || Xbutton2 Tdof | ||
+ ||-------]/[---------[TOF 2.000 s]-+ ||
+ || ||
+ || ||
+ || ||
+ || Rchatter Ton Tnew Rchatter ||
+ 2 ||-------]/[---------[TON 1.000 s]----[TOF 1.000 s]---------( )-------||
+ || ||
+ || ||
+ || ||
+ ||------[END]---------------------------------------------------------||
+ || ||
+ || ||
+
+(TON=turn-on gecikme; TOF-turn-off gecikme. --] [-- giriþler, diðer bir
+deyiþle kontaklardýr. --( )-- ise çýkýþlardýr. Bunlar bir rölenin bobini
+gibi davranýrlar. Ladder diyagramý ile ilgili bol miktarda kaynak internet
+üzerinde bulunmaktadýr. Burada LDMicro'ya has özelliklerden bahsedeceðiz.
+
+LDmicro ladder diyagramýný PIC16 veya AVR koduna çevirir. Aþaðýda desteklenen
+iþlemcilerin listesi bulunmaktadýr:
+ * PIC16F877
+ * PIC16F628
+ * PIC16F876 (denenmedi)
+ * PIC16F88 (denenmedi)
+ * PIC16F819 (denenmedi)
+ * PIC16F887 (denenmedi)
+ * PIC16F886 (denenmedi)
+ * ATmega128
+ * ATmega64
+ * ATmega162 (denenmedi)
+ * ATmega32 (denenmedi)
+ * ATmega16 (denenmedi)
+ * ATmega8 (denenmedi)
+
+Aslýnda daha fazla PIC16 ve AVR iþlemci desteklenebilir. Ancak test ettiklerim
+ve desteklediðini düþündüklerimi yazdým. Örneðin PIC16F648 ile PIC16F628
+arasýnda fazla bir fark bulunmamaktadýr. Eðer bir iþlemcinin desteklenmesini
+istiyorsanýz ve bana bildirirseniz ilgilenirim.
+
+LDMicro ile ladder diyagramýný çizebilir, devrenizi denemek için gerçek zamanlý
+simülasyon yapabilirsiniz. Programýnýzýn çalýþtýðýndan eminseniz programdaki
+giriþ ve çýkýþlara mikrokontrolörün bacaklarýný atarsýnýz. Ýþlemci bacaklarý
+belli olduktan sonra programýnýzý derleyebilirsiniz. Derleme sonucunda oluþan
+dosya .hex dosyasýdýr. Bu dosyayý PIC/AVR programlayýcý ile iþlemcinize kaydedersiniz.
+PIC/AVR ile uðraþanlar konuya yabancý deðildir.
+
+
+LDMicro ticari PLC programlarý gibi tasarlanmýþtýr. Bazý eksiklikler vardýr.
+Kitapçýðý dikkatlice okumanýzý tavsiye ederim. Kullaným esnasýnda PLC ve
+PIC/AVR hakkýnda temel bilgilere sahip olduðunuz düþünülmüþtür.
+
+DÝÐER AMAÇLAR
+==================
+
+ANSI C kodunu oluþturmak mümkündür. C derleyicisi olan herhangi bir
+iþlemci için bu özellikten faydalanabilirsiniz. Ancak çalýþtýrmak için
+gerekli dosyalarý siz saðlamalýsýnýz. Yani, LDMicro sadece PlcCycle()
+isimli fonksiyonu üretir. Her döngüde PlcCycle fonksiyonunu çaðýrmak, ve
+PlcCycle() fonksiyonunun çaðýrdýðý dijital giriþi yazma/okuma vs gibi
+G/Ç fonksiyonlarý sizin yapmanýz gereken iþlemlerdir.
+Oluþturulan kodu incelerseniz faydalý olur.
+
+KOMUT SATIRI SEÇENEKLERÝ
+========================
+
+Normal þartlarda ldmicro.exe komut satýrýndan seçenek almadan çalýþýr.
+LDMicro'ya komut satýrýndan dosya ismi verebilirsiniz. Örneðin;komut
+satýrýndan 'ldmicro.exe asd.ld' yazarsanýz bu dosya açýlmaya çalýþýrlýr.
+Dosya varsa açýlýr. Yoksa hata mesajý alýrsýnýz. Ýsterseniz .ld uzantýsýný
+ldmicro.exe ile iliþkilendirirseniz .ld uzantýlý bir dosyayý çift týklattýðýnýzda
+bu dosya otomatik olarak açýlýr. Bkz. Klasör Seçenekleri (Windows).
+
+`ldmicro.exe /c src.ld dest.hex', þeklinde kullanýlýrsa src.ld derlenir
+ve hazýrlanan derleme dest.hex dosyasýna kaydedilir. Ýþlem bitince LDMicro kapanýr.
+Oluþabilecek tüm mesajlar konsoldan görünür.
+
+TEMEL BÝLGÝLER
+==============
+
+LDMicro açýldýðýnda boþ bir program ile baþlar. Varolan bir dosya ile baþlatýrsanýz
+bu program açýlýr. LDMicro kendi dosya biçimini kullandýðýndan diðer dosya
+biçimlerinden dosyalarý açamazsýnýz.
+
+Boþ bir dosya ile baþlarsanýz ekranda bir tane boþ satýr görürsünüz. Bu satýra
+komutlarý ekleyebilir, satýr sayýsýný artýrabilirsiniz. Satýrlara Rung denilir.
+Örneðin; Komutlar->Kontak Ekle diyerek bir kontak ekleyebilirsiniz. Bu kontaða
+'Xnew' ismi verilir. 'X' bu kontaðýn iþlemcinin bacaðýna denk geldiðini gösterir.
+Bu kontaða derlemeden önce isim vermeli ve mikrokontrolörün bir bacaðý ile
+eþleþtirmelisiniz. Eþleþtirme iþlemi içinde önce iþlemciyi seçmelisiniz.
+Elemanlarýn ilk harfi o elemanýn ne olduðu ile ilgilidir. Örnekler:
+
+ * Xname -- mikrokontrolördeki bir giriþ bacaðý
+ * Yname -- mikrokontrolördeki bir çýkýþ bacaðý
+ * Rname -- `dahili röle': hafýzada bir bit.
+ * Tname -- zamanlayýcý; turn-on, turn-off yada retentive
+ * Cname -- sayýcý, yukarý yada aþaðý sayýcý
+ * Aname -- A/D çeviriciden okunan bir tamsayý deðer
+ * name -- genel deðiþken (tamsayý)
+
+Ýstediðiniz ismi seçebilirsiniz. Seçilen bir isim nerede kullanýlýrsa
+kullanýlsýn ayný yere denk gelir. Örnekler; bir satýrda Xasd kullandýðýnýzda
+bir baþka satýrda Xasd kullanýrsanýz ayný deðere sahiptirler.
+Bazen bu mecburi olarak kullanýlmaktadýr. Ancak bazý durumlarda hatalý olabilir.
+Mesela bir (TON) Turn-On Gecikmeye Tgec ismini verdikten sonra bir (TOF)
+Turn_Off gecikme devresine de Tgec ismini verirseniz hata yapmýþ olursunuz.
+Dikkat ederseniz yaptýðýnýz bir mantýk hatasýdýr. Her gecikme devresi kendi
+hafýzasýna sahip olmalýdýr. Ama Tgec (TON) turn-on gecikme devresini sýfýrlamak
+için kullanýlan RES komutunda Tgec ismini kullanmak gerekmektedir.
+
+Deðiþken isimleri harfleri, sayýlarý, alt çizgileri ihtiva edebilir.
+(_). Deðiþken isimleri sayý ile baþlamamalýdýr. Deðiþken isimleri büyük-küçük harf duyarlýdýr.
+Örneðin; TGec ve Tgec ayný zamanlayýcýlar deðildir.
+
+
+Genel deðiþkenlerle ilgili komutlar (MOV, ADD, EQU vs) herhangi bir
+isimdeki deðiþkenlerle çalýþýr. Bunun anlamý bu komutlar zamanlayýcýlar
+ve sayýcýlarla çalýþýr. Zaman zaman bu faydalý olabilir. Örneðin; bir
+zamanlayýcýnýn deðeri ile ilgili bir karþýlaþtýrma yapabilirsiniz.
+
+Deðiþkenler hr zaman için 16 bit tamsayýdýr. -32768 ile 32767 arasýnda
+bir deðere sahip olabilirler. Her zaman için iþaretlidir. (+ ve - deðere
+sahip olabilirler) Onluk sayý sisteminde sayý kullanabilirsiniz. Týrnak
+arasýna koyarak ('A', 'z' gibi) ASCII karakterler kullanabilirsiniz.
+
+Ekranýn alt tarafýndaki kýsýmda kullanýlan tüm elemanlarýn bir listesi görünür.
+Bu liste program tarafýndan otomatik olarak oluþturulur ve kendiliðinden
+güncelleþtirilir. Sadece 'Xname', 'Yname', 'Aname' elemanlarý için
+mikrokontrolörün bacak numaralarý belirtilmelidir. Önce Ayarlar->Ýþlemci Seçimi
+menüsünden iþlemciyi seçiniz. Daha sonra G/Ç uçlarýný çift týklatarak açýlan
+pencereden seçiminizi yapýnýz.
+
+Komut ekleyerek veya çýkararak programýnýzý deðiþtirebilirsiniz. Programdaki
+kursör eleman eklenecek yeri veya hakkýnda iþlem yapýlacak elemaný göstermek
+amacýyla yanýp söner. Elemanlar arasýnda tuþu ile gezinebilirsiniz. Yada
+elemaný fare ile týklatarak iþlem yapýlacak elemaný seçebilirsiniz. Kursör elemanýn
+solunda, saðýnda, altýnda ve üstünde olabilir. Solunda ve saðýnda olduðunda
+ekleme yaptýðýnýzda eklenen eleman o tarafa eklenir. Üstünde ve altýnda iken
+eleman eklerseniz eklenen eleman seçili elemana paralel olarak eklenir.
+Bazý iþlemleri yapamazsýnýz. Örneðin bir bobinin saðýna eleman ekleyemezsiniz.
+LDMicro buna izin vermeyecektir.
+
+Program boþ bir satýrla baþlar. Kendiniz alta ve üste satýr ekleyerek dilediðiniz
+gibi diyagramýnýzý oluþturabilirsiniz. Yukarýda bahsedildiði gibi alt devreler
+oluþturabilirsiniz.
+
+Programýnýz yazdýðýnýzda simülasyon yapabilir, .hex dosyasýný oluþturabilirsiniz.
+
+SÝMÜLASYON
+==========
+
+Simülasyon moduna geçmek için Simülasyon->Simülasyon modu menüsünü týklatabilir,
+yada tuþ kombinasyonuna basabilirsiniz. Simülasyon modunda program
+farklý bir görüntü alýr. Kursör görünmez olur. Enerji alan yerler ve elemanlar
+parlak kýrmýzý, enerji almayan yerler ve elemanlar gri görünür. Boþluk tuþuna
+basarak bir çevrim ilerleyebilir yada menüden Simülasyon->Gerçek Zamanlý Simülasyonu Baþlat
+diyerek (veya ) devamlý bir çevrim baþlatabilirsiniz. Ladder diyagramýnýn
+çalýþmasýna göre gerçek zamanlý olarak elemanlar ve yollar program tarafýndan deðiþtirilir.
+
+Giriþ elemanlarýnýn durumunu çift týklatarak deðiþtirebilirsiniz. Mesela, 'Xname'
+kontaðýný çift týklatýranýz açýktan kapalýya veya kapalýdan açýða geçiþ yapar.
+
+DERLEME
+=======
+
+Ladder diyagramýnýn yapýlmasýndaki amaç iþlemciye yüklenecek .hex dosyasýnýn
+oluþturulmasýdýr. Buna 'derleme' denir. Derlemeden önce þu aþamalar tamamlanmalýdýr:
+ 1- Ýþlemci seçilmelidir. Ayarlar->Ýþlemci Seçimi menüsünden yapýlýr.
+ 2- G/Ç uçlarýnýn mikrokontrolördeki hangi bacaklara baðlanacaðý seçilmelidir.
+ Elemanýn üzerine çift týklanýr ve çýkan listeden seçim yapýlýr.
+ 3- Çevrim süresi tanýmlanmalýdýr. Ayarlar->Ýþlemci Ayarlarý menüsünden yapýlýr.
+ Bu süre iþlemcinin çalýþtýðý frekansa baðlýdýr. Çoðu uygulamalar için 10ms
+ uygun bir seçimdir. Ayný yerden kristal frekansýný ayarlamayý unutmayýnýz.
+
+Artýk kodu üretebilirsiniz. Derle->Derle yada Derle->Farklý Derle seçeneklerinden
+birini kullanacaksýnýz. Aradaki fark Kaydet ve Farklý Kaydet ile aynýdýr. Sonuçta
+Intel IHEX dosyanýz programýnýzda hata yoksa üretilecektir. Programýnýzda hata varsa
+uyarý alýrsýnýz.
+
+Progamlayýcýnýz ile bu dosyayý iþlemcinize yüklemelisiniz. Buradaki en önemli nokta
+iþlemcinin konfigürasyon bitlerinin ayarlanmasýdýr. PIC16 iþlemciler için gerekli
+ayar bilgileri hex dosyasýna kaydedildiðinden programlayýcýnýz bu ayarlarý algýlayacaktýr.
+Ancak AVR iþlemciler için gerekli ayarlarý siz yapmalýsýnýz.
+
+KOMUTLAR ve ELEMANLAR
+=====================
+
+> KONTAK, NORMALDE AÇIK Xname Rname Yname
+ ----] [---- ----] [---- ----] [----
+
+ Normalde açýk bir anahtar gibi davranýr. Komutu kontrol eden sinyal 0 ise
+ çýkýþýndaki sinyalde 0 olur. Eðer kontrol eden sinyal 1 olursa çýkýþý da 1
+ olur ve çýkýþa baðlý bobin aktif olur. Bu kontak iþlemci bacaðýndan alýnan
+ bir giriþ, çýkýþ bacaðý yada dahili bir röle olabilir.
+
+
+> KONTAK, NORMALDE KAPALI Xname Rname Yname
+ ----]/[---- ----]/[---- ----]/[----
+
+ Normalde kapalý bir anahtar gibi davranýr. Komutun kontrol eden sinyal 0 ise
+ çýkýþý 1 olur. Eðer kontrol eden sinyal 1 olursa çýkýþý 0 olur ve çýkýþa
+ baðlý elemanlar pasif olur. Normalde çýkýþa gerilim verilir, ancak bu kontaðý
+ kontrol eden sinyal 1 olursa kontaðýn çýkýþýnda gerilim olmaz. Bu kontak
+ iþlemci bacaðýndan alýnan bir giriþ, çýkýþ bacaðý yada dahili bir röle olabilir
+
+
+> BOBÝN, NORMAL Rname Yname
+ ----( )---- ----( )----
+
+ Elemana giren sinyal 0 ise dahili röle yada çýkýþ bacaðý 0 yapýlýr.
+ Elemana giren sinyal 1 ise dahili röle yada çýkýþ bacaðý 1 yapýlýr.
+ Bobine giriþ deðiþkeni atamak mantýksýzdýr. Bu eleman bir satýrda
+ saðdaki en son eleman olmalýdýr.
+
+
+> BOBÝN, TERSLENMÝÞ Rname Yname
+ ----(/)---- ----(/)----
+
+ Elemana giren sinyal 0 ise dahili röle yada çýkýþ bacaðý 1 yapýlýr.
+ Elemana giren sinyal 1 ise dahili röle yada çýkýþ bacaðý 0 yapýlýr.
+ Bobine giriþ deðiþkeni atamak mantýksýzdýr. Bu eleman bir satýrda
+ saðdaki en son eleman olmalýdýr. Normal bobinin tersi çalýþýr.
+
+
+> BOBÝN, SET Rname Yname
+ ----(S)---- ----(S)----
+
+ Elemana giren sinyal 1 ise dahili röle yada çýkýþ bacaðý 1 yapýlýr.
+ Diðer durumlarda bu bobinin durumunda bir deðiþiklik olmaz. Bu komut
+ bobinin durumunu sadece 0'dan 1'e çevirir. Bu nedenle çoðunlukla
+ BOBÝN-RESET ile beraber çalýþýr. Bu eleman bir satýrda saðdaki en
+ son eleman olmalýdýr.
+
+
+> BOBÝN, RESET Rname Yname
+ ----(R)---- ----(R)----
+
+ Elemana giren sinyal 1 ise dahili röle yada çýkýþ bacaðý 0 yapýlýr.
+ Diðer durumlarda bu bobinin durumunda bir deðiþiklik olmaz. Bu komut
+ bobinin durumunu sadece 1'dEn 0'a çevirir. Bu nedenle çoðunlukla
+ BOBÝN-SET ile beraber çalýþýr. Bu eleman bir satýrda saðdaki en
+ son eleman olmalýdýr.
+
+
+> TURN-ON GECÝKME Tdon
+ -[TON 1.000 s]-
+
+ Bir zamanlayýcýdýr. Giriþindeki sinyal 0'dan 1'e geçerse ayarlanan
+ süre kadar sürede çýkýþ 0 olarak kalýr, süre bitince çýkýþý 1 olur.
+ Giriþindeki sinyal 1'den 0'a geçerse çýkýþ hemen 0 olur.
+ Giriþi 0 olduðu zaman zamanlayýcý sýfýrlanýr. Ayrýca; ayarlanan süre
+ boyunca giriþ 1 olarak kalmalýdýr.
+
+ Zamanlayýcý 0'dan baþlayarak her çevrim süresinde 1 artarak sayar.
+ Sayý ayarlanan süreye eþit yada büyükse çýkýþ 1 olur. Zamanlayýcý
+ deðiþkeni üzerinde iþlem yapmak mümkündür. (Örneðin MOV komutu ile)
+
+
+> TURN-OFF GECÝKME Tdoff
+ -[TOF 1.000 s]-
+
+ Bir zamanlayýcýdýr. Giriþindeki sinyal 1'den 0'a geçerse ayarlanan
+ süre kadar sürede çýkýþ 1 olarak kalýr, süre bitince çýkýþý 0 olur.
+ Giriþindeki sinyal 0'dan 1'e geçerse çýkýþ hemen 1 olur.
+ Giriþi 0'dan 1'e geçtiðinde zamanlayýcý sýfýrlanýr. Ayrýca; ayarlanan
+ süre boyunca giriþ 0 olarak kalmalýdýr.
+
+ Zamanlayýcý 0'dan baþlayarak her çevrim süresinde 1 artarak sayar.
+ Sayý ayarlanan süreye eþit yada büyükse çýkýþ 1 olur. Zamanlayýcý
+ deðiþkeni üzerinde iþlem yapmak mümkündür. (Örneðin MOV komutu ile)
+
+
+> SÜRE SAYAN TURN-ON GECÝKME Trto
+ -[RTO 1.000 s]-
+
+ Bu zamanlayýcý giriþindeki sinyalin ne kadar süre ile 1 olduðunu
+ ölçer. Ayaralanan süre boyunca giriþ 1 ise çýkýþý 1 olur. Aksi halde
+ çýkýþý 0 olur. Ayarlanan süre devamlý olmasý gerekmez. Örneðin; süre
+ 1 saniyeye ayarlanmýþsa ve giriþ önce 0.6 sn 1 olmuþsa, sonra 2.0 sn
+ boyunca 0 olmuþsa daha sonra 0.4 sn boyunca giriþ tekrar 1 olursa
+ 0.6 + 0.4 = 1sn olduðundan çýkýþ 1 olur. Çýkýþ 1 olduktan sonra
+ giriþ 0 olsa dahi çýkýþ 0'a dönmez. Bu nedenle zamanlayýcý RES reset
+ komutu ile resetlenmelidir.
+
+ Zamanlayýcý 0'dan baþlayarak her çevrim süresinde 1 artarak sayar.
+ Sayý ayarlanan süreye eþit yada büyükse çýkýþ 1 olur. Zamanlayýcý
+ deðiþkeni üzerinde iþlem yapmak mümkündür. (Örneðin MOV komutu ile)
+
+
+> RESET (SAYICI SIFIRLAMASI) Trto Citems
+ ----{RES}---- ----{RES}----
+
+ Bu komut bir zamanlayýcý veya sayýcýyý sýfýrlar. TON ve TOF zamanlayýcý
+ komutlarý kendiliðinden sýfýrlandýðýndan bu komuta ihtiyaç duymazlar.
+ RTO zamanlayýcýsý ve CTU/CTD sayýcýlarý kendiliðinden sýfýrlanmadýðýndan
+ sýfýrlanmalarý için kullanýcý tarafýndan bu komutile sýfýrlanmasý
+ gerekir. Bu komutun giriþi 1 olduðunda sayýcý/zamanlayýcý sýfýrlanýr.
+ Bu komut bir satýrýn saðýndaki son komut olmalýdýr.
+
+
+> YÜKSELEN KENAR _
+ --[OSR_/ ]--
+
+ Bu komutun çýkýþý normalde 0'dýr. Bu komutun çýkýþýnýn 1 olabilmesi
+ için bir önceki çevrimde giriþinin 0 þimdiki çevrimde giriþinin 1
+ olmasý gerekir. Komutun çýkýþý bir çevrimlik bir pals üretir.
+ Bu komut bir sinyalin yükselen kenarýnda bir tetikleme gereken
+ uygulamalarda faydalýdýr.
+
+
+> DÜÞEN KENAR _
+ --[OSF \_]--
+
+ Bu komutun çýkýþý normalde 0'dýr. Bu komutun çýkýþýnýn 1 olabilmesi
+ için bir önceki çevrimde giriþinin 1 þimdiki çevrimde giriþinin 0
+ olmasý gerekir. Komutun çýkýþý bir çevrimlik bir pals üretir.
+ Bu komut bir sinyalin düþen kenarýnda bir tetikleme gereken
+ uygulamalarda faydalýdýr.
+
+
+> KISA DEVRE, AÇIK DEVRE
+ ----+----+---- ----+ +----
+
+ Kýsa devrenin çýkýþý her zaman giriþinin aynýsýdýr.
+ Açýk devrenin çýkýþý her zaman 0'dýr. Bildiðimiz açýk/kýsa devrenin
+ aynýsýdýr. Genellikle hata aramada kullanýlýrlar.
+
+> ANA KONTROL RÖLESÝ
+ -{MASTER RLY}-
+
+ Normalde her satýrýn ilk giriþi 1'dir. Birden fazla satýrýn tek bir þart ile
+ kontrol edilmesi gerektiðinde paralel baðlantý yapmak gerekir. Bu ise zordur.
+ Bu iþlemi kolayca yapabilmek için ana kontrol rölesini kullanabiliriz.
+ Ana kontrol rölesi eklendiðinde kendisinden sonraki satýrlar bu röleye baðlý
+ hale gelir. Böylece; birden fazla satýr tek bir þart ile kontrolü saðlanýr.
+ Bir ana kontrol rölesi kendisinden sonra gelen ikinci bir ana kontrol
+ rölesine kadar devam eder. Diðer bir deyiþle birinci ana kontrol rölesi
+ baþlangýcý ikincisi ise bitiþi temsil eder. Ana kontrol rölesi kullandýktan
+ sonra iþlevini bitirmek için ikinci bir ana kontrol rölesi eklemelisiniz.
+
+> MOVE {destvar := } {Tret := }
+ -{ 123 MOV}- -{ srcvar MOV}-
+
+ Giriþi 1 olduðunda verilen sabit sayýyý (123 gibi) yada verilen deðiþkenin
+ içeriðini (srcvar) belirtilen deðiþkene (destvar) atar. Giriþ 0 ise herhangi
+ bir iþlem olmaz. Bu komut ile zamanlayýcý ve sayýcýlar da dahil olmak üzere
+ tüm deðiþkenlere deðer atayabilirsiniz. Örneðin Tsay zamanlayýcýsýna MOVE ile
+ 0 atamak ile RES ile sýfýrlamak ayný sonucu doðurur. Bu komut bir satýrýn
+ saðýndaki en son komut olmalýdýr.
+
+> MATEMATÝK ÝÞLEMLER {ADD kay :=} {SUB Ccnt :=}
+ -{ 'a' + 10 }- -{ Ccnt - 10 }-
+
+> {MUL dest :=} {DIV dv := }
+ -{ var * -990 }- -{ dv / -10000}-
+
+ Bu komutun giriþi doðru ise belirtilen hedef deðiþkenine verilen matematik
+ iþlemin sonucunu kaydeder. Ýþlenen bilgi zamanlayýcý ve sayýcýlar dahil
+ olmak üzere deðiþkenler yada sabit sayýlar olabilir. Ýþlenen bilgi 16 bit
+ iþaretli sayýdýr. Her çevrimde iþlemin yeniden yapýldýðý unutulmamalýdýr.
+ Örneðin artýrma yada eksiltme yapýyorsanýz yükselen yada düþen kenar
+ kullanmanýz gerekebilir. Bölme (DIV) virgülden sonrasýný keser. Örneðin;
+ 8 / 3 = 2 olur. Bu komut bir satýrýn saðýndaki en son komut olmalýdýr.
+
+
+> KARÞILAÞTIRMA [var ==] [var >] [1 >=]
+ -[ var2 ]- -[ 1 ]- -[ Ton]-
+
+> [var /=] [-4 < ] [1 <=]
+ -[ var2 ]- -[ vartwo]- -[ Cup]-
+
+ Deðiþik karþýlaþtýrma komutlarý vardýr. Bu komutlarýn giriþi doðru (1)
+ ve verilen þart da doðru ise çýkýþlarý 1 olur.
+
+
+> SAYICI Cname Cname
+ --[CTU >=5]-- --[CTD >=5]--
+
+ Sayýcýlar giriþlerinin 0'dan 1'e her geçiþinde yani yükselen kenarýnda
+ deðerlerini 1 artýrýr (CTU) yada eksiltirler (CTD). Verilen þart doðru ise
+ çýkýþlarý aktif (1) olur. CTU ve CTD sayýcýlarýna ayný ismi erebilirsiniz.
+ Böylece ayný sayýcýyý artýrmýþ yada eksiltmiþ olursunuz. RES komutu sayýcýlarý
+ sýfýrlar. Sayýcýlar ile genel deðiþkenlerle kullandýðýnýz komutlarý kullanabilirsiniz.
+
+
+> DAÝRESEL SAYICI Cname
+ --{CTC 0:7}--
+
+ Normal yukarý sayýcýdan farký belirtilen limite ulaþýnca sayýcý tekrar 0'dan baþlar
+ Örneðin sayýcý 0, 1, 2, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 2,.... þeklinde
+ sayabilir. Yani bir dizi sayýcý olarak düþünülebilir. CTC sayýcýlar giriþlerinin
+ yükselen kenarýnda deðer deðiþtirirler. Bu komut bir satýrýn saðýndaki
+ en son komut olmalýdýr.
+
+
+> SHIFT REGISTER {SHIFT REG }
+ -{ reg0..3 }-
+
+ Bir dizi deðiþken ile beraber çalýþýr. Ýsim olarak reg verdiðinizi ve aþama
+ sayýsýný 3 olarak tanýmladýysanýz reg0, reg1, reg2 deðikenleri ile çalýþýrsýnýz.
+ Kaydedicinin giriþi reg0 olur. Giriþin her yükselen kenarýnda deðerler kaydedicide
+ bir saða kayar. Mesela; `reg2 := reg1'. and `reg1 := reg0'. `reg0' deðiþmez.
+ Geniþ bir kaydedici hafýzada çok yer kaplar.
+ Bu komut bir satýrýn saðýndaki en son komut olmalýdýr.
+
+
+> DEÐER TABLOSU {dest := }
+ -{ LUT[i] }-
+
+ Deðer tablosu sýralanmýþ n adet deðer içeren bir tablodur. Giriþi doðru olduðunda
+ `dest' tamsayý deðiþkeni `i' tamsayý deðiþkenine karþýlýk gelen deðeri alýr. Sýra
+ 0'dan baþlar. bu nedenle `i' 0 ile (n-1) arasýnda olabilir. `i' bu deðerler
+ arasýnda deðilse komutun ne yapacaðý tanýmlý deðildir.
+ Bu komut bir satýrýn saðýndaki en son komut olmalýdýr.
+
+
+> PIECEWISE LINEAR TABLE {yvar := }
+ -{ PWL[xvar] }-
+
+ Bir matris tablo olarak düþünülebilir. Bir deðere baðlý olarak deðerin önceden
+ belirlenen bir baþka deðer ile deðiþtirilmesi içi oluþturulan bir tablodur.
+ Bu bir eðri oluþturmak, sensörden alýnan deðere göre çýkýþta baþka bir eðri
+ oluþturmak gibi amaçlar için kullanýlabilir.
+
+ Farzedelimki x tamsayý giriþ deðerini y tamsayý çýkýþ deðerine yaklaþtýrmak
+ istiyoruz. Deðerlerin belirli noktalarda olduðunu biliyoruz. Örneðin;
+
+ f(0) = 2
+ f(5) = 10
+ f(10) = 50
+ f(100) = 100
+
+ Bu þu noktalarýn eðride olduðunu gösterir:
+
+ (x0, y0) = ( 0, 2)
+ (x1, y1) = ( 5, 10)
+ (x2, y2) = ( 10, 50)
+ (x3, y3) = (100, 100)
+
+ Dört deðeri parçalý lineer tabloya gireriz. Komut, xvar'ýn deðerine bakarak
+ yvar'a deðer verir. Örneðin, yukarýdaki örneðe bakarak, xvar = 10 ise
+ yvar = 50 olur.
+
+ Tabloya kayýtlý iki deðerin arasýnda bir deðer verirseniz verilen deðer de
+ alýnmasý gereken iki deðerin arasýnda uygun gelen yerde bir deðer olur.
+ Mesela; xvar=55 yazarsanýz yvar=75 olur. (Tablodaki deðerler (10,50) ve
+ (100,100) olduðuna göre). 55, 10 ve 100 deðerlerinin ortasýndadýr. Bu
+ nedenle 55 ve 75 deðerlerinin ortasý olan 75 deðeri alýnýr.
+
+ Deðerler x koordinatýnda artan deðerler olarak yazýlmalýdýr. 16 bit tamsayý
+ kullanan bazý deðerler için arama tablosu üzerinde matematik iþlemler
+ gerçekleþmeyebilir. Bu durumda LDMicro sizi uyaracaktýr. Örneðin aþaðýdaki
+ tablo bir hata oluþturacaktýr:
+
+ (x0, y0) = ( 0, 0)
+ (x1, y1) = (300, 300)
+
+ Bu tip hatalarý noktalar arsýnda ara deðerler oluþturarak giderebilirsiniz.
+ Örneðin aþaðýdaki tablo yukarýdakinin aynýsý olmasýna raðmen hata
+ oluþturmayacaktýr.
+
+ (x0, y0) = ( 0, 0)
+ (x1, y1) = (150, 150)
+ (x2, y2) = (300, 300)
+
+ Genelde 5 yada 6 noktadan daha fazla deðer kullanmak gerekmeyecektir.
+ Daha fazla nokta demek daha fazla kod ve daha yavaþ çalýþma demektir.
+ En fazla 10 nokta oluþturabilirsiniz. xvar deðiþkenine x koordinatýnda
+ tablonun en yüksek deðerinden daha büyük bir deðer girmenin ve en düþük
+ deðerinden daha küçük bir deðer girmenin sonucu tanýmlý deðildir.
+ Bu komut bir satýrýn saðýndaki en son komut olmalýdýr.
+
+> A/D ÇEVÝRÝCÝDEN OKUMA Aname
+ --{READ ADC}--
+
+ LDmicro A/D çeviriciden deðer okumak için gerekli kodlarý desteklediði
+ iþlemciler için oluþturabilir. Komutun giriþi 1 olduðunda A/D çeviriciden
+ deðer okunur ve okunan deðer `Aname' deðiþkenine aktarýlýr. Bu deðiþken
+ üzerinde genel deðiþkenlerle kullanýlabilen iþlemler kullanýlabilir.
+ (büyük, küçük, büyük yada eþit gibi). Bu deðiþkene iþlemcinin bacaklarýndan
+ uygun biri tanýmlanmalýdýr. Komutun giriþi 0 ise `Aname'deðiþkeninde bir
+ deðiþiklik olmaz.
+
+ Þu an desteklenen iþlemciler için; 0 Volt için ADC'den okunan deðer 0,
+ Vdd (besleme gerilimi) deðerine eþit gerilim deðeri için ADC'den okunan deðer
+ 1023 olmaktadýr. AVR kullanýyorsanýz AREF ucunu Vdd besleme gerilimine
+ baðlayýnýz.
+
+ Aritmetik iþlemler ADC deðiþkeni için kullanýlabilir. Ayrýca bacak tanýmlarken
+ ADC olmayan bacaklarýn tanýmlanmasýný LDMicro engelleyecektir.
+ Bu komut bir satýrýn saðýndaki en son komut olmalýdýr.
+
+ > PWM PALS GENÝÞLÝÐÝ AYARI duty_cycle
+ -{PWM 32.8 kHz}-
+
+ LDmicro desteklediði mikrokontrolörler için gerekli PWM kodlarýný üretebilir.
+ Bu komutun giriþi doðru (1) olduðunda PWM sinyalinin pals geniþliði duty_cycle
+ deðiþkeninin deðerine ayarlanýr. Bu deðer 0 ile 100 arasýnda deðiþir. Pals
+ geniþliði yüzde olarak ayarlanýr. Bir periyot 100 birim kabul edilirse bu
+ geniþliðin yüzde kaçýnýn palsi oluþturacaðý ayarlanýr. 0 periyodun tümü sýfýr
+ 100 ise periyodun tamamý 1 olsun anlamýna gelir. 10 deðeri palsin %10'u 1 geri
+ kalan %90'ý sýfýr olsun anlamýna gelir.
+
+ PWM frekansýný ayarlayabilirsiniz. Verilen deðer Hz olarak verilir.
+ Verdiðiniz frekans kesinlikle ayarlanabilir olmalýdýr. LDMicro verdiðiniz deðeri
+ olabilecek en yakýn deðerle deðiþtirir. Yüksek hýzlarda doðruluk azalýr.
+
+ Bu komut bir satýrýn saðýndaki en son komut olmalýdýr.
+ Periyodun süresinin ölçülebilmesi için iþlemcinin zamanlayýcýlarýnýn bir tanesi
+ kullanýlýr. Bu nedenle PWM en az iki tane zamanlayýcýsý olan iþlemcilerde kullanýlýr.
+ PWM PIC16 iþlemcilerde CCP2'yi, AVR'lerde ise OC2'yi kullanýr.
+
+
+> EEPROMDA SAKLA saved_var
+ --{PERSIST}--
+
+ Bu komut ile belirtilen deðiþkenin EEPROM'da saklanmasý gereken bir deðiþken olduðunu
+ belirmiþ olursunuz. Komutun giriþi doðru ise belirtilen deðiþkenin içeriði EEPROM'a
+ kaydedilir. Enerji kesildiðinde kaybolmamasý istenen deðerler için bu komut kullanýlýr.
+ Deðiþkenin içeriði gerilim geldiðinde tekrar EEPROM'dan yüklenir. Ayrýca;
+ deðiþkenin içeriði her deðiþtiðinde yeni deðer tekrar EEPROM'a kaydedilir.
+ Ayrýca bir iþlem yapýlmasý gerekmez.
+ Bu komut bir satýrýn saðýndaki en son komut olmalýdýr.
+
+************************
+> UART (SERÝ BÝLGÝ) AL var
+ --{UART RECV}--
+
+ LDmicro belirli iþlemciler için gerekli UART kodlarýný üretebilir. AVR iþlemcilerde
+ sadece UART1 (UART0) deðil) desteklenmektedir. Ýletiþim hýzý (baudrate) ayarlarýný
+ Ayarlar->Ýþlemci Ayarlarý menüsünden yapmalýsýnýz. Hýz kristal frekansýna baðlý olup,
+ bazý hýzlar desteklenmeyebilir. Bu durumda LDMicro sizi uyaracaktýr.
+
+ Bu komutun giriþi yanlýþsa herhangi bir iþlem yapýlmaz. Doðru ise UART'dan 1 karakter
+ alýnmaya çalýþýlýr. Okuma yapýlamaz ise komutun çýkýþý yanlýþ (0) olur. Karakter
+ okunursa okunan karakter `var' deðiþkeninde saklanýr ve komutun çýkýþý doðru (1) olur.
+ Çýkýþýn doðru olmasý sadece bir PLC çevrimi sürer.
+
+
+> UART (SERÝ BÝLGÝ) GÖNDER var
+ --{UART SEND}--
+
+ LDmicro belirli iþlemciler için gerekli UART kodlarýný üretebilir. AVR iþlemcilerde
+ sadece UART1 (UART0) deðil) desteklenmektedir. Ýletiþim hýzý (baudrate) ayarlarýný
+ Ayarlar->Ýþlemci Ayarlarý menüsünden yapmalýsýnýz. Hýz kristal frekansýna baðlý olup,
+ bazý hýzlar desteklenmeyebilir. Bu durumda LDMicro sizi uyaracaktýr.
+
+ Bu komutun giriþi yanlýþsa herhangi bir iþlem yapýlmaz. Doðru ise UART'dan 1 karakter
+ gönderilir. Gönderilecek karakter gönderme iþleminden önce `var' deðiþkeninde saklý
+ olmalýdýr. Komutun çýkýþý UART meþgulse (bir karakterin gönderildiði sürece)
+ doðru (1) olur. Aksi halde yanlýþ olur.
+ Çýkýþýn doðru olmasý sadece bir PLC çevrimi sürer.
+
+ Karakterin gönderilmesi belirli bir zaman alýr. Bu nedenle baþka bir karakter
+ göndermeden önce önceki karakterin gönderildiðini kontrol ediniz veya gönderme
+ iþlemlerinin arasýna geikme ekleyiniz. Komutun giriþini sadece çýkýþ yanlýþ
+ (UART meþgul deðilse)ise doðru yapýnýz.
+
+ Bu komut yerine biçimlendirilmiþ kelime komutunu (bir sonraki komut) inceleyiniz.
+ Biçimlendirilmiþ kelime komutunun kullanýmý daha kolaydýr. Ýstediðiniz iþlemleri
+ daha rahat gerçekleþtirebilirsiniz.
+
+
+> UART ÜZERÝNDEN BÝÇÝMLENDÝRÝLMÝÞ KELÝME var
+ -{"Pressure: \3\r\n"}-
+
+ LDmicro belirli iþlemciler için gerekli UART kodlarýný üretebilir. AVR iþlemcilerde
+ sadece UART1 (UART0) deðil) desteklenmektedir. Ýletiþim hýzý (baudrate) ayarlarýný
+ Ayarlar->Ýþlemci Ayarlarý menüsünden yapmalýsýnýz. Hýz kristal frekansýna baðlý olup,
+ bazý hýzlar desteklenmeyebilir. Bu durumda LDMicro sizi uyaracaktýr.
+
+ Bu komutun giriþi yanlýþtan doðruya geçerse (yükselen kenar) ise seri port üzerinden
+ tüm kelimeyi gönderir. Eðer kelime `\3' özel kodunu içeriyorsa dizi içeriði
+ `var' deðiþkenin içeriði otomatik olarak kelimeye (string) çevrilerek`var'
+ deðiþkeninin içeriði ile deðiþtirilir. Deðiþkenin uzunluðu 3 karakter olacak þekilde
+ deðiþtirilir. Mesela; `var' deðiþkeninin içeriði 35 ise kelime 35 rakamýnýn baþýna bir
+ adet boþul eklenerek `Pressure: 35\r\n' haline getirilir. Veya `var'deðiþkeninin
+ içeriði 1453 ise yapýlacak iþlem belli olmaz. Bu durumda `\4' kullanmak gerekebilir.
+
+ Deðiþken negatif bir sayý olabilecekse `\-3d' (veya `\-4d') gibi uygun bir deðer
+ kullanmalýsýnýz. Bu durumda LDMicro negatif sayýlarýn önüne eksi iþareti, pozitif sayýlarýn
+ önüne ise bir boþluk karakteri yerleþtirecektir.
+
+ Ayný anda birkaç iþlem tanýmlanýrsa, yada UART ile ilgili iþlemler birbirine
+ karýþýk hale getirilirse programýn davranýþý belirli olmayacaktýr. Bu nedenle
+ dikkatli olmalýsýnýz.
+
+ Kullanýlabilecek özel karakterler (escape kodlarý) þunlardýr:
+ * \r -- satýr baþýna geç
+ * \n -- yeni satýr
+ * \f -- kaðýdý ilerlet (formfeed)
+ * \b -- bir karakter geri gel (backspace)
+ * \xAB -- ASCII karakter kodu 0xAB (hex)
+
+ Bu komutun çýkýþý bilgi gönderiyorken doðru diðer durumlarda yanlýþ olur.
+ Bu komut program hafýzasýnda çok yer kaplar.
+
+
+MATEMATÝKSEL ÝÞLEMLER ÝLE ÝLGÝLÝ BÝLGÝ
+======================================
+
+Unutmayýn ki, LDMicro 16-bit tamsayý matematik komutlarýna sahiptir.
+Bu iþlemlerde kullanýlan deðerler ve hesaplamanýn sonucu -32768 ile
+32767 arasýnda bir tamsayý olabilir.
+
+Mesela y = (1/x)*1200 formülünü hesaplamaya çalýþalým. x 1 ile 20
+arasýnda bir sayýdýr. Bu durumda y 1200 ile 60 arasýnda olur. Bu sayý
+16-bit bir tamsayý sýnýrlarý içindedir. Ladder diyagramýmýzý yazalým.
+Önce bölelim, sonra çarpma iþlemini yapalým:
+
+ || {DIV temp :=} ||
+ ||---------{ 1 / x }----------||
+ || ||
+ || {MUL y := } ||
+ ||----------{ temp * 1200}----------||
+ || ||
+
+Yada bölmeyi doðrudan yapalým:
+
+ || {DIV y :=} ||
+ ||-----------{ 1200 / x }-----------||
+
+Matematiksel olarak iki iþlem aynýd sonucu vermelidir. Ama birinci iþlem
+yanlýþ sonuç verecektir. (y=0 olur). Bu hata `temp' deðiþkeninin 1'den
+küçük sonuç vermesindendir.Mesela x = 3 iken (1 / x) = 0.333 olur. Ama
+0.333 bir tamsayý deðildir. Bu nedenle sonuç 0 olur. Ýkinci adýmda ise
+y = temp * 1200 = 0 olur. Ýkinci þekilde ise bölen bir tamsayý olduðundan
+sonuç doðru çýkacaktýr.
+
+Ýþlemlerinizde bir sorun varsa dikkatle kontrol ediniz. Ayrýca sonucun
+baþa dönmemesine de dikkat ediniz. Mesela 32767 + 1 = -32768 olur.
+32767 sýnýrý aþýlmýþ olacaktýr.
+
+Hesaplamalarýnýzda mantýksal deðiþimler yaparak doðru sonuçlar elde edebilirsiniz.
+Örneðin; y = 1.8*x ise formülünüzü y = (9/5)*x þeklinde yazýnýz.(1.8 = 9/5)
+y = (9*x)/5 þeklindeki bir kod sonucu daha tutarlý hale getirecektir.
+performing the multiplication first:
+
+ || {MUL temp :=} ||
+ ||---------{ x * 9 }----------||
+ || ||
+ || {DIV y :=} ||
+ ||-----------{ temp / 5 }-----------||
+
+
+KODALAMA ÞEKLÝ
+==============
+
+Programýn saðladýðý kolaylýklardan faydalanýn. Mesela:
+
+ || Xa Ya ||
+ 1 ||-------] [--------------( )-------||
+ || ||
+ || Xb Yb ||
+ ||-------] [------+-------( )-------||
+ || | ||
+ || | Yc ||
+ || +-------( )-------||
+ || ||
+
+yazmak aþaðýdakinden daha kolay olacaktýr.
+
+ || Xa Ya ||
+ 1 ||-------] [--------------( )-------||
+ || ||
+ || ||
+ || ||
+ || ||
+ || Xb Yb ||
+ 2 ||-------] [--------------( )-------||
+ || ||
+ || ||
+ || ||
+ || ||
+ || Xb Yc ||
+ 3 ||-------] [--------------( )-------||
+ || ||
+
+ * * *
+
+Yazdýðýnýz kodlarýn sonuçlarýna dikkat ediniz. Aþaðýdaki satýrlarda
+mantýksýz bir programlama yapýlmýþtýr. Çünkü hem Xa hemde Xb ayný
+anda doðru olabilir.
+
+ || Xa {v := } ||
+ 1 ||-------] [--------{ 12 MOV}--||
+ || ||
+ || Xb {v := } ||
+ ||-------] [--------{ 23 MOV}--||
+ || ||
+ || ||
+ || ||
+ || ||
+ || [v >] Yc ||
+ 2 ||------[ 15]-------------( )-------||
+ || ||
+
+Aþaðýdaki satýrlar yukarda bahsi geçen tarzdadýr. Ancak yapýlan
+iþlem 4-bit binary sayý tamsayýya çevrilmektedir.
+
+ || {v := } ||
+ 3 ||-----------------------------------{ 0 MOV}--||
+ || ||
+ || Xb0 {ADD v :=} ||
+ ||-------] [------------------{ v + 1 }-----------||
+ || ||
+ || Xb1 {ADD v :=} ||
+ ||-------] [------------------{ v + 2 }-----------||
+ || ||
+ || Xb2 {ADD v :=} ||
+ ||-------] [------------------{ v + 4 }-----------||
+ || ||
+ || Xb3 {ADD v :=} ||
+ ||-------] [------------------{ v + 8 }-----------||
+ || ||
+
+
+HATALAR (BUG)
+=============
+
+LDmicro tarafýndan üretilen kodlar çok verimli kodlar deðildir. Yavaþ çalýþan
+ve hafýzada fazla yer kaplayan kodlar olabilirler. Buna raðmen orta büyüklükte
+bir PIC veya AVR küçük bir PLC'nin yaptýðý iþi yapar. Bu nedenle diðer sorunlar
+yer yer gözardý edlebilir.
+
+Deðiþken isimleri çok uzun olmamalýdýr.
+
+Programýnýz yada kullandýðýnýz hafýza seçtiðiniz iþlemcinin sahip olduðundan
+büyükse LDMicro hata vermeyebilir. Dikkat etmezseniz programýnýz hatalý çalýþacaktýr.
+
+Bulduðunuz hatalarý yazara bildiriniz.
+
+Teþekkürler:
+ * Marcelo Solano, Windows 98'deki UI problemini bildirdiði için,
+ * Serge V. Polubarjev, PIC16F628 iþlemcisi seçildiðinde RA3:0'ýn çalýþmadýðý
+ ve nasýl düzelteceðimi bildirdiði için,
+ * Maxim Ibragimov, ATmega16 ve ATmega162 iþlemcileri test ettikleri, problemleri
+ bulduklarý ve bildirdikleri için,
+ * Bill Kishonti, sýfýra bölüm hatasý olduðunda simülasyonun çöktüðünü bildirdikleri
+ için,
+ * Mohamed Tayae, PIC16F628 iþlemcisinde EEPROM'da saklanmasý gereken deðiþkenlerin
+ aslýnda saklanmadýðýný bildirdiði için,
+ * David Rothwell, kullanýcý arayüzündeki birkaç problemi ve "Metin Dosyasý Olarak Kaydet"
+ fonksiyonundaki problemi bildirdiði için.
+
+
+KOPYALAMA VE KULLANIM ÞARTLARI
+==============================
+
+LDMICRO TARAFINDAN ÜRETÝLEN KODU ÝNSAN HAYATI VE ÝNSAN HAYATINI ETKÝLEYEBÝLECEK
+PROJELERDE KULLANMAYINIZ. LDMICRO PROGRAMCISI LDMICRO'NUN KENDÝNDEN VE LDMICRO
+ÝLE ÜRETÝLEN KODDAN KAYNAKLANAN HÝÇBÝR PROBLEM ÝÇÝN SORUMLULUK KABUL ETMEMEKTEDÝR.
+
+Bu program ücretsiz bir program olup, dilediðiniz gibi daðýtabilirsiniz,
+kaynak kodda deðiþiklik yapabilirsiniz. Programýn kullanýmý Free Software Foundation
+tarafýndan yazýlan GNU General Public License (version 3 ve sonrasý)þartlarýna baðlýdýr.
+
+Program faydalý olmasý ümidiyle daðýtýlmýþtýr. Ancak hiçbir garanti verilmemektedir.
+Detaylar için GNU General Public License içeriðine bakýnýz.
+
+Söz konusu sözleþmenin bir kopyasý bu programla beraber gelmiþ olmasý gerekmektedir.
+Gelmediyse adresinde bulabilirsiniz.
+
+
+Jonathan Westhues
+
+Rijswijk -- Dec 2004
+Waterloo ON -- Jun, Jul 2005
+Cambridge MA -- Sep, Dec 2005
+ Feb, Mar 2006
+ Feb 2007
+
+Email: user jwesthues, at host cq.cx
+
+Türkçe Versiyon :
diff --git a/ldmicro/manual.txt b/ldmicro/manual.txt
new file mode 100644
index 0000000..10a483a
--- /dev/null
+++ b/ldmicro/manual.txt
@@ -0,0 +1,969 @@
+
+INTRODUCTION
+============
+
+LDmicro generates native code for certain Microchip PIC16 and Atmel AVR
+microcontrollers. Usually software for these microcontrollers is written
+in a programming language like assembler, C, or BASIC. A program in one
+of these languages comprises a list of statements. These languages are
+powerful and well-suited to the architecture of the processor, which
+internally executes a list of instructions.
+
+PLCs, on the other hand, are often programmed in `ladder logic.' A simple
+program might look like this:
+
+ || ||
+ || Xbutton1 Tdon Rchatter Yred ||
+ 1 ||-------]/[---------[TON 1.000 s]-+-------]/[--------------( )-------||
+ || | ||
+ || Xbutton2 Tdof | ||
+ ||-------]/[---------[TOF 2.000 s]-+ ||
+ || ||
+ || ||
+ || ||
+ || Rchatter Ton Tnew Rchatter ||
+ 2 ||-------]/[---------[TON 1.000 s]----[TOF 1.000 s]---------( )-------||
+ || ||
+ || ||
+ || ||
+ ||------[END]---------------------------------------------------------||
+ || ||
+ || ||
+
+(TON is a turn-on delay; TOF is a turn-off delay. The --] [-- statements
+are inputs, which behave sort of like the contacts on a relay. The
+--( )-- statements are outputs, which behave sort of like the coil of a
+relay. Many good references for ladder logic are available on the Internet
+and elsewhere; details specific to this implementation are given below.)
+
+A number of differences are apparent:
+
+ * The program is presented in graphical format, not as a textual list
+ of statements. Many people will initially find this easier to
+ understand.
+
+ * At the most basic level, programs look like circuit diagrams, with
+ relay contacts (inputs) and coils (outputs). This is intuitive to
+ programmers with knowledge of electric circuit theory.
+
+ * The ladder logic compiler takes care of what gets calculated
+ where. You do not have to write code to determine when the outputs
+ have to get recalculated based on a change in the inputs or a
+ timer event, and you do not have to specify the order in which
+ these calculations must take place; the PLC tools do that for you.
+
+LDmicro compiles ladder logic to PIC16 or AVR code. The following
+processors are supported:
+ * PIC16F877
+ * PIC16F628
+ * PIC16F876 (untested)
+ * PIC16F88 (untested)
+ * PIC16F819 (untested)
+ * PIC16F887 (untested)
+ * PIC16F886 (untested)
+ * ATmega128
+ * ATmega64
+ * ATmega162 (untested)
+ * ATmega32 (untested)
+ * ATmega16 (untested)
+ * ATmega8 (untested)
+
+It would be easy to support more AVR or PIC16 chips, but I do not have
+any way to test them. If you need one in particular then contact me and
+I will see what I can do.
+
+Using LDmicro, you can draw a ladder diagram for your program. You can
+simulate the logic in real time on your PC. Then when you are convinced
+that it is correct you can assign pins on the microcontroller to the
+program inputs and outputs. Once you have assigned the pins, you can
+compile PIC or AVR code for your program. The compiler output is a .hex
+file that you can program into your microcontroller using any PIC/AVR
+programmer.
+
+LDmicro is designed to be somewhat similar to most commercial PLC
+programming systems. There are some exceptions, and a lot of things
+aren't standard in industry anyways. Carefully read the description
+of each instruction, even if it looks familiar. This document assumes
+basic knowledge of ladder logic and of the structure of PLC software
+(the execution cycle: read inputs, compute, write outputs).
+
+
+ADDITIONAL TARGETS
+==================
+
+It is also possible to generate ANSI C code. You could use this with any
+processor for which you have a C compiler, but you are responsible for
+supplying the runtime. That means that LDmicro just generates source
+for a function PlcCycle(). You are responsible for calling PlcCycle
+every cycle time, and you are responsible for implementing all the I/O
+(read/write digital input, etc.) functions that the PlcCycle() calls. See
+the comments in the generated source for more details.
+
+Finally, LDmicro can generate processor-independent bytecode for a
+virtual machine designed to run ladder logic code. I have provided a
+sample implementation of the interpreter/VM, written in fairly portable
+C. This target will work for just about any platform, as long as you
+can supply your own VM. This might be useful for applications where you
+wish to use ladder logic as a `scripting language' to customize a larger
+program. See the comments in the sample interpreter for details.
+
+
+COMMAND LINE OPTIONS
+====================
+
+ldmicro.exe is typically run with no command line options. That means
+that you can just make a shortcut to the program, or save it to your
+desktop and double-click the icon when you want to run it, and then you
+can do everything from within the GUI.
+
+If LDmicro is passed a single filename on the command line
+(e.g. `ldmicro.exe asd.ld'), then LDmicro will try to open `asd.ld',
+if it exists. An error is produced if `asd.ld' does not exist. This
+means that you can associate ldmicro.exe with .ld files, so that it runs
+automatically when you double-click a .ld file.
+
+If LDmicro is passed command line arguments in the form
+`ldmicro.exe /c src.ld dest.hex', then it tries to compile `src.ld',
+and save the output as `dest.hex'. LDmicro exits after compiling,
+whether the compile was successful or not. Any messages are printed
+to the console. This mode is useful only when running LDmicro from the
+command line.
+
+
+BASICS
+======
+
+If you run LDmicro with no arguments then it starts with an empty
+program. If you run LDmicro with the name of a ladder program (xxx.ld)
+on the command line then it will try to load that program at startup.
+LDmicro uses its own internal format for the program; it cannot import
+logic from any other tool.
+
+If you did not load an existing program then you will be given a program
+with one empty rung. You could add an instruction to it; for example
+you could add a set of contacts (Instruction -> Insert Contacts) named
+`Xnew'. `X' means that the contacts will be tied to an input pin on the
+microcontroller. You could assign a pin to it later, after choosing a
+microcontroller and renaming the contacts. The first letter of a name
+indicates what kind of object it is. For example:
+
+ * Xname -- tied to an input pin on the microcontroller
+ * Yname -- tied to an output pin on the microcontroller
+ * Rname -- `internal relay': a bit in memory
+ * Tname -- a timer; turn-on delay, turn-off delay, or retentive
+ * Cname -- a counter, either count-up or count-down
+ * Aname -- an integer read from an A/D converter
+ * name -- a general-purpose (integer) variable
+
+Choose the rest of the name so that it describes what the object does,
+and so that it is unique within the program. The same name always refers
+to the same object within the program. For example, it would be an error
+to have a turn-on delay (TON) called `Tdelay' and a turn-off delay (TOF)
+called `Tdelay' in the same program, since each counter needs its own
+memory. On the other hand, it would be correct to have a retentive timer
+(RTO) called `Tdelay' and a reset instruction (RES) associated with
+`Tdelay', since it that case you want both instructions to work with
+the same timer.
+
+Variable names can consist of letters, numbers, and underscores
+(_). A variable name must not start with a number. Variable names are
+case-sensitive.
+
+The general variable instructions (MOV, ADD, EQU, etc.) can work on
+variables with any name. This means that they can access timer and
+counter accumulators. This may sometimes be useful; for example, you
+could check if the count of a timer is in a particular range.
+
+Variables are always 16 bit integers. This means that they can go
+from -32768 to 32767. Variables are always treated as signed. You can
+specify literals as normal decimal numbers (0, 1234, -56). You can also
+specify ASCII character values ('A', 'z') by putting the character in
+single-quotes. You can use an ASCII character code in most places that
+you could use a decimal number.
+
+At the bottom of the screen you will see a list of all the objects in
+the program. This list is automatically generated from the program;
+there is no need to keep it up to date by hand. Most objects do not
+need any configuration. `Xname', `Yname', and `Aname' objects must be
+assigned to a pin on the microcontroller, however. First choose which
+microcontroller you are using (Settings -> Microcontroller). Then assign
+your I/O pins by double-clicking them on the list.
+
+You can modify the program by inserting or deleting instructions. The
+cursor in the program display blinks to indicate the currently selected
+instruction and the current insertion point. If it is not blinking then
+press or click on an instruction. Now you can delete the current
+instruction, or you can insert a new instruction to the right or left
+(in series with) or above or below (in parallel with) the selected
+instruction. Some operations are not allowed. For example, no instructions
+are allowed to the right of a coil.
+
+The program starts with just one rung. You can add more rungs by selecting
+Insert Rung Before/After in the Logic menu. You could get the same effect
+by placing many complicated subcircuits in parallel within one rung,
+but it is more clear to use multiple rungs.
+
+Once you have written a program, you can test it in simulation, and then
+you can compile it to a HEX file for the target microcontroller.
+
+
+SIMULATION
+==========
+
+To enter simulation mode, choose Simulate -> Simulation Mode or press
+. The program is shown differently in simulation mode. There is
+no longer a cursor. The instructions that are energized show up bright
+red; the instructions that are not appear greyed. Press the space bar to
+run the PLC one cycle. To cycle continuously in real time, choose
+Simulate -> Start Real-Time Simulation, or press . The display of
+the program will be updated in real time as the program state changes.
+
+You can set the state of the inputs to the program by double-clicking
+them in the list at the bottom of the screen, or by double-clicking an
+`Xname' contacts instruction in the program. If you change the state of
+an input pin then that change will not be reflected in how the program
+is displayed until the PLC cycles; this will happen automatically if
+you are running a real time simulation, or when you press the space bar.
+
+
+COMPILING TO NATIVE CODE
+========================
+
+Ultimately the point is to generate a .hex file that you can program
+into your microcontroller. First you must select the part number of the
+microcontroller, under the Settings -> Microcontroller menu. Then you
+must assign an I/O pin to each `Xname' or `Yname' object. Do this by
+double-clicking the object name in the list at the bottom of the screen.
+A dialog will pop up where you can choose an unallocated pin from a list.
+
+Then you must choose the cycle time that you will run with, and you must
+tell the compiler what clock speed the micro will be running at. These
+are set under the Settings -> MCU Parameters... menu. In general you
+should not need to change the cycle time; 10 ms is a good value for most
+applications. Type in the frequency of the crystal that you will use
+with the microcontroller (or the ceramic resonator, etc.) and click okay.
+
+Now you can generate code from your program. Choose Compile -> Compile,
+or Compile -> Compile As... if you have previously compiled this program
+and you want to specify a different output file name. If there are no
+errors then LDmicro will generate an Intel IHEX file ready for
+programming into your chip.
+
+Use whatever programming software and hardware you have to load the hex
+file into the microcontroller. Remember to set the configuration bits
+(fuses)! For PIC16 processors, the configuration bits are included in the
+hex file, and most programming software will look there automatically.
+For AVR processors you must set the configuration bits by hand.
+
+
+INSTRUCTIONS REFERENCE
+======================
+
+> CONTACT, NORMALLY OPEN Xname Rname Yname
+ ----] [---- ----] [---- ----] [----
+
+ If the signal going into the instruction is false, then the output
+ signal is false. If the signal going into the instruction is true,
+ then the output signal is true if and only if the given input pin,
+ output pin, or internal relay is true, else it is false. This
+ instruction can examine the state of an input pin, an output pin,
+ or an internal relay.
+
+
+> CONTACT, NORMALLY CLOSED Xname Rname Yname
+ ----]/[---- ----]/[---- ----]/[----
+
+ If the signal going into the instruction is false, then the output
+ signal is false. If the signal going into the instruction is true,
+ then the output signal is true if and only if the given input pin,
+ output pin, or internal relay is false, else it is false. This
+ instruction can examine the state of an input pin, an output pin,
+ or an internal relay. This is the opposite of a normally open contact.
+
+
+> COIL, NORMAL Rname Yname
+ ----( )---- ----( )----
+
+ If the signal going into the instruction is false, then the given
+ internal relay or output pin is cleared false. If the signal going
+ into this instruction is true, then the given internal relay or output
+ pin is set true. It is not meaningful to assign an input variable to a
+ coil. This instruction must be the rightmost instruction in its rung.
+
+
+> COIL, NEGATED Rname Yname
+ ----(/)---- ----(/)----
+
+ If the signal going into the instruction is true, then the given
+ internal relay or output pin is cleared false. If the signal going
+ into this instruction is false, then the given internal relay or
+ output pin is set true. It is not meaningful to assign an input
+ variable to a coil. This is the opposite of a normal coil. This
+ instruction must be the rightmost instruction in its rung.
+
+
+> COIL, SET-ONLY Rname Yname
+ ----(S)---- ----(S)----
+
+ If the signal going into the instruction is true, then the given
+ internal relay or output pin is set true. Otherwise the internal
+ relay or output pin state is not changed. This instruction can only
+ change the state of a coil from false to true, so it is typically
+ used in combination with a reset-only coil. This instruction must
+ be the rightmost instruction in its rung.
+
+
+> COIL, RESET-ONLY Rname Yname
+ ----(R)---- ----(R)----
+
+ If the signal going into the instruction is true, then the given
+ internal relay or output pin is cleared false. Otherwise the
+ internal relay or output pin state is not changed. This instruction
+ instruction can only change the state of a coil from true to false,
+ so it is typically used in combination with a set-only coil. This
+ instruction must be the rightmost instruction in its rung.
+
+
+> TURN-ON DELAY Tdon
+ -[TON 1.000 s]-
+
+ When the signal going into the instruction goes from false to true,
+ the output signal stays false for 1.000 s before going true. When the
+ signal going into the instruction goes from true to false, the output
+ signal goes false immediately. The timer is reset every time the input
+ goes false; the input must stay true for 1000 consecutive milliseconds
+ before the output will go true. The delay is configurable.
+
+ The `Tname' variable counts up from zero in units of scan times. The
+ TON instruction outputs true when the counter variable is greater
+ than or equal to the given delay. It is possible to manipulate the
+ counter variable elsewhere, for example with a MOV instruction.
+
+
+> TURN-OFF DELAY Tdoff
+ -[TOF 1.000 s]-
+
+ When the signal going into the instruction goes from true to false,
+ the output signal stays true for 1.000 s before going false. When
+ the signal going into the instruction goes from false to true,
+ the output signal goes true immediately. The timer is reset every
+ time the input goes false; the input must stay false for 1000
+ consecutive milliseconds before the output will go false. The delay
+ is configurable.
+
+ The `Tname' variable counts up from zero in units of scan times. The
+ TON instruction outputs true when the counter variable is greater
+ than or equal to the given delay. It is possible to manipulate the
+ counter variable elsewhere, for example with a MOV instruction.
+
+
+> RETENTIVE TIMER Trto
+ -[RTO 1.000 s]-
+
+ This instruction keeps track of how long its input has been true. If
+ its input has been true for at least 1.000 s, then the output is
+ true. Otherwise the output is false. The input need not have been
+ true for 1000 consecutive milliseconds; if the input goes true
+ for 0.6 s, then false for 2.0 s, and then true for 0.4 s, then the
+ output will go true. After the output goes true it will stay true
+ even after the input goes false, as long as the input has been true
+ for longer than 1.000 s. This timer must therefore be reset manually,
+ using the reset instruction.
+
+ The `Tname' variable counts up from zero in units of scan times. The
+ TON instruction outputs true when the counter variable is greater
+ than or equal to the given delay. It is possible to manipulate the
+ counter variable elsewhere, for example with a MOV instruction.
+
+
+> RESET Trto Citems
+ ----{RES}---- ----{RES}----
+
+ This instruction resets a timer or a counter. TON and TOF timers are
+ automatically reset when their input goes false or true, so RES is
+ not required for these timers. RTO timers and CTU/CTD counters are
+ not reset automatically, so they must be reset by hand using a RES
+ instruction. When the input is true, the counter or timer is reset;
+ when the input is false, no action is taken. This instruction must
+ be the rightmost instruction in its rung.
+
+
+> ONE-SHOT RISING _
+ --[OSR_/ ]--
+
+ This instruction normally outputs false. If the instruction's input
+ is true during this scan and it was false during the previous scan
+ then the output is true. It therefore generates a pulse one scan
+ wide on each rising edge of its input signal. This instruction is
+ useful if you want to trigger events off the rising edge of a signal.
+
+
+> ONE-SHOT FALLING _
+ --[OSF \_]--
+
+ This instruction normally outputs false. If the instruction's input
+ is false during this scan and it was true during the previous scan
+ then the output is true. It therefore generates a pulse one scan
+ wide on each falling edge of its input signal. This instruction is
+ useful if you want to trigger events off the falling edge of a signal.
+
+
+> SHORT CIRCUIT, OPEN CIRCUIT
+ ----+----+---- ----+ +----
+
+ The output condition of a short-circuit is always equal to its
+ input condition. The output condition of an open-circuit is always
+ false. These are mostly useful for debugging.
+
+
+> MASTER CONTROL RELAY
+ -{MASTER RLY}-
+
+ By default, the rung-in condition of every rung is true. If a master
+ control relay instruction is executed with a rung-in condition of
+ false, then the rung-in condition for all following rungs becomes
+ false. This will continue until the next master control relay
+ instruction is reached (regardless of the rung-in condition of that
+ instruction). These instructions must therefore be used in pairs:
+ one to (maybe conditionally) start the possibly-disabled section,
+ and one to end it.
+
+
+> MOVE {destvar := } {Tret := }
+ -{ 123 MOV}- -{ srcvar MOV}-
+
+ When the input to this instruction is true, it sets the given
+ destination variable equal to the given source variable or
+ constant. When the input to this instruction is false nothing
+ happens. You can assign to any variable with the move instruction;
+ this includes timer and counter state variables, which can be
+ distinguished by the leading `T' or `C'. For example, an instruction
+ moving 0 into `Tretentive' is equivalent to a reset (RES) instruction
+ for that timer. This instruction must be the rightmost instruction
+ in its rung.
+
+
+> ARITHMETIC OPERATION {ADD kay :=} {SUB Ccnt :=}
+ -{ 'a' + 10 }- -{ Ccnt - 10 }-
+
+> {MUL dest :=} {DIV dv := }
+ -{ var * -990 }- -{ dv / -10000}-
+
+ When the input to this instruction is true, it sets the given
+ destination variable equal to the given expression. The operands
+ can be either variables (including timer and counter variables)
+ or constants. These instructions use 16 bit signed math. Remember
+ that the result is evaluated every cycle when the input condition
+ true. If you are incrementing or decrementing a variable (i.e. if
+ the destination variable is also one of the operands) then you
+ probably don't want that; typically you would use a one-shot so that
+ it is evaluated only on the rising or falling edge of the input
+ condition. Divide truncates; 8 / 3 = 2. This instruction must be
+ the rightmost instruction in its rung.
+
+
+> COMPARE [var ==] [var >] [1 >=]
+ -[ var2 ]- -[ 1 ]- -[ Ton]-
+
+> [var /=] [-4 < ] [1 <=]
+ -[ var2 ]- -[ vartwo]- -[ Cup]-
+
+ If the input to this instruction is false then the output is false. If
+ the input is true then the output is true if and only if the given
+ condition is true. This instruction can be used to compare (equals,
+ is greater than, is greater than or equal to, does not equal,
+ is less than, is less than or equal to) a variable to a variable,
+ or to compare a variable to a 16-bit signed constant.
+
+
+> COUNTER Cname Cname
+ --[CTU >=5]-- --[CTD >=5]--
+
+ A counter increments (CTU, count up) or decrements (CTD, count
+ down) the associated count on every rising edge of the rung input
+ condition (i.e. what the rung input condition goes from false to
+ true). The output condition from the counter is true if the counter
+ variable is greater than or equal to 5, and false otherwise. The
+ rung output condition may be true even if the input condition is
+ false; it only depends on the counter variable. You can have CTU
+ and CTD instructions with the same name, in order to increment and
+ decrement the same counter. The RES instruction can reset a counter,
+ or you can perform general variable operations on the count variable.
+
+
+> CIRCULAR COUNTER Cname
+ --{CTC 0:7}--
+
+ A circular counter works like a normal CTU counter, except that
+ after reaching its upper limit, it resets its counter variable
+ back to 0. For example, the counter shown above would count 0, 1,
+ 2, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 2,.... This is useful in
+ combination with conditional statements on the variable `Cname';
+ you can use this like a sequencer. CTC counters clock on the rising
+ edge of the rung input condition condition. This instruction must
+ be the rightmost instruction in its rung.
+
+
+> SHIFT REGISTER {SHIFT REG }
+ -{ reg0..3 }-
+
+ A shift register is associated with a set of variables. For example,
+ this shift register is associated with the variables `reg0', `reg1',
+ `reg2', and `reg3'. The input to the shift register is `reg0'. On
+ every rising edge of the rung-in condition, the shift register will
+ shift right. That means that it assigns `reg3 := reg2', `reg2 :=
+ reg1'. and `reg1 := reg0'. `reg0' is left unchanged. A large shift
+ register can easily consume a lot of memory. This instruction must
+ be the rightmost instruction in its rung.
+
+
+> LOOK-UP TABLE {dest := }
+ -{ LUT[i] }-
+
+ A look-up table is an ordered set of n values. When the rung-in
+ condition is true, the integer variable `dest' is set equal to the
+ entry in the lookup table corresponding to the integer variable
+ `i'. The index starts from zero, so `i' must be between 0 and
+ (n-1). The behaviour of this instruction is not defined if the
+ index is outside this range. This instruction must be the rightmost
+ instruction in its rung.
+
+
+> PIECEWISE LINEAR TABLE {yvar := }
+ -{ PWL[xvar] }-
+
+ This is a good way to approximate a complicated function or
+ curve. It might, for example, be useful if you are trying to apply
+ a calibration curve to convert a raw output voltage from a sensor
+ into more convenient units.
+
+ Assume that you are trying to approximate a function that converts
+ an integer input variable, x, to an integer output variable, y. You
+ know the function at several points; for example, you might know that
+
+ f(0) = 2
+ f(5) = 10
+ f(10) = 50
+ f(100) = 100
+
+ This means that the points
+
+ (x0, y0) = ( 0, 2)
+ (x1, y1) = ( 5, 10)
+ (x2, y2) = ( 10, 50)
+ (x3, y3) = (100, 100)
+
+ lie on that curve. You can enter those 4 points into a table
+ associated with the piecewise linear instruction. The piecewise linear
+ instruction will look at the value of xvar, and set the value of
+ yvar. It will set yvar in such a way that the piecewise linear curve
+ will pass through all of the points that you give it; for example,
+ if you set xvar = 10, then the instruction will set yvar = 50.
+
+ If you give the instruction a value of xvar that lies between two
+ of the values of x for which you have given it points, then the
+ instruction will set yvar so that (xvar, yvar) lies on the straight
+ line connecting those two points in the table. For example, xvar =
+ 55 gives an output of yvar = 75. (The two points in the table are
+ (10, 50) and (100, 100). 55 is half-way between 10 and 100, and 75
+ is half-way between 50 and 100, so (55, 75) lies on the line that
+ connects those two points.)
+
+ The points must be specified in ascending order by x coordinate. It
+ may not be possible to perform mathematical operations required for
+ certain look-up tables using 16-bit integer math; if this is the
+ case, then LDmicro will warn you. For example, this look up table
+ will produce an error:
+
+ (x0, y0) = ( 0, 0)
+ (x1, y1) = (300, 300)
+
+ You can fix these errors by making the distance between points in
+ the table smaller. For example, this table is equivalent to the one
+ given above, and it does not produce an error:
+
+ (x0, y0) = ( 0, 0)
+ (x1, y1) = (150, 150)
+ (x2, y2) = (300, 300)
+
+ It should hardly ever be necessary to use more than five or six
+ points. Adding more points makes your code larger and slower to
+ execute. The behaviour if you pass a value of `xvar' greater than
+ the greatest x coordinate in the table or less than the smallest x
+ coordinate in the table is undefined. This instruction must be the
+ rightmost instruction in its rung.
+
+
+> A/D CONVERTER READ Aname
+ --{READ ADC}--
+
+ LDmicro can generate code to use the A/D converters built in to
+ certain microcontrollers. If the input condition to this instruction
+ is true, then a single sample from the A/D converter is acquired and
+ stored in the variable `Aname'. This variable can subsequently be
+ manipulated with general variable operations (less than, greater than,
+ arithmetic, and so on). Assign a pin to the `Axxx' variable in the
+ same way that you would assign a pin to a digital input or output,
+ by double-clicking it in the list at the bottom of the screen. If
+ the input condition to this rung is false then the variable `Aname'
+ is left unchanged.
+
+ For all currently-supported devices, 0 volts input corresponds to
+ an ADC reading of 0, and an input equal to Vdd (the supply voltage)
+ corresponds to an ADC reading of 1023. If you are using an AVR, then
+ connect AREF to Vdd. You can use arithmetic operations to scale the
+ reading to more convenient units afterwards, but remember that you
+ are using integer math. In general not all pins will be available
+ for use with the A/D converter. The software will not allow you to
+ assign non-A/D pins to an analog input. This instruction must be
+ the rightmost instruction in its rung.
+
+
+> SET PWM DUTY CYCLE duty_cycle
+ -{PWM 32.8 kHz}-
+
+ LDmicro can generate code to use the PWM peripheral built in to
+ certain microcontrollers. If the input condition to this instruction
+ is true, then the duty cycle of the PWM peripheral is set to the
+ value of the variable duty_cycle. The duty cycle must be a number
+ between 0 and 100; 0 corresponds to always low, and 100 corresponds to
+ always high. (If you are familiar with how the PWM peripheral works,
+ then notice that that means that LDmicro automatically scales the
+ duty cycle variable from percent to PWM clock periods.)
+
+ You can specify the target PWM frequency, in Hz. The frequency that
+ you specify might not be exactly achievable, depending on how it
+ divides into the microcontroller's clock frequency. LDmicro will
+ choose the closest achievable frequency; if the error is large then
+ it will warn you. Faster speeds may sacrifice resolution.
+
+ This instruction must be the rightmost instruction in its rung.
+ The ladder logic runtime consumes one timer to measure the cycle
+ time. That means that PWM is only available on microcontrollers
+ with at least two suitable timers. PWM uses pin CCP2 (not CCP1)
+ on PIC16 chips and OC2 (not OC1A) on AVRs.
+
+
+> MAKE PERSISTENT saved_var
+ --{PERSIST}--
+
+ When the rung-in condition of this instruction is true, it causes the
+ specified integer variable to be automatically saved to EEPROM. That
+ means that its value will persist, even when the micro loses
+ power. There is no need to explicitly save the variable to EEPROM;
+ that will happen automatically, whenever the variable changes. The
+ variable is automatically loaded from EEPROM after power-on reset. If
+ a variable that changes frequently is made persistent, then the
+ EEPROM in your micro may wear out very quickly, because it is only
+ good for a limited (~100 000) number of writes. When the rung-in
+ condition is false, nothing happens. This instruction must be the
+ rightmost instruction in its rung.
+
+
+> UART (SERIAL) RECEIVE var
+ --{UART RECV}--
+
+ LDmicro can generate code to use the UART built in to certain
+ microcontrollers. On AVRs with multiple UARTs only UART1 (not
+ UART0) is supported. Configure the baud rate using Settings -> MCU
+ Parameters. Certain baud rates may not be achievable with certain
+ crystal frequencies; LDmicro will warn you if this is the case.
+
+ If the input condition to this instruction is false, then nothing
+ happens. If the input condition is true then this instruction tries
+ to receive a single character from the UART. If no character is read
+ then the output condition is false. If a character is read then its
+ ASCII value is stored in `var', and the output condition is true
+ for a single PLC cycle.
+
+
+> UART (SERIAL) SEND var
+ --{UART SEND}--
+
+ LDmicro can generate code to use the UARTs built in to certain
+ microcontrollers. On AVRS with multiple UARTs only UART1 (not
+ UART0) is supported. Configure the baud rate using Settings -> MCU
+ Parameters. Certain baud rates may not be achievable with certain
+ crystal frequencies; LDmicro will warn you if this is the case.
+
+ If the input condition to this instruction is false, then nothing
+ happens. If the input condition is true then this instruction writes
+ a single character to the UART. The ASCII value of the character to
+ send must previously have been stored in `var'. The output condition
+ of the rung is true if the UART is busy (currently transmitting a
+ character), and false otherwise.
+
+ Remember that characters take some time to transmit. Check the output
+ condition of this instruction to ensure that the first character has
+ been transmitted before trying to send a second character, or use
+ a timer to insert a delay between characters. You must only bring
+ the input condition true (try to send a character) when the output
+ condition is false (UART is not busy).
+
+ Investigate the formatted string instruction (next) before using this
+ instruction. The formatted string instruction is much easier to use,
+ and it is almost certainly capable of doing what you want.
+
+
+> FORMATTED STRING OVER UART var
+ -{"Pressure: \3\r\n"}-
+
+ LDmicro can generate code to use the UARTs built in to certain
+ microcontrollers. On AVRS with multiple UARTs only UART1 (not
+ UART0) is supported. Configure the baud rate using Settings -> MCU
+ Parameters. Certain baud rates may not be achievable with certain
+ crystal frequencies; LDmicro will warn you if this is the case.
+
+ When the rung-in condition for this instruction goes from false to
+ true, it starts to send an entire string over the serial port. If
+ the string contains the special sequence `\3', then that sequence
+ will be replaced with the value of `var', which is automatically
+ converted into a string. The variable will be formatted to take
+ exactly 3 characters; for example, if `var' is equal to 35, then
+ the exact string printed will be `Pressure: 35\r\n' (note the extra
+ space). If instead `var' were equal to 1432, then the behaviour would
+ be undefined, because 1432 has more than three digits. In that case
+ it would be necessary to use `\4' instead.
+
+ If the variable might be negative, then use `\-3d' (or `\-4d'
+ etc.) instead. That will cause LDmicro to print a leading space for
+ positive numbers, and a leading minus sign for negative numbers.
+
+ If multiple formatted string instructions are energized at once
+ (or if one is energized before another completes), or if these
+ instructions are intermixed with the UART TX instructions, then the
+ behaviour is undefined.
+
+ It is also possible to use this instruction to output a fixed string,
+ without interpolating an integer variable's value into the text that
+ is sent over serial. In that case simply do not include the special
+ escape sequence.
+
+ Use `\\' for a literal backslash. In addition to the escape sequence
+ for interpolating an integer variable, the following control
+ characters are available:
+ * \r -- carriage return
+ * \n -- newline
+ * \f -- formfeed
+ * \b -- backspace
+ * \xAB -- character with ASCII value 0xAB (hex)
+
+ The rung-out condition of this instruction is true while it is
+ transmitting data, else false. This instruction consumes a very
+ large amount of program memory, so it should be used sparingly. The
+ present implementation is not efficient, but a better one will
+ require modifications to all the back-ends.
+
+
+A NOTE ON USING MATH
+====================
+
+Remember that LDmicro performs only 16-bit integer math. That means
+that the final result of any calculation that you perform must be an
+integer between -32768 and 32767. It also mean that the intermediate
+results of your calculation must all be within that range.
+
+For example, let us say that you wanted to calculate y = (1/x)*1200,
+where x is between 1 and 20. Then y goes between 1200 and 60, which
+fits into a 16-bit integer, so it is at least in theory possible to
+perform the calculation. There are two ways that you might code this:
+you can perform the reciprocal, and then multiply:
+
+ || {DIV temp :=} ||
+ ||---------{ 1 / x }----------||
+ || ||
+ || {MUL y := } ||
+ ||----------{ temp * 1200}----------||
+ || ||
+
+Or you could just do the division directly, in a single step:
+
+ || {DIV y :=} ||
+ ||-----------{ 1200 / x }-----------||
+
+Mathematically, these two are equivalent; but if you try them, then you
+will find that the first one gives an incorrect result of y = 0. That
+is because the variable `temp' underflows. For example, when x = 3,
+(1 / x) = 0.333, but that is not an integer; the division operation
+approximates this as temp = 0. Then y = temp * 1200 = 0. In the second
+case there is no intermediate result to underflow, so everything works.
+
+If you are seeing problems with your math, then check intermediate
+results for underflow (or overflow, which `wraps around'; for example,
+32767 + 1 = -32768). When possible, choose units that put values in
+a range of -100 to 100.
+
+When you need to scale a variable by some factor, do it using a multiply
+and a divide. For example, to scale y = 1.8*x, calculate y = (9/5)*x
+(which is the same, since 1.8 = 9/5), and code this as y = (9*x)/5,
+performing the multiplication first:
+
+ || {MUL temp :=} ||
+ ||---------{ x * 9 }----------||
+ || ||
+ || {DIV y :=} ||
+ ||-----------{ temp / 5 }-----------||
+
+This works for all x < (32767 / 9), or x < 3640. For larger values of x,
+the variable `temp' would overflow. There is a similar lower limit on x.
+
+
+CODING STYLE
+============
+
+I allow multiple coils in parallel in a single rung. This means that
+you can do things like this:
+
+ || Xa Ya ||
+ 1 ||-------] [--------------( )-------||
+ || ||
+ || Xb Yb ||
+ ||-------] [------+-------( )-------||
+ || | ||
+ || | Yc ||
+ || +-------( )-------||
+ || ||
+
+Instead of this:
+
+ || Xa Ya ||
+ 1 ||-------] [--------------( )-------||
+ || ||
+ || ||
+ || ||
+ || ||
+ || Xb Yb ||
+ 2 ||-------] [--------------( )-------||
+ || ||
+ || ||
+ || ||
+ || ||
+ || Xb Yc ||
+ 3 ||-------] [--------------( )-------||
+ || ||
+
+This means that in theory you could write any program as one giant rung,
+and there is no need to use multiple rungs at all. In practice that
+would be a bad idea, because as rungs become more complex they become
+more difficult to edit without deleting and redrawing a lot of logic.
+
+Still, it is often a good idea to group related logic together as a single
+rung. This generates nearly identical code to if you made separate rungs,
+but it shows that they are related when you look at them on the ladder
+diagram.
+
+ * * *
+
+In general, it is considered poor form to write code in such a way that
+its output depends on the order of the rungs. For example, this code
+isn't very good if both Xa and Xb might ever be true:
+
+ || Xa {v := } ||
+ 1 ||-------] [--------{ 12 MOV}--||
+ || ||
+ || Xb {v := } ||
+ ||-------] [--------{ 23 MOV}--||
+ || ||
+ || ||
+ || ||
+ || ||
+ || [v >] Yc ||
+ 2 ||------[ 15]-------------( )-------||
+ || ||
+
+I will break this rule if in doing so I can make a piece of code
+significantly more compact, though. For example, here is how I would
+convert a 4-bit binary quantity on Xb3:0 into an integer:
+
+ || {v := } ||
+ 3 ||-----------------------------------{ 0 MOV}--||
+ || ||
+ || Xb0 {ADD v :=} ||
+ ||-------] [------------------{ v + 1 }-----------||
+ || ||
+ || Xb1 {ADD v :=} ||
+ ||-------] [------------------{ v + 2 }-----------||
+ || ||
+ || Xb2 {ADD v :=} ||
+ ||-------] [------------------{ v + 4 }-----------||
+ || ||
+ || Xb3 {ADD v :=} ||
+ ||-------] [------------------{ v + 8 }-----------||
+ || ||
+
+If the MOV statement were moved to the bottom of the rung instead of the
+top, then the value of v when it is read elsewhere in the program would
+be 0. The output of this code therefore depends on the order in which
+the instructions are evaluated. Considering how cumbersome it would be
+to code this any other way, I accept that.
+
+
+BUGS
+====
+
+LDmicro does not generate very efficient code; it is slow to execute, and
+wasteful of flash and RAM. In spite of this, a mid-sized PIC or AVR can
+do everything that a small PLC can, so this does not bother me very much.
+
+The maximum length of variable names is highly limited. This is so that
+they fit nicely onto the ladder diagram, so I don't see a good solution
+to that.
+
+If your program is too big for the time, program memory, or data memory
+constraints of the device that you have chosen then you probably won't
+get an error. It will just screw up somewhere.
+
+Careless programming in the file load/save routines probably makes it
+possible to crash or execute arbitrary code given a corrupt or malicious
+.ld file.
+
+Please report additional bugs or feature requests to the author.
+
+Thanks to:
+ * Marcelo Solano, for reporting a UI bug under Win98
+ * Serge V. Polubarjev, for not only noticing that RA3:0 on the
+ PIC16F628 didn't work but also telling me how to fix it
+ * Maxim Ibragimov, for reporting and diagnosing major problems
+ with the till-then-untested ATmega16 and ATmega162 targets
+ * Bill Kishonti, for reporting that the simulator crashed when the
+ ladder logic program divided by zero
+ * Mohamed Tayae, for reporting that persistent variables were broken
+ on the PIC16F628
+ * David Rothwell, for reporting several user interface bugs and a
+ problem with the "Export as Text" function
+
+
+COPYING, AND DISCLAIMER
+=======================
+
+DO NOT USE CODE GENERATED BY LDMICRO IN APPLICATIONS WHERE SOFTWARE
+FAILURE COULD RESULT IN DANGER TO HUMAN LIFE OR DAMAGE TO PROPERTY. THE
+AUTHOR ASSUMES NO LIABILITY FOR ANY DAMAGES RESULTING FROM THE OPERATION
+OF LDMICRO OR CODE GENERATED BY LDMICRO.
+
+This program 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.
+
+This program 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 this program. If not, see .
+
+
+Jonathan Westhues
+
+Rijswijk -- Dec 2004
+Waterloo ON -- Jun, Jul 2005
+Cambridge MA -- Sep, Dec 2005
+ Feb, Mar 2006
+ Feb 2007
+Seattle WA -- Feb 2009
+
+Email: user jwesthues, at host cq.cx
+
+
diff --git a/ldmicro/mcutable.h b/ldmicro/mcutable.h
new file mode 100644
index 0000000..a33c41e
--- /dev/null
+++ b/ldmicro/mcutable.h
@@ -0,0 +1,862 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// The table of supported MCUs, used to determine where the IOs are, what
+// instruction set, what init code, etc.
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// ATmega128 or ATmega64
+
+McuIoPinInfo AvrAtmega128_64TQFPIoPinInfo[] = {
+ { 'E', 0, 2 },
+ { 'E', 1, 3 },
+ { 'E', 2, 4 },
+ { 'E', 3, 5 },
+ { 'E', 4, 6 },
+ { 'E', 5, 7 },
+ { 'E', 6, 8 },
+ { 'E', 7, 9 },
+
+ { 'B', 0, 10 },
+ { 'B', 1, 11 },
+ { 'B', 2, 12 },
+ { 'B', 3, 13 },
+ { 'B', 4, 14 },
+ { 'B', 5, 15 },
+ { 'B', 6, 16 },
+ { 'B', 7, 17 },
+
+ { 'G', 3, 18 },
+ { 'G', 4, 19 },
+
+ { 'D', 0, 25 },
+ { 'D', 1, 26 },
+ { 'D', 2, 27 },
+ { 'D', 3, 28 },
+ { 'D', 4, 29 },
+ { 'D', 5, 30 },
+ { 'D', 6, 31 },
+ { 'D', 7, 32 },
+
+ { 'G', 0, 33 },
+ { 'G', 1, 34 },
+
+ { 'C', 0, 35 },
+ { 'C', 1, 36 },
+ { 'C', 2, 37 },
+ { 'C', 3, 38 },
+ { 'C', 4, 39 },
+ { 'C', 5, 40 },
+ { 'C', 6, 41 },
+ { 'C', 7, 42 },
+
+ { 'G', 2, 43 },
+
+ { 'A', 7, 44 },
+ { 'A', 6, 45 },
+ { 'A', 5, 46 },
+ { 'A', 4, 47 },
+ { 'A', 3, 48 },
+ { 'A', 2, 49 },
+ { 'A', 1, 50 },
+ { 'A', 0, 51 },
+
+ { 'F', 7, 54 },
+ { 'F', 6, 55 },
+ { 'F', 5, 56 },
+ { 'F', 4, 57 },
+ { 'F', 3, 58 },
+ { 'F', 2, 59 },
+ { 'F', 1, 60 },
+ { 'F', 0, 61 },
+};
+
+McuAdcPinInfo AvrAtmega128_64TQFPAdcPinInfo[] = {
+ { 61, 0x00 },
+ { 60, 0x01 },
+ { 59, 0x02 },
+ { 58, 0x03 },
+ { 57, 0x04 },
+ { 56, 0x05 },
+ { 55, 0x06 },
+ { 54, 0x07 },
+};
+
+
+//-----------------------------------------------------------------------------
+// ATmega162
+
+McuIoPinInfo AvrAtmega162IoPinInfo[] = {
+ { 'B', 0, 1 },
+ { 'B', 1, 2 },
+ { 'B', 2, 3 },
+ { 'B', 3, 4 },
+ { 'B', 4, 5 },
+ { 'B', 5, 6 },
+ { 'B', 6, 7 },
+ { 'B', 7, 8 },
+ { 'D', 0, 10 },
+ { 'D', 1, 11 },
+ { 'D', 2, 12 },
+ { 'D', 3, 13 },
+ { 'D', 4, 14 },
+ { 'D', 5, 15 },
+ { 'D', 6, 16 },
+ { 'D', 7, 17 },
+ { 'C', 0, 21 },
+ { 'C', 1, 22 },
+ { 'C', 2, 23 },
+ { 'C', 3, 24 },
+ { 'C', 4, 25 },
+ { 'C', 5, 26 },
+ { 'C', 6, 27 },
+ { 'C', 7, 28 },
+ { 'E', 2, 29 },
+ { 'E', 1, 30 },
+ { 'E', 0, 31 },
+ { 'A', 7, 32 },
+ { 'A', 6, 33 },
+ { 'A', 5, 34 },
+ { 'A', 4, 35 },
+ { 'A', 3, 36 },
+ { 'A', 2, 37 },
+ { 'A', 1, 38 },
+ { 'A', 0, 39 },
+};
+
+
+//-----------------------------------------------------------------------------
+// ATmega16 or ATmega32
+
+McuIoPinInfo AvrAtmega16or32IoPinInfo[] = {
+ { 'B', 0, 1 },
+ { 'B', 1, 2 },
+ { 'B', 2, 3 },
+ { 'B', 3, 4 },
+ { 'B', 4, 5 },
+ { 'B', 5, 6 },
+ { 'B', 6, 7 },
+ { 'B', 7, 8 },
+ { 'D', 0, 14 },
+ { 'D', 1, 15 },
+ { 'D', 2, 16 },
+ { 'D', 3, 17 },
+ { 'D', 4, 18 },
+ { 'D', 5, 19 },
+ { 'D', 6, 20 },
+ { 'D', 7, 21 },
+ { 'C', 0, 22 },
+ { 'C', 1, 23 },
+ { 'C', 2, 24 },
+ { 'C', 3, 25 },
+ { 'C', 4, 26 },
+ { 'C', 5, 27 },
+ { 'C', 6, 28 },
+ { 'C', 7, 29 },
+ { 'A', 7, 33 },
+ { 'A', 6, 34 },
+ { 'A', 5, 35 },
+ { 'A', 4, 36 },
+ { 'A', 3, 37 },
+ { 'A', 2, 38 },
+ { 'A', 1, 39 },
+ { 'A', 0, 40 },
+};
+
+McuAdcPinInfo AvrAtmega16or32AdcPinInfo[] = {
+ { 40, 0x00 },
+ { 39, 0x01 },
+ { 38, 0x02 },
+ { 37, 0x03 },
+ { 36, 0x04 },
+ { 35, 0x05 },
+ { 34, 0x06 },
+ { 33, 0x07 },
+};
+
+
+//-----------------------------------------------------------------------------
+// ATmega328_28PDIP Arduino
+
+McuIoPinInfo AvrAtmega328_28PDIPIoPinInfo[] = {
+ { 'C', 6, 1 },
+ { 'D', 0, 2 },
+ { 'D', 1, 3 },
+ { 'D', 2, 4 },
+ { 'D', 3, 5 },
+ { 'D', 4, 6 },
+ { 'B', 6, 9 },
+ { 'B', 7, 10 },
+ { 'D', 5, 11 },
+ { 'D', 6, 12 },
+ { 'D', 7, 13 },
+ { 'B', 0, 14 },
+ { 'B', 1, 15 },
+ { 'B', 2, 16 },
+ { 'B', 3, 17 },
+ { 'B', 4, 18 },
+ { 'B', 5, 19 },
+ { 'C', 0, 23 },
+ { 'C', 1, 24 },
+ { 'C', 2, 25 },
+ { 'C', 3, 26 },
+ { 'C', 4, 27 },
+ { 'C', 5, 28 },
+};
+
+McuAdcPinInfo AvrAtmega328_28PDIPAdcPinInfo[] = {
+ { 23, 0x00 },
+ { 24, 0x01 },
+ { 25, 0x02 },
+ { 26, 0x03 },
+ { 27, 0x04 },
+};
+
+
+//-----------------------------------------------------------------------------
+// ATmega8
+
+McuIoPinInfo AvrAtmega8IoPinInfo[] = {
+ { 'D', 0, 2 },
+ { 'D', 1, 3 },
+ { 'D', 2, 4 },
+ { 'D', 3, 5 },
+ { 'D', 4, 6 },
+ { 'D', 5, 11 },
+ { 'D', 6, 12 },
+ { 'D', 7, 13 },
+ { 'B', 0, 14 },
+ { 'B', 1, 15 },
+ { 'B', 2, 16 },
+ { 'B', 3, 17 },
+ { 'B', 4, 18 },
+ { 'B', 5, 19 },
+ { 'C', 0, 23 },
+ { 'C', 1, 24 },
+ { 'C', 2, 25 },
+ { 'C', 3, 26 },
+ { 'C', 4, 27 },
+ { 'C', 5, 28 },
+};
+
+McuAdcPinInfo AvrAtmega8AdcPinInfo[] = {
+ { 23, 0x00 }, // ADC0
+ { 24, 0x01 },
+ { 25, 0x02 },
+ { 26, 0x03 },
+ { 27, 0x04 },
+ { 28, 0x05 }, // ADC5
+};
+
+
+//-----------------------------------------------------------------------------
+// A variety of 18-pin PICs that share the same digital IO assignment.
+
+McuIoPinInfo Pic18PinIoInfo[] = {
+ { 'A', 2, 1 },
+ { 'A', 3, 2 },
+ { 'A', 4, 3 },
+ { 'A', 5, 4 },
+ { 'B', 0, 6 },
+ { 'B', 1, 7 },
+ { 'B', 2, 8 },
+ { 'B', 3, 9 },
+ { 'B', 4, 10 },
+ { 'B', 5, 11 },
+ { 'B', 6, 12 },
+ { 'B', 7, 13 },
+ { 'A', 6, 15 },
+ { 'A', 7, 16 },
+ { 'A', 0, 17 },
+ { 'A', 1, 18 },
+};
+
+McuAdcPinInfo Pic16f819AdcPinInfo[] = {
+ { 1, 0x02 },
+ { 2, 0x03 },
+ { 3, 0x04 },
+ { 17, 0x00 },
+ { 18, 0x01 },
+};
+
+McuAdcPinInfo Pic16f88AdcPinInfo[] = {
+ { 1, 0x02 },
+ { 2, 0x03 },
+ { 3, 0x04 },
+ { 12, 0x05 },
+ { 13, 0x06 },
+ { 17, 0x00 },
+ { 18, 0x01 },
+};
+
+
+//-----------------------------------------------------------------------------
+// PIC16F877
+
+McuIoPinInfo Pic16f877IoPinInfo[] = {
+ { 'A', 0, 2 },
+ { 'A', 1, 3 },
+ { 'A', 2, 4 },
+ { 'A', 3, 5 },
+ { 'A', 4, 6 },
+ { 'A', 5, 7 },
+ { 'E', 0, 8 },
+ { 'E', 1, 9 },
+ { 'E', 2, 10 },
+ { 'C', 0, 15 },
+ { 'C', 1, 16 },
+ { 'C', 2, 17 },
+ { 'C', 3, 18 },
+ { 'D', 0, 19 },
+ { 'D', 1, 20 },
+ { 'D', 2, 21 },
+ { 'D', 3, 22 },
+ { 'C', 4, 23 },
+ { 'C', 5, 24 },
+ { 'C', 6, 25 },
+ { 'C', 7, 26 },
+ { 'D', 4, 27 },
+ { 'D', 5, 28 },
+ { 'D', 6, 29 },
+ { 'D', 7, 30 },
+ { 'B', 0, 33 },
+ { 'B', 1, 34 },
+ { 'B', 2, 35 },
+ { 'B', 3, 36 },
+ { 'B', 4, 37 },
+ { 'B', 5, 38 },
+ { 'B', 6, 39 },
+ { 'B', 7, 40 },
+};
+
+McuAdcPinInfo Pic16f877AdcPinInfo[] = {
+ { 2, 0x00 },
+ { 3, 0x01 },
+ { 4, 0x02 },
+ { 5, 0x03 },
+ { 7, 0x04 },
+ { 8, 0x05 },
+ { 9, 0x06 },
+ { 10, 0x07 },
+};
+
+
+//-----------------------------------------------------------------------------
+// PIC16F876
+
+McuIoPinInfo Pic16f876IoPinInfo[] = {
+ { 'A', 0, 2 },
+ { 'A', 1, 3 },
+ { 'A', 2, 4 },
+ { 'A', 3, 5 },
+ { 'A', 4, 6 },
+ { 'A', 5, 7 },
+ { 'C', 0, 11 },
+ { 'C', 1, 12 },
+ { 'C', 2, 13 },
+ { 'C', 3, 14 },
+ { 'C', 4, 15 },
+ { 'C', 5, 16 },
+ { 'C', 6, 17 },
+ { 'C', 7, 18 },
+ { 'B', 0, 21 },
+ { 'B', 1, 22 },
+ { 'B', 2, 23 },
+ { 'B', 3, 24 },
+ { 'B', 4, 25 },
+ { 'B', 5, 26 },
+ { 'B', 6, 27 },
+ { 'B', 7, 28 },
+};
+
+McuAdcPinInfo Pic16f876AdcPinInfo[] = {
+ { 2, 0x00 },
+ { 3, 0x01 },
+ { 4, 0x02 },
+ { 5, 0x03 },
+ { 7, 0x04 }
+};
+
+
+//-----------------------------------------------------------------------------
+// PIC16F887
+
+McuIoPinInfo Pic16f887IoPinInfo[] = {
+ { 'A', 0, 2 },
+ { 'A', 1, 3 },
+ { 'A', 2, 4 },
+ { 'A', 3, 5 },
+ { 'A', 4, 6 },
+ { 'A', 5, 7 },
+ { 'E', 0, 8 },
+ { 'E', 1, 9 },
+ { 'E', 2, 10 },
+ { 'C', 0, 15 },
+ { 'C', 1, 16 },
+ { 'C', 2, 17 },
+ { 'C', 3, 18 },
+ { 'D', 0, 19 },
+ { 'D', 1, 20 },
+ { 'D', 2, 21 },
+ { 'D', 3, 22 },
+ { 'C', 4, 23 },
+ { 'C', 5, 24 },
+ { 'C', 6, 25 },
+ { 'C', 7, 26 },
+ { 'D', 4, 27 },
+ { 'D', 5, 28 },
+ { 'D', 6, 29 },
+ { 'D', 7, 30 },
+ { 'B', 0, 33 },
+ { 'B', 1, 34 },
+ { 'B', 2, 35 },
+ { 'B', 3, 36 },
+ { 'B', 4, 37 },
+ { 'B', 5, 38 },
+ { 'B', 6, 39 },
+ { 'B', 7, 40 },
+};
+
+McuAdcPinInfo Pic16f887AdcPinInfo[] = {
+ { 2, 0x00 },
+ { 3, 0x01 },
+ { 4, 0x02 },
+ { 5, 0x03 },
+ { 7, 0x04 },
+ { 8, 0x05 },
+ { 9, 0x06 },
+ { 10, 0x07 },
+ { 33, 0x0c },
+ { 34, 0x0a },
+ { 35, 0x08 },
+ { 36, 0x09 },
+ { 37, 0x0b },
+ { 38, 0x0d },
+};
+
+
+//-----------------------------------------------------------------------------
+// PIC16F886
+
+McuIoPinInfo Pic16f886IoPinInfo[] = {
+ { 'A', 0, 2 },
+ { 'A', 1, 3 },
+ { 'A', 2, 4 },
+ { 'A', 3, 5 },
+ { 'A', 4, 6 },
+ { 'A', 5, 7 },
+ { 'C', 0, 11 },
+ { 'C', 1, 12 },
+ { 'C', 2, 13 },
+ { 'C', 3, 14 },
+ { 'C', 4, 15 },
+ { 'C', 5, 16 },
+ { 'C', 6, 17 },
+ { 'C', 7, 18 },
+ { 'B', 0, 21 },
+ { 'B', 1, 22 },
+ { 'B', 2, 23 },
+ { 'B', 3, 24 },
+ { 'B', 4, 25 },
+ { 'B', 5, 26 },
+ { 'B', 6, 27 },
+ { 'B', 7, 28 },
+};
+
+McuAdcPinInfo Pic16f886AdcPinInfo[] = {
+ { 2, 0x00 },
+ { 3, 0x01 },
+ { 4, 0x02 },
+ { 5, 0x03 },
+ { 7, 0x04 },
+ { 21, 0x0c },
+ { 22, 0x0a },
+ { 23, 0x08 },
+ { 24, 0x09 },
+ { 25, 0x0b },
+ { 26, 0x0d },
+};
+
+
+#define arraylen(x) (sizeof(x)/sizeof((x)[0]))
+
+McuIoInfo SupportedMcus[NUM_SUPPORTED_MCUS] = {
+ {
+ "Atmel AVR ATmega128 64-TQFP",
+ 'P',
+ { 0x39, 0x36, 0x33, 0x30, 0x21, 0x20, 0x63 }, // PINx
+ { 0x3b, 0x38, 0x35, 0x32, 0x23, 0x62, 0x65 }, // PORTx
+ { 0x3a, 0x37, 0x34, 0x31, 0x22, 0x61, 0x64 }, // DDRx
+ 64*1024,
+ { { 0x100, 4096 } },
+ AvrAtmega128_64TQFPIoPinInfo,
+ arraylen(AvrAtmega128_64TQFPIoPinInfo),
+ AvrAtmega128_64TQFPAdcPinInfo,
+ arraylen(AvrAtmega128_64TQFPAdcPinInfo),
+ 1023,
+ { 27, 28 },
+ 17,
+ ISA_AVR,
+ TRUE,
+ 0
+ },
+ {
+ "Atmel AVR ATmega64 64-TQFP",
+ 'P',
+ { 0x39, 0x36, 0x33, 0x30, 0x21, 0x20, 0x63 }, // PINx
+ { 0x3b, 0x38, 0x35, 0x32, 0x23, 0x62, 0x65 }, // PORTx
+ { 0x3a, 0x37, 0x34, 0x31, 0x22, 0x61, 0x64 }, // DDRx
+ 32*1024,
+ { { 0x100, 4096 } },
+ AvrAtmega128_64TQFPIoPinInfo,
+ arraylen(AvrAtmega128_64TQFPIoPinInfo),
+ AvrAtmega128_64TQFPAdcPinInfo,
+ arraylen(AvrAtmega128_64TQFPAdcPinInfo),
+ 1023,
+ { 27, 28 },
+ 17,
+ ISA_AVR,
+ TRUE,
+ 0
+ },
+ {
+ "Atmel AVR ATmega162 40-PDIP",
+ 'P',
+ { 0x39, 0x36, 0x33, 0x30, 0x25 }, // PINx
+ { 0x3b, 0x38, 0x35, 0x32, 0x27 }, // PORTx
+ { 0x3a, 0x37, 0x34, 0x31, 0x26 }, // DDRx
+ 8*1024,
+ { { 0x100, 1024 } },
+ AvrAtmega162IoPinInfo,
+ arraylen(AvrAtmega162IoPinInfo),
+ NULL,
+ 0,
+ 0,
+ { 0, 0 },
+ 0,
+ ISA_AVR,
+ TRUE,
+ 0
+ },
+ {
+ "Atmel AVR ATmega32 40-PDIP",
+ 'P',
+ { 0x39, 0x36, 0x33, 0x30 }, // PINx
+ { 0x3b, 0x38, 0x35, 0x32 }, // PORTx
+ { 0x3a, 0x37, 0x34, 0x31 }, // DDRx
+ 16*1024,
+ { { 0x60, 2048 } },
+ AvrAtmega16or32IoPinInfo,
+ arraylen(AvrAtmega16or32IoPinInfo),
+ AvrAtmega16or32AdcPinInfo,
+ arraylen(AvrAtmega16or32AdcPinInfo),
+ 1023,
+ { 14, 15 },
+ 0,
+ ISA_AVR,
+ TRUE,
+ 0
+ },
+ {
+ "Atmel AVR ATmega16 40-PDIP",
+ 'P',
+ { 0x39, 0x36, 0x33, 0x30 }, // PINx
+ { 0x3b, 0x38, 0x35, 0x32 }, // PORTx
+ { 0x3a, 0x37, 0x34, 0x31 }, // DDRx
+ 8*1024,
+ { { 0x60, 1024 } },
+ AvrAtmega16or32IoPinInfo,
+ arraylen(AvrAtmega16or32IoPinInfo),
+ AvrAtmega16or32AdcPinInfo,
+ arraylen(AvrAtmega16or32AdcPinInfo),
+ 1023,
+ { 14, 15 },
+ 21,
+ ISA_AVR,
+ TRUE,
+ 0
+ },
+ {
+ "Atmel AVR ATmega8 28-PDIP",
+ 'P',
+ { 0xff, 0x36, 0x33, 0x30 }, // PINx (but there is no xxxA)
+ { 0xff, 0x38, 0x35, 0x32 }, // PORTx
+ { 0xff, 0x37, 0x34, 0x31 }, // DDRx
+ 4*1024,
+ { { 0x60, 1024 } },
+ AvrAtmega8IoPinInfo,
+ arraylen(AvrAtmega8IoPinInfo),
+ AvrAtmega8AdcPinInfo,
+ arraylen(AvrAtmega8AdcPinInfo),
+ 1023,
+ { 2, 3 },
+ 17,
+ ISA_AVR,
+ TRUE,
+ 0
+ },
+ {
+ "Atmel AVR ATmega328 28-PDIP",
+ 'P',
+ { 0xff, 0x23, 0x26, 0x29 }, // PINx (but there is no xxxA)
+ { 0xff, 0x25, 0x28, 0x2B }, // PORTx
+ { 0xff, 0x24, 0x27, 0x2A }, // DDRx
+ 16*1024,
+ { { 0x100, 1024 } },
+ AvrAtmega328_28PDIPIoPinInfo,
+ arraylen(AvrAtmega328_28PDIPIoPinInfo),
+ AvrAtmega328_28PDIPAdcPinInfo,
+ arraylen(AvrAtmega328_28PDIPAdcPinInfo),
+ 1023,
+ { 2, 3 },
+ 17,
+ ISA_ARDUINO,
+ TRUE,
+ 0
+ },
+ {
+ "Microchip PIC16F628 18-PDIP or 18-SOIC",
+ 'R',
+ { 0x05, 0x06 }, // PORTx
+ { 0x05, 0x06 }, // PORTx
+ { 0x85, 0x86 }, // TRISx
+ 2048,
+ { { 0x20, 96 }, { 0xa0, 80 }, { 0x120, 48 } },
+ Pic18PinIoInfo,
+ arraylen(Pic18PinIoInfo),
+ NULL,
+ 0,
+ 0,
+ { 7, 8 },
+ 0,
+ ISA_PIC16,
+ FALSE,
+ // code protection off, data code protection off, LVP disabled,
+ // BOD reset enabled, RA5/nMCLR is RA5, PWRT enabled, WDT disabled,
+ // HS oscillator
+ 0x3f62
+ },
+ {
+ "Microchip PIC16F88 18-PDIP or 18-SOIC",
+ 'R',
+ { 0x05, 0x06 }, // PORTx
+ { 0x05, 0x06 }, // PORTx
+ { 0x85, 0x86 }, // TRISx
+ 4096,
+ { { 0x20, 96 }, { 0xa0, 80 }, { 0x120, 48 } },
+ Pic18PinIoInfo,
+ arraylen(Pic18PinIoInfo),
+ Pic16f88AdcPinInfo,
+ arraylen(Pic16f88AdcPinInfo),
+ 1023,
+ { 8, 11 },
+ 0,
+ ISA_PIC16,
+ FALSE,
+ (1 << 13) | // CP off
+ (1 << 12) | // CCP on RB2 (doesn't matter)
+ (1 << 11) | // ICD disabled
+ (3 << 9) | // flash write protection off
+ (1 << 8) | // code protection off
+ (0 << 7) | // LVP disabled
+ (1 << 6) | // BOR enabled
+ (0 << 5) | // RA5/nMCLR is RA5
+ (0 << 4) | // for osc sel, later
+ (0 << 3) | // PWRT enabled
+ (0 << 2) | // WDT disabled
+ (2 << 0), // HS oscillator
+ },
+ {
+ "Microchip PIC16F819 18-PDIP or 18-SOIC",
+ 'R',
+ { 0x05, 0x06 }, // PORTx
+ { 0x05, 0x06 }, // PORTx
+ { 0x85, 0x86 }, // TRISx
+ 2048,
+ { { 0x20, 96 } },
+ Pic18PinIoInfo,
+ arraylen(Pic18PinIoInfo),
+ Pic16f819AdcPinInfo,
+ arraylen(Pic16f819AdcPinInfo),
+ 1023,
+ { 0, 0 },
+ 0,
+ ISA_PIC16,
+ FALSE,
+ (1 << 13) | // code protect off
+ (1 << 12) | // CCP1 on RB2 (doesn't matter, can't use)
+ (1 << 11) | // disable debugger
+ (3 << 9) | // flash protection off
+ (1 << 8) | // data protect off
+ (0 << 7) | // LVP disabled
+ (1 << 6) | // BOR enabled
+ (0 << 5) | // nMCLR/RA5 is RA5
+ (0 << 3) | // PWRTE enabled
+ (0 << 2) | // WDT disabled
+ (2 << 0), // HS oscillator
+ },
+ {
+ "Microchip PIC16F877 40-PDIP",
+ 'R',
+ { 0x05, 0x06, 0x07, 0x08, 0x09 }, // PORTx
+ { 0x05, 0x06, 0x07, 0x08, 0x09 }, // PORTx
+ { 0x85, 0x86, 0x87, 0x88, 0x89 }, // TRISx
+ 8*1024,
+ { { 0x20, 96 }, { 0xa0, 80 }, { 0x110, 96 }, { 0x190, 96 } },
+ Pic16f877IoPinInfo,
+ arraylen(Pic16f877IoPinInfo),
+ Pic16f877AdcPinInfo,
+ arraylen(Pic16f877AdcPinInfo),
+ 1023,
+ { 26, 25 },
+ 16,
+ ISA_PIC16,
+ FALSE,
+ // code protection off, debug off, flash write off, EE code protection
+ // off, LVP disabled, BOD enabled, CP off, PWRT enabled, WDT disabled,
+ // HS oscillator
+ 0x3f72
+ },
+ {
+ "Microchip PIC16F876 28-PDIP or 28-SOIC",
+ 'R',
+ { 0x05, 0x06, 0x07 }, // PORTx
+ { 0x05, 0x06, 0x07 }, // PORTx
+ { 0x85, 0x86, 0x87 }, // TRISx
+ 8*1024,
+ { { 0x20, 96 }, { 0xa0, 80 }, { 0x110, 96 }, { 0x190, 96 } },
+ Pic16f876IoPinInfo,
+ arraylen(Pic16f876IoPinInfo),
+ Pic16f876AdcPinInfo,
+ arraylen(Pic16f876AdcPinInfo),
+ 1023,
+ { 18, 17 },
+ 12,
+ ISA_PIC16,
+ FALSE,
+ // code protection off, debug off, flash write off, EE code protection
+ // off, LVP disabled, BOD enabled, CP off, PWRT enabled, WDT disabled,
+ // HS oscillator
+ 0x3f72
+ },
+ {
+ "Microchip PIC16F887 40-PDIP",
+ 'R',
+ { 0x05, 0x06, 0x07, 0x08, 0x09 }, // PORTx
+ { 0x05, 0x06, 0x07, 0x08, 0x09 }, // PORTx
+ { 0x85, 0x86, 0x87, 0x88, 0x89 }, // TRISx
+ 8*1024,
+ { { 0x20, 96 }, { 0xa0, 80 }, { 0x120, 80 }, { 0x1a0, 80 } },
+ Pic16f887IoPinInfo,
+ arraylen(Pic16f887IoPinInfo),
+ Pic16f887AdcPinInfo,
+ arraylen(Pic16f887AdcPinInfo),
+ 1023,
+ { 26, 25 },
+ 16,
+ ISA_PIC16,
+ FALSE,
+ (3 << (9+16)) | // flash write protection off
+ (0 << (8+16)) | // BOR at 2.1 V
+ (1 << 13) | // ICD disabled
+ (0 << 12) | // LVP disabled
+ (0 << 11) | // fail-safe clock monitor disabled
+ (0 << 10) | // internal/external switchover disabled
+ (3 << 8) | // brown-out detect enabled
+ (1 << 7) | // data code protection disabled
+ (1 << 6) | // code protection disabled
+ (1 << 5) | // nMCLR enabled
+ (0 << 4) | // PWRTE enabled
+ (0 << 3) | // WDTE disabled
+ (2 << 0) // HS oscillator
+
+ },
+ {
+ "Microchip PIC16F886 28-PDIP or 28-SOIC",
+ 'R',
+ { 0x05, 0x06, 0x07, 0x08, 0x09 }, // PORTx
+ { 0x05, 0x06, 0x07, 0x08, 0x09 }, // PORTx
+ { 0x85, 0x86, 0x87, 0x88, 0x89 }, // TRISx
+ 8*1024,
+ { { 0x20, 96 }, { 0xa0, 80 }, { 0x120, 80 }, { 0x1a0, 80 } },
+ Pic16f886IoPinInfo,
+ arraylen(Pic16f886IoPinInfo),
+ Pic16f886AdcPinInfo,
+ arraylen(Pic16f886AdcPinInfo),
+ 1023,
+ { 18, 17 },
+ 12,
+ ISA_PIC16,
+ FALSE,
+ (3 << (9+16)) | // flash write protection off
+ (0 << (8+16)) | // BOR at 2.1 V
+ (1 << 13) | // ICD disabled
+ (0 << 12) | // LVP disabled
+ (0 << 11) | // fail-safe clock monitor disabled
+ (0 << 10) | // internal/external switchover disabled
+ (3 << 8) | // brown-out detect enabled
+ (1 << 7) | // data code protection disabled
+ (1 << 6) | // code protection disabled
+ (1 << 5) | // nMCLR enabled
+ (0 << 4) | // PWRTE enabled
+ (0 << 3) | // WDTE disabled
+ (2 << 0) // HS oscillator
+ },
+ {
+ "ANSI C Code",
+ 'x',
+ { 0x00 },
+ { 0x00 },
+ { 0x00 },
+ 0,
+ { { 0x00, 0 } },
+ NULL,
+ 0,
+ NULL,
+ 0,
+ 0,
+ { 0, 0 },
+ 0,
+ ISA_ANSIC,
+ FALSE,
+ 0x00
+ },
+ {
+ "Interpretable Byte Code",
+ 'x',
+ { 0x00 },
+ { 0x00 },
+ { 0x00 },
+ 0,
+ { { 0x00, 0 } },
+ NULL,
+ 0,
+ NULL,
+ 0,
+ 0,
+ { 0, 0 },
+ 0,
+ ISA_INTERPRETED,
+ FALSE,
+ 0x00
+ }
+};
+
diff --git a/ldmicro/mcutablenw.h b/ldmicro/mcutablenw.h
new file mode 100644
index 0000000..119a3e4
--- /dev/null
+++ b/ldmicro/mcutablenw.h
@@ -0,0 +1,862 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// The table of supported MCUs, used to determine where the IOs are, what
+// instruction set, what init code, etc.
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// ATmega128 or ATmega64
+
+McuIoPinInfo AvrAtmega128_64TQFPIoPinInfo[] = {
+ { 'E', 0, 2 },
+ { 'E', 1, 3 },
+ { 'E', 2, 4 },
+ { 'E', 3, 5 },
+ { 'E', 4, 6 },
+ { 'E', 5, 7 },
+ { 'E', 6, 8 },
+ { 'E', 7, 9 },
+
+ { 'B', 0, 10 },
+ { 'B', 1, 11 },
+ { 'B', 2, 12 },
+ { 'B', 3, 13 },
+ { 'B', 4, 14 },
+ { 'B', 5, 15 },
+ { 'B', 6, 16 },
+ { 'B', 7, 17 },
+
+ { 'G', 3, 18 },
+ { 'G', 4, 19 },
+
+ { 'D', 0, 25 },
+ { 'D', 1, 26 },
+ { 'D', 2, 27 },
+ { 'D', 3, 28 },
+ { 'D', 4, 29 },
+ { 'D', 5, 30 },
+ { 'D', 6, 31 },
+ { 'D', 7, 32 },
+
+ { 'G', 0, 33 },
+ { 'G', 1, 34 },
+
+ { 'C', 0, 35 },
+ { 'C', 1, 36 },
+ { 'C', 2, 37 },
+ { 'C', 3, 38 },
+ { 'C', 4, 39 },
+ { 'C', 5, 40 },
+ { 'C', 6, 41 },
+ { 'C', 7, 42 },
+
+ { 'G', 2, 43 },
+
+ { 'A', 7, 44 },
+ { 'A', 6, 45 },
+ { 'A', 5, 46 },
+ { 'A', 4, 47 },
+ { 'A', 3, 48 },
+ { 'A', 2, 49 },
+ { 'A', 1, 50 },
+ { 'A', 0, 51 },
+
+ { 'F', 7, 54 },
+ { 'F', 6, 55 },
+ { 'F', 5, 56 },
+ { 'F', 4, 57 },
+ { 'F', 3, 58 },
+ { 'F', 2, 59 },
+ { 'F', 1, 60 },
+ { 'F', 0, 61 },
+};
+
+McuAdcPinInfo AvrAtmega128_64TQFPAdcPinInfo[] = {
+ { 61, 0x00 },
+ { 60, 0x01 },
+ { 59, 0x02 },
+ { 58, 0x03 },
+ { 57, 0x04 },
+ { 56, 0x05 },
+ { 55, 0x06 },
+ { 54, 0x07 },
+};
+
+
+//-----------------------------------------------------------------------------
+// ATmega162
+
+McuIoPinInfo AvrAtmega162IoPinInfo[] = {
+ { 'B', 0, 1 },
+ { 'B', 1, 2 },
+ { 'B', 2, 3 },
+ { 'B', 3, 4 },
+ { 'B', 4, 5 },
+ { 'B', 5, 6 },
+ { 'B', 6, 7 },
+ { 'B', 7, 8 },
+ { 'D', 0, 10 },
+ { 'D', 1, 11 },
+ { 'D', 2, 12 },
+ { 'D', 3, 13 },
+ { 'D', 4, 14 },
+ { 'D', 5, 15 },
+ { 'D', 6, 16 },
+ { 'D', 7, 17 },
+ { 'C', 0, 21 },
+ { 'C', 1, 22 },
+ { 'C', 2, 23 },
+ { 'C', 3, 24 },
+ { 'C', 4, 25 },
+ { 'C', 5, 26 },
+ { 'C', 6, 27 },
+ { 'C', 7, 28 },
+ { 'E', 2, 29 },
+ { 'E', 1, 30 },
+ { 'E', 0, 31 },
+ { 'A', 7, 32 },
+ { 'A', 6, 33 },
+ { 'A', 5, 34 },
+ { 'A', 4, 35 },
+ { 'A', 3, 36 },
+ { 'A', 2, 37 },
+ { 'A', 1, 38 },
+ { 'A', 0, 39 },
+};
+
+
+//-----------------------------------------------------------------------------
+// ATmega16 or ATmega32
+
+McuIoPinInfo AvrAtmega16or32IoPinInfo[] = {
+ { 'B', 0, 1 },
+ { 'B', 1, 2 },
+ { 'B', 2, 3 },
+ { 'B', 3, 4 },
+ { 'B', 4, 5 },
+ { 'B', 5, 6 },
+ { 'B', 6, 7 },
+ { 'B', 7, 8 },
+ { 'D', 0, 14 },
+ { 'D', 1, 15 },
+ { 'D', 2, 16 },
+ { 'D', 3, 17 },
+ { 'D', 4, 18 },
+ { 'D', 5, 19 },
+ { 'D', 6, 20 },
+ { 'D', 7, 21 },
+ { 'C', 0, 22 },
+ { 'C', 1, 23 },
+ { 'C', 2, 24 },
+ { 'C', 3, 25 },
+ { 'C', 4, 26 },
+ { 'C', 5, 27 },
+ { 'C', 6, 28 },
+ { 'C', 7, 29 },
+ { 'A', 7, 33 },
+ { 'A', 6, 34 },
+ { 'A', 5, 35 },
+ { 'A', 4, 36 },
+ { 'A', 3, 37 },
+ { 'A', 2, 38 },
+ { 'A', 1, 39 },
+ { 'A', 0, 40 },
+};
+
+McuAdcPinInfo AvrAtmega16or32AdcPinInfo[] = {
+ { 40, 0x00 },
+ { 39, 0x01 },
+ { 38, 0x02 },
+ { 37, 0x03 },
+ { 36, 0x04 },
+ { 35, 0x05 },
+ { 34, 0x06 },
+ { 33, 0x07 },
+};
+
+
+//-----------------------------------------------------------------------------
+// ATmega8
+
+McuIoPinInfo AvrAtmega8IoPinInfo[] = {
+ { 'D', 0, 2 },
+ { 'D', 1, 3 },
+ { 'D', 2, 4 },
+ { 'D', 3, 5 },
+ { 'D', 4, 6 },
+ { 'D', 5, 11 },
+ { 'D', 6, 12 },
+ { 'D', 7, 13 },
+ { 'B', 0, 14 },
+ { 'B', 1, 15 },
+ { 'B', 2, 16 },
+ { 'B', 3, 17 },
+ { 'B', 4, 18 },
+ { 'B', 5, 19 },
+ { 'C', 0, 23 },
+ { 'C', 1, 24 },
+ { 'C', 2, 25 },
+ { 'C', 3, 26 },
+ { 'C', 4, 27 },
+ { 'C', 5, 28 },
+};
+
+McuAdcPinInfo AvrAtmega8AdcPinInfo[] = {
+ { 23, 0x00 }, // ADC0
+ { 24, 0x01 },
+ { 25, 0x02 },
+ { 26, 0x03 },
+ { 27, 0x04 },
+ { 28, 0x05 }, // ADC5
+};
+
+
+// ATmega328
+
+McuIoPinInfo AvrAtmega328IoPinInfo[] = {
+ { 'D', 0, 2 },
+ { 'D', 1, 3 },
+ { 'D', 2, 4 },
+ { 'D', 3, 5 },
+ { 'D', 4, 6 },
+ { 'D', 5, 11 },
+ { 'D', 6, 12 },
+ { 'D', 7, 13 },
+ { 'B', 0, 14 },
+ { 'B', 1, 15 },
+ { 'B', 2, 16 },
+ { 'B', 3, 17 },
+ { 'B', 4, 18 },
+ { 'B', 5, 19 },
+ { 'C', 0, 23 },
+ { 'C', 1, 24 },
+ { 'C', 2, 25 },
+ { 'C', 3, 26 },
+ { 'C', 4, 27 },
+ { 'C', 5, 28 },
+};
+
+McuAdcPinInfo AvrAtmega328AdcPinInfo[] = {
+ { 23, 0x00 }, // ADC0
+ { 24, 0x01 },
+ { 25, 0x02 },
+ { 26, 0x03 },
+ { 27, 0x04 },
+ { 28, 0x05 }, // ADC5
+};
+
+
+
+
+
+
+//-----------------------------------------------------------------------------
+// A variety of 18-pin PICs that share the same digital IO assignment.
+
+McuIoPinInfo Pic18PinIoInfo[] = {
+ { 'A', 2, 1 },
+ { 'A', 3, 2 },
+ { 'A', 4, 3 },
+ { 'A', 5, 4 },
+ { 'B', 0, 6 },
+ { 'B', 1, 7 },
+ { 'B', 2, 8 },
+ { 'B', 3, 9 },
+ { 'B', 4, 10 },
+ { 'B', 5, 11 },
+ { 'B', 6, 12 },
+ { 'B', 7, 13 },
+ { 'A', 6, 15 },
+ { 'A', 7, 16 },
+ { 'A', 0, 17 },
+ { 'A', 1, 18 },
+};
+
+McuAdcPinInfo Pic16f819AdcPinInfo[] = {
+ { 1, 0x02 },
+ { 2, 0x03 },
+ { 3, 0x04 },
+ { 17, 0x00 },
+ { 18, 0x01 },
+};
+
+McuAdcPinInfo Pic16f88AdcPinInfo[] = {
+ { 1, 0x02 },
+ { 2, 0x03 },
+ { 3, 0x04 },
+ { 12, 0x05 },
+ { 13, 0x06 },
+ { 17, 0x00 },
+ { 18, 0x01 },
+};
+
+
+//-----------------------------------------------------------------------------
+// PIC16F877
+
+McuIoPinInfo Pic16f877IoPinInfo[] = {
+ { 'A', 0, 2 },
+ { 'A', 1, 3 },
+ { 'A', 2, 4 },
+ { 'A', 3, 5 },
+ { 'A', 4, 6 },
+ { 'A', 5, 7 },
+ { 'E', 0, 8 },
+ { 'E', 1, 9 },
+ { 'E', 2, 10 },
+ { 'C', 0, 15 },
+ { 'C', 1, 16 },
+ { 'C', 2, 17 },
+ { 'C', 3, 18 },
+ { 'D', 0, 19 },
+ { 'D', 1, 20 },
+ { 'D', 2, 21 },
+ { 'D', 3, 22 },
+ { 'C', 4, 23 },
+ { 'C', 5, 24 },
+ { 'C', 6, 25 },
+ { 'C', 7, 26 },
+ { 'D', 4, 27 },
+ { 'D', 5, 28 },
+ { 'D', 6, 29 },
+ { 'D', 7, 30 },
+ { 'B', 0, 33 },
+ { 'B', 1, 34 },
+ { 'B', 2, 35 },
+ { 'B', 3, 36 },
+ { 'B', 4, 37 },
+ { 'B', 5, 38 },
+ { 'B', 6, 39 },
+ { 'B', 7, 40 },
+};
+
+McuAdcPinInfo Pic16f877AdcPinInfo[] = {
+ { 2, 0x00 },
+ { 3, 0x01 },
+ { 4, 0x02 },
+ { 5, 0x03 },
+ { 7, 0x04 },
+ { 8, 0x05 },
+ { 9, 0x06 },
+ { 10, 0x07 },
+};
+
+
+//-----------------------------------------------------------------------------
+// PIC16F876
+
+McuIoPinInfo Pic16f876IoPinInfo[] = {
+ { 'A', 0, 2 },
+ { 'A', 1, 3 },
+ { 'A', 2, 4 },
+ { 'A', 3, 5 },
+ { 'A', 4, 6 },
+ { 'A', 5, 7 },
+ { 'C', 0, 11 },
+ { 'C', 1, 12 },
+ { 'C', 2, 13 },
+ { 'C', 3, 14 },
+ { 'C', 4, 15 },
+ { 'C', 5, 16 },
+ { 'C', 6, 17 },
+ { 'C', 7, 18 },
+ { 'B', 0, 21 },
+ { 'B', 1, 22 },
+ { 'B', 2, 23 },
+ { 'B', 3, 24 },
+ { 'B', 4, 25 },
+ { 'B', 5, 26 },
+ { 'B', 6, 27 },
+ { 'B', 7, 28 },
+};
+
+McuAdcPinInfo Pic16f876AdcPinInfo[] = {
+ { 2, 0x00 },
+ { 3, 0x01 },
+ { 4, 0x02 },
+ { 5, 0x03 },
+ { 7, 0x04 }
+};
+
+
+//-----------------------------------------------------------------------------
+// PIC16F887
+
+McuIoPinInfo Pic16f887IoPinInfo[] = {
+ { 'A', 0, 2 },
+ { 'A', 1, 3 },
+ { 'A', 2, 4 },
+ { 'A', 3, 5 },
+ { 'A', 4, 6 },
+ { 'A', 5, 7 },
+ { 'E', 0, 8 },
+ { 'E', 1, 9 },
+ { 'E', 2, 10 },
+ { 'C', 0, 15 },
+ { 'C', 1, 16 },
+ { 'C', 2, 17 },
+ { 'C', 3, 18 },
+ { 'D', 0, 19 },
+ { 'D', 1, 20 },
+ { 'D', 2, 21 },
+ { 'D', 3, 22 },
+ { 'C', 4, 23 },
+ { 'C', 5, 24 },
+ { 'C', 6, 25 },
+ { 'C', 7, 26 },
+ { 'D', 4, 27 },
+ { 'D', 5, 28 },
+ { 'D', 6, 29 },
+ { 'D', 7, 30 },
+ { 'B', 0, 33 },
+ { 'B', 1, 34 },
+ { 'B', 2, 35 },
+ { 'B', 3, 36 },
+ { 'B', 4, 37 },
+ { 'B', 5, 38 },
+ { 'B', 6, 39 },
+ { 'B', 7, 40 },
+};
+
+McuAdcPinInfo Pic16f887AdcPinInfo[] = {
+ { 2, 0x00 },
+ { 3, 0x01 },
+ { 4, 0x02 },
+ { 5, 0x03 },
+ { 7, 0x04 },
+ { 8, 0x05 },
+ { 9, 0x06 },
+ { 10, 0x07 },
+ { 33, 0x0c },
+ { 34, 0x0a },
+ { 35, 0x08 },
+ { 36, 0x09 },
+ { 37, 0x0b },
+ { 38, 0x0d },
+};
+
+
+//-----------------------------------------------------------------------------
+// PIC16F886
+
+McuIoPinInfo Pic16f886IoPinInfo[] = {
+ { 'A', 0, 2 },
+ { 'A', 1, 3 },
+ { 'A', 2, 4 },
+ { 'A', 3, 5 },
+ { 'A', 4, 6 },
+ { 'A', 5, 7 },
+ { 'C', 0, 11 },
+ { 'C', 1, 12 },
+ { 'C', 2, 13 },
+ { 'C', 3, 14 },
+ { 'C', 4, 15 },
+ { 'C', 5, 16 },
+ { 'C', 6, 17 },
+ { 'C', 7, 18 },
+ { 'B', 0, 21 },
+ { 'B', 1, 22 },
+ { 'B', 2, 23 },
+ { 'B', 3, 24 },
+ { 'B', 4, 25 },
+ { 'B', 5, 26 },
+ { 'B', 6, 27 },
+ { 'B', 7, 28 },
+};
+
+McuAdcPinInfo Pic16f886AdcPinInfo[] = {
+ { 2, 0x00 },
+ { 3, 0x01 },
+ { 4, 0x02 },
+ { 5, 0x03 },
+ { 7, 0x04 },
+ { 21, 0x0c },
+ { 22, 0x0a },
+ { 23, 0x08 },
+ { 24, 0x09 },
+ { 25, 0x0b },
+ { 26, 0x0d },
+};
+
+
+#define arraylen(x) (sizeof(x)/sizeof((x)[0]))
+
+McuIoInfo SupportedMcus[NUM_SUPPORTED_MCUS] = {
+ {
+ "Atmel AVR ATmega128 64-TQFP",
+ 'P',
+ { 0x39, 0x36, 0x33, 0x30, 0x21, 0x20, 0x63 }, // PINx
+ { 0x3b, 0x38, 0x35, 0x32, 0x23, 0x62, 0x65 }, // PORTx
+ { 0x3a, 0x37, 0x34, 0x31, 0x22, 0x61, 0x64 }, // DDRx
+ 64*1024,
+ { { 0x100, 4096 } },//SRAM(start address,size)
+ AvrAtmega128_64TQFPIoPinInfo,
+ arraylen(AvrAtmega128_64TQFPIoPinInfo),
+ AvrAtmega128_64TQFPAdcPinInfo,
+ arraylen(AvrAtmega128_64TQFPAdcPinInfo),
+ 1023,
+ { 27, 28 },
+ 17,
+ ISA_AVR,
+ TRUE,
+ 0
+ },
+ {
+ "Atmel AVR ATmega64 64-TQFP",
+ 'P',
+ { 0x39, 0x36, 0x33, 0x30, 0x21, 0x20, 0x63 }, // PINx
+ { 0x3b, 0x38, 0x35, 0x32, 0x23, 0x62, 0x65 }, // PORTx
+ { 0x3a, 0x37, 0x34, 0x31, 0x22, 0x61, 0x64 }, // DDRx
+ 32*1024,
+ { { 0x100, 4096 } },
+ AvrAtmega128_64TQFPIoPinInfo,
+ arraylen(AvrAtmega128_64TQFPIoPinInfo),
+ AvrAtmega128_64TQFPAdcPinInfo,
+ arraylen(AvrAtmega128_64TQFPAdcPinInfo),
+ 1023,
+ { 27, 28 },
+ 17,
+ ISA_AVR,
+ TRUE,
+ 0
+ },
+ {
+ "Atmel AVR ATmega162 40-PDIP",
+ 'P',
+ { 0x39, 0x36, 0x33, 0x30, 0x25 }, // PINx
+ { 0x3b, 0x38, 0x35, 0x32, 0x27 }, // PORTx
+ { 0x3a, 0x37, 0x34, 0x31, 0x26 }, // DDRx
+ 8*1024,
+ { { 0x100, 1024 } },
+ AvrAtmega162IoPinInfo,
+ arraylen(AvrAtmega162IoPinInfo),
+ NULL,
+ 0,
+ 0,
+ { 0, 0 },
+ 0,
+ ISA_AVR,
+ TRUE,
+ 0
+ },
+ {
+ "Atmel AVR ATmega32 40-PDIP",
+ 'P',
+ { 0x39, 0x36, 0x33, 0x30 }, // PINx
+ { 0x3b, 0x38, 0x35, 0x32 }, // PORTx
+ { 0x3a, 0x37, 0x34, 0x31 }, // DDRx
+ 16*1024,
+ { { 0x60, 2048 } },
+ AvrAtmega16or32IoPinInfo,
+ arraylen(AvrAtmega16or32IoPinInfo),
+ AvrAtmega16or32AdcPinInfo,
+ arraylen(AvrAtmega16or32AdcPinInfo),
+ 1023,
+ { 14, 15 },
+ 0,
+ ISA_AVR,
+ TRUE,
+ 0
+ },
+ {
+ "Atmel AVR ATmega16 40-PDIP",
+ 'P',
+ { 0x39, 0x36, 0x33, 0x30 }, // PINx
+ { 0x3b, 0x38, 0x35, 0x32 }, // PORTx
+ { 0x3a, 0x37, 0x34, 0x31 }, // DDRx
+ 8*1024,
+ { { 0x60, 1024 } },
+ AvrAtmega16or32IoPinInfo,
+ arraylen(AvrAtmega16or32IoPinInfo),
+ AvrAtmega16or32AdcPinInfo,
+ arraylen(AvrAtmega16or32AdcPinInfo),
+ 1023,
+ { 14, 15 },
+ 0,
+ ISA_AVR,
+ TRUE,
+ 0
+ },
+ {
+ "Atmel AVR ATmega8 28-PDIP",
+ 'P',
+ { 0xff, 0x36, 0x33, 0x30 }, // PINx (but there is no xxxA)
+ { 0xff, 0x38, 0x35, 0x32 }, // PORTx
+ { 0xff, 0x37, 0x34, 0x31 }, // DDRx
+ 4*1024,
+ { { 0x60, 1024 } },
+ AvrAtmega8IoPinInfo,
+ arraylen(AvrAtmega8IoPinInfo),
+ AvrAtmega8AdcPinInfo,
+ arraylen(AvrAtmega8AdcPinInfo),
+ 1023,
+ { 2, 3 },
+ 17,
+ ISA_AVR,
+ TRUE,
+ 0
+ },
+ {
+ "Atmel AVR ATmega328 28-PDIP 32kFlash",
+ 'P',
+ { 0xff, 0x23, 0x26, 0x29 }, // PINx (but there is no xxxA)
+ { 0xff, 0x25, 0x28, 0x2B }, // PORTx
+ { 0xff, 0x24, 0x27, 0x2A }, // DDRx
+ 16*1024,
+ { { 0x60, 1024 } },
+ AvrAtmega328IoPinInfo,
+ arraylen(AvrAtmega328IoPinInfo),
+ AvrAtmega328AdcPinInfo,
+ arraylen(AvrAtmega328AdcPinInfo),
+ 1023,
+ { 2, 3 },
+ 17,
+ ISA_AVR,
+ TRUE,
+ 0
+ },
+ {
+ "Microchip PIC16F628 18-PDIP or 18-SOIC",
+ 'R',
+ { 0x05, 0x06 }, // PORTx
+ { 0x05, 0x06 }, // PORTx
+ { 0x85, 0x86 }, // TRISx
+ 2048,
+ { { 0x20, 96 }, { 0xa0, 80 }, { 0x120, 48 } },
+ Pic18PinIoInfo,
+ arraylen(Pic18PinIoInfo),
+ NULL,
+ 0,
+ 0,
+ { 7, 8 },
+ 0,
+ ISA_PIC16,
+ FALSE,
+ // code protection off, data code protection off, LVP disabled,
+ // BOD reset enabled, RA5/nMCLR is RA5, PWRT enabled, WDT disabled,
+ // HS oscillator
+ 0x3f62
+ },
+ {
+ "Microchip PIC16F88 18-PDIP or 18-SOIC",
+ 'R',
+ { 0x05, 0x06 }, // PORTx
+ { 0x05, 0x06 }, // PORTx
+ { 0x85, 0x86 }, // TRISx
+ 4096,
+ { { 0x20, 96 }, { 0xa0, 80 }, { 0x120, 48 } },
+ Pic18PinIoInfo,
+ arraylen(Pic18PinIoInfo),
+ Pic16f88AdcPinInfo,
+ arraylen(Pic16f88AdcPinInfo),
+ 1023,
+ { 8, 11 },
+ 0,
+ ISA_PIC16,
+ FALSE,
+ (1 << 13) | // CP off
+ (1 << 12) | // CCP on RB2 (doesn't matter)
+ (1 << 11) | // ICD disabled
+ (3 << 9) | // flash write protection off
+ (1 << 8) | // code protection off
+ (0 << 7) | // LVP disabled
+ (1 << 6) | // BOR enabled
+ (0 << 5) | // RA5/nMCLR is RA5
+ (0 << 4) | // for osc sel, later
+ (0 << 3) | // PWRT enabled
+ (0 << 2) | // WDT disabled
+ (2 << 0), // HS oscillator
+ },
+ {
+ "Microchip PIC16F819 18-PDIP or 18-SOIC",
+ 'R',
+ { 0x05, 0x06 }, // PORTx
+ { 0x05, 0x06 }, // PORTx
+ { 0x85, 0x86 }, // TRISx
+ 2048,
+ { { 0x20, 96 } },
+ Pic18PinIoInfo,
+ arraylen(Pic18PinIoInfo),
+ Pic16f819AdcPinInfo,
+ arraylen(Pic16f819AdcPinInfo),
+ 1023,
+ { 0, 0 },
+ 0,
+ ISA_PIC16,
+ FALSE,
+ (1 << 13) | // code protect off
+ (1 << 12) | // CCP1 on RB2 (doesn't matter, can't use)
+ (1 << 11) | // disable debugger
+ (3 << 9) | // flash protection off
+ (1 << 8) | // data protect off
+ (0 << 7) | // LVP disabled
+ (1 << 6) | // BOR enabled
+ (0 << 5) | // nMCLR/RA5 is RA5
+ (0 << 3) | // PWRTE enabled
+ (0 << 2) | // WDT disabled
+ (2 << 0), // HS oscillator
+ },
+ {
+ "Microchip PIC16F877 40-PDIP",
+ 'R',
+ { 0x05, 0x06, 0x07, 0x08, 0x09 }, // PORTx
+ { 0x05, 0x06, 0x07, 0x08, 0x09 }, // PORTx
+ { 0x85, 0x86, 0x87, 0x88, 0x89 }, // TRISx
+ 8*1024,
+ { { 0x20, 96 }, { 0xa0, 80 }, { 0x110, 96 }, { 0x190, 96 } },
+ Pic16f877IoPinInfo,
+ arraylen(Pic16f877IoPinInfo),
+ Pic16f877AdcPinInfo,
+ arraylen(Pic16f877AdcPinInfo),
+ 1023,
+ { 26, 25 },
+ 16,
+ ISA_PIC16,
+ FALSE,
+ // code protection off, debug off, flash write off, EE code protection
+ // off, LVP disabled, BOD enabled, CP off, PWRT enabled, WDT disabled,
+ // HS oscillator
+ 0x3f72
+ },
+ {
+ "Microchip PIC16F876 28-PDIP or 28-SOIC",
+ 'R',
+ { 0x05, 0x06, 0x07 }, // PORTx
+ { 0x05, 0x06, 0x07 }, // PORTx
+ { 0x85, 0x86, 0x87 }, // TRISx
+ 8*1024,
+ { { 0x20, 96 }, { 0xa0, 80 }, { 0x110, 96 }, { 0x190, 96 } },
+ Pic16f876IoPinInfo,
+ arraylen(Pic16f876IoPinInfo),
+ Pic16f876AdcPinInfo,
+ arraylen(Pic16f876AdcPinInfo),
+ 1023,
+ { 18, 17 },
+ 12,
+ ISA_PIC16,
+ FALSE,
+ // code protection off, debug off, flash write off, EE code protection
+ // off, LVP disabled, BOD enabled, CP off, PWRT enabled, WDT disabled,
+ // HS oscillator
+ 0x3f72
+ },
+ {
+ "Microchip PIC16F887 40-PDIP",
+ 'R',
+ { 0x05, 0x06, 0x07, 0x08, 0x09 }, // PORTx
+ { 0x05, 0x06, 0x07, 0x08, 0x09 }, // PORTx
+ { 0x85, 0x86, 0x87, 0x88, 0x89 }, // TRISx
+ 8*1024,
+ { { 0x20, 96 }, { 0xa0, 80 }, { 0x120, 80 }, { 0x1a0, 80 } },
+ Pic16f887IoPinInfo,
+ arraylen(Pic16f887IoPinInfo),
+ Pic16f887AdcPinInfo,
+ arraylen(Pic16f887AdcPinInfo),
+ 1023,
+ { 26, 25 },
+ 16,
+ ISA_PIC16,
+ FALSE,
+ (3 << (9+16)) | // flash write protection off
+ (0 << (8+16)) | // BOR at 2.1 V
+ (1 << 13) | // ICD disabled
+ (0 << 12) | // LVP disabled
+ (0 << 11) | // fail-safe clock monitor disabled
+ (0 << 10) | // internal/external switchover disabled
+ (3 << 8) | // brown-out detect enabled
+ (1 << 7) | // data code protection disabled
+ (1 << 6) | // code protection disabled
+ (1 << 5) | // nMCLR enabled
+ (0 << 4) | // PWRTE enabled
+ (0 << 3) | // WDTE disabled
+ (2 << 0) // HS oscillator
+
+ },
+ {
+ "Microchip PIC16F886 28-PDIP or 28-SOIC",
+ 'R',
+ { 0x05, 0x06, 0x07, 0x08, 0x09 }, // PORTx
+ { 0x05, 0x06, 0x07, 0x08, 0x09 }, // PORTx
+ { 0x85, 0x86, 0x87, 0x88, 0x89 }, // TRISx
+ 8*1024,
+ { { 0x20, 96 }, { 0xa0, 80 }, { 0x120, 80 }, { 0x1a0, 80 } },
+ Pic16f886IoPinInfo,
+ arraylen(Pic16f886IoPinInfo),
+ Pic16f886AdcPinInfo,
+ arraylen(Pic16f886AdcPinInfo),
+ 1023,
+ { 18, 17 },
+ 12,
+ ISA_PIC16,
+ FALSE,
+ (3 << (9+16)) | // flash write protection off
+ (0 << (8+16)) | // BOR at 2.1 V
+ (1 << 13) | // ICD disabled
+ (0 << 12) | // LVP disabled
+ (0 << 11) | // fail-safe clock monitor disabled
+ (0 << 10) | // internal/external switchover disabled
+ (3 << 8) | // brown-out detect enabled
+ (1 << 7) | // data code protection disabled
+ (1 << 6) | // code protection disabled
+ (1 << 5) | // nMCLR enabled
+ (0 << 4) | // PWRTE enabled
+ (0 << 3) | // WDTE disabled
+ (2 << 0) // HS oscillator
+ },
+ {
+ "ANSI C Code",
+ 'x',
+ { 0x00 },
+ { 0x00 },
+ { 0x00 },
+ 0,
+ { { 0x00, 0 } },
+ NULL,
+ 0,
+ NULL,
+ 0,
+ 0,
+ { 0, 0 },
+ 0,
+ ISA_ANSIC,
+ FALSE,
+ 0x00
+ },
+ {
+ "Interpretable Byte Code",
+ 'x',
+ { 0x00 },
+ { 0x00 },
+ { 0x00 },
+ 0,
+ { { 0x00, 0 } },
+ NULL,
+ 0,
+ NULL,
+ 0,
+ 0,
+ { 0, 0 },
+ 0,
+ ISA_INTERPRETED,
+ FALSE,
+ 0x00
+ }
+};
diff --git a/ldmicro/miscutil.cpp b/ldmicro/miscutil.cpp
new file mode 100644
index 0000000..724e224
--- /dev/null
+++ b/ldmicro/miscutil.cpp
@@ -0,0 +1,384 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Miscellaneous utility functions that don't fit anywhere else. IHEX writing,
+// verified memory allocator, other junk.
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+// We should display messages to the user differently if we are running
+// interactively vs. in batch (command-line) mode.
+BOOL RunningInBatchMode = FALSE;
+
+// Allocate memory on a local heap
+HANDLE MainHeap;
+
+// Running checksum as we build up IHEX records.
+static int IhexChecksum;
+
+// Try to common a bit of stuff between the dialog boxes, since only one
+// can be open at any time.
+HWND OkButton;
+HWND CancelButton;
+BOOL DialogDone;
+BOOL DialogCancel;
+
+HFONT MyNiceFont;
+HFONT MyFixedFont;
+
+//-----------------------------------------------------------------------------
+// printf-like debug function, to the Windows debug log.
+//-----------------------------------------------------------------------------
+void dbp(char *str, ...)
+{
+ va_list f;
+ char buf[1024];
+ va_start(f, str);
+ vsprintf(buf, str, f);
+ OutputDebugString(buf);
+ OutputDebugString("\n");
+}
+
+//-----------------------------------------------------------------------------
+// Wrapper for AttachConsole that does nothing running under = 0x500
+BOOL AttachConsoleDynamic(DWORD base)
+{
+ typedef BOOL WINAPI fptr_acd(DWORD base);
+ fptr_acd *fp;
+
+ HMODULE hm = LoadLibrary("kernel32.dll");
+ if(!hm) return FALSE;
+
+ fp = (fptr_acd *)GetProcAddress(hm, "AttachConsole");
+ if(!fp) return FALSE;
+
+ return fp(base);
+}
+
+//-----------------------------------------------------------------------------
+// For error messages to the user; printf-like, to a message box.
+//-----------------------------------------------------------------------------
+void Error(char *str, ...)
+{
+ va_list f;
+ char buf[1024];
+ va_start(f, str);
+ vsprintf(buf, str, f);
+ if(RunningInBatchMode) {
+ AttachConsoleDynamic(ATTACH_PARENT_PROCESS);
+ HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
+ DWORD written;
+
+ // Indicate that it's an error, plus the output filename
+ char str[MAX_PATH+100];
+ sprintf(str, "compile error ('%s'): ", CurrentCompileFile);
+ WriteFile(h, str, strlen(str), &written, NULL);
+ // The error message itself
+ WriteFile(h, buf, strlen(buf), &written, NULL);
+ // And an extra newline to be safe.
+ strcpy(str, "\n");
+ WriteFile(h, str, strlen(str), &written, NULL);
+ } else {
+ HWND h = GetForegroundWindow();
+ MessageBox(h, buf, _("LDmicro Error"), MB_OK | MB_ICONERROR);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// A standard format for showing a message that indicates that a compile
+// was successful.
+//-----------------------------------------------------------------------------
+void CompileSuccessfulMessage(char *str)
+{
+ if(RunningInBatchMode) {
+ char str[MAX_PATH+100];
+ sprintf(str, "compiled okay, wrote '%s'\n", CurrentCompileFile);
+
+ AttachConsoleDynamic(ATTACH_PARENT_PROCESS);
+ HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
+ DWORD written;
+ WriteFile(h, str, strlen(str), &written, NULL);
+ } else {
+ MessageBox(MainWindow, str, _("Compile Successful"),
+ MB_OK | MB_ICONINFORMATION);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Check the consistency of the heap on which all the PLC program stuff is
+// stored.
+//-----------------------------------------------------------------------------
+void CheckHeap(char *file, int line)
+{
+ static unsigned int SkippedCalls;
+ static SDWORD LastCallTime;
+ SDWORD now = GetTickCount();
+
+ // It slows us down too much to do the check every time we are called;
+ // but let's still do the check periodically; let's do it every 70
+ // calls or every 20 ms, whichever is sooner.
+ if(SkippedCalls < 70 && (now - LastCallTime) < 20) {
+ SkippedCalls++;
+ return;
+ }
+
+ SkippedCalls = 0;
+ LastCallTime = now;
+
+ if(!HeapValidate(MainHeap, 0, NULL)) {
+ dbp("file %s line %d", file, line);
+ Error("Noticed memory corruption at file '%s' line %d.", file, line);
+ oops();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Like malloc/free, but memsets memory allocated to all zeros. Also TODO some
+// checking and something sensible if it fails.
+//-----------------------------------------------------------------------------
+void *CheckMalloc(size_t n)
+{
+ ok();
+ void *p = HeapAlloc(MainHeap, HEAP_ZERO_MEMORY, n);
+ return p;
+}
+void CheckFree(void *p)
+{
+ ok();
+ HeapFree(MainHeap, 0, p);
+}
+
+
+//-----------------------------------------------------------------------------
+// Clear the checksum and write the : that starts an IHEX record.
+//-----------------------------------------------------------------------------
+void StartIhex(FILE *f)
+{
+ fprintf(f, ":");
+ IhexChecksum = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Write an octet in hex format to the given stream, and update the checksum
+// for the IHEX file.
+//-----------------------------------------------------------------------------
+void WriteIhex(FILE *f, BYTE b)
+{
+ fprintf(f, "%02X", b);
+ IhexChecksum += b;
+}
+
+//-----------------------------------------------------------------------------
+// Write the finished checksum to the IHEX file from the running sum
+// calculated by WriteIhex.
+//-----------------------------------------------------------------------------
+void FinishIhex(FILE *f)
+{
+ IhexChecksum = ~IhexChecksum + 1;
+ IhexChecksum = IhexChecksum & 0xff;
+ fprintf(f, "%02X\n", IhexChecksum);
+}
+
+//-----------------------------------------------------------------------------
+// Create a window with a given client area.
+//-----------------------------------------------------------------------------
+HWND CreateWindowClient(DWORD exStyle, char *className, char *windowName,
+ DWORD style, int x, int y, int width, int height, HWND parent,
+ HMENU menu, HINSTANCE instance, void *param)
+{
+ HWND h = CreateWindowEx(exStyle, className, windowName, style, x, y,
+ width, height, parent, menu, instance, param);
+
+ RECT r;
+ GetClientRect(h, &r);
+ width = width - (r.right - width);
+ height = height - (r.bottom - height);
+
+ SetWindowPos(h, HWND_TOP, x, y, width, height, 0);
+
+ return h;
+}
+
+//-----------------------------------------------------------------------------
+// Window proc for the dialog boxes. This Ok/Cancel stuff is common to a lot
+// of places, and there are no other callbacks from the children.
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (msg) {
+ case WM_NOTIFY:
+ break;
+
+ case WM_COMMAND: {
+ HWND h = (HWND)lParam;
+ if(h == OkButton && wParam == BN_CLICKED) {
+ DialogDone = TRUE;
+ } else if(h == CancelButton && wParam == BN_CLICKED) {
+ DialogDone = TRUE;
+ DialogCancel = TRUE;
+ }
+ break;
+ }
+
+ case WM_CLOSE:
+ case WM_DESTROY:
+ DialogDone = TRUE;
+ DialogCancel = TRUE;
+ break;
+
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Set the font of a control to a pretty proportional font (typ. Tahoma).
+//-----------------------------------------------------------------------------
+void NiceFont(HWND h)
+{
+ SendMessage(h, WM_SETFONT, (WPARAM)MyNiceFont, TRUE);
+}
+
+//-----------------------------------------------------------------------------
+// Set the font of a control to a pretty fixed-width font (typ. Lucida
+// Console).
+//-----------------------------------------------------------------------------
+void FixedFont(HWND h)
+{
+ SendMessage(h, WM_SETFONT, (WPARAM)MyFixedFont, TRUE);
+}
+
+//-----------------------------------------------------------------------------
+// Create our dialog box class, used for most of the popup dialogs.
+//-----------------------------------------------------------------------------
+void MakeDialogBoxClass(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)DialogProc;
+ wc.hInstance = Instance;
+ wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
+ wc.lpszClassName = "LDmicroDialog";
+ wc.lpszMenuName = NULL;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hIcon = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
+ IMAGE_ICON, 32, 32, 0);
+ wc.hIconSm = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
+ IMAGE_ICON, 16, 16, 0);
+
+ RegisterClassEx(&wc);
+
+ MyNiceFont = CreateFont(16, 0, 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
+ ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
+ FF_DONTCARE, "Tahoma");
+ if(!MyNiceFont)
+ MyNiceFont = (HFONT)GetStockObject(SYSTEM_FONT);
+
+ MyFixedFont = 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(!MyFixedFont)
+ MyFixedFont = (HFONT)GetStockObject(SYSTEM_FONT);
+}
+
+//-----------------------------------------------------------------------------
+// Map an I/O type to a string describing it. Used both in the on-screen
+// list and when we write a text file to describe it.
+//-----------------------------------------------------------------------------
+char *IoTypeToString(int ioType)
+{
+ switch(ioType) {
+ case IO_TYPE_DIG_INPUT: return _("digital in");
+ case IO_TYPE_DIG_OUTPUT: return _("digital out");
+ case IO_TYPE_INTERNAL_RELAY: return _("int. relay");
+ case IO_TYPE_UART_TX: return _("UART tx");
+ case IO_TYPE_UART_RX: return _("UART rx");
+ case IO_TYPE_PWM_OUTPUT: return _("PWM out");
+ case IO_TYPE_TON: return _("turn-on delay");
+ case IO_TYPE_TOF: return _("turn-off delay");
+ case IO_TYPE_RTO: return _("retentive timer");
+ case IO_TYPE_COUNTER: return _("counter");
+ case IO_TYPE_GENERAL: return _("general var");
+ case IO_TYPE_READ_ADC: return _("adc input");
+ default: return _("");
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Get a pin number for a given I/O; for digital ins and outs and analog ins,
+// this is easy, but for PWM and UART this is forced (by what peripherals
+// are available) so we look at the characteristics of the MCU that is in
+// use.
+//-----------------------------------------------------------------------------
+void PinNumberForIo(char *dest, PlcProgramSingleIo *io)
+{
+ if(!dest) return;
+
+ if(!io) {
+ strcpy(dest, "");
+ return;
+ }
+
+ int type = io->type;
+ if(type == IO_TYPE_DIG_INPUT || type == IO_TYPE_DIG_OUTPUT
+ || type == IO_TYPE_READ_ADC)
+ {
+ int pin = io->pin;
+ if(pin == NO_PIN_ASSIGNED) {
+ strcpy(dest, _("(not assigned)"));
+ } else {
+ sprintf(dest, "%d", pin);
+ }
+ } else if(type == IO_TYPE_UART_TX && Prog.mcu) {
+ if(Prog.mcu->uartNeeds.txPin == 0) {
+ strcpy(dest, _(""));
+ } else {
+ sprintf(dest, "%d", Prog.mcu->uartNeeds.txPin);
+ }
+ } else if(type == IO_TYPE_UART_RX && Prog.mcu) {
+ if(Prog.mcu->uartNeeds.rxPin == 0) {
+ strcpy(dest, _(""));
+ } else {
+ sprintf(dest, "%d", Prog.mcu->uartNeeds.rxPin);
+ }
+ } else if(type == IO_TYPE_PWM_OUTPUT && Prog.mcu) {
+ if(Prog.mcu->pwmNeedsPin == 0) {
+ strcpy(dest, _(""));
+ } else {
+ sprintf(dest, "%d", Prog.mcu->pwmNeedsPin);
+ }
+ } else {
+ strcpy(dest, "");
+ }
+}
diff --git a/ldmicro/pic16.cpp b/ldmicro/pic16.cpp
new file mode 100644
index 0000000..c44c421
--- /dev/null
+++ b/ldmicro/pic16.cpp
@@ -0,0 +1,1625 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// A PIC16... assembler, for our own internal use, plus routines to generate
+// code from the ladder logic structure, plus routines to generate the
+// runtime needed to schedule the cycles.
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+#include
+#include
+
+#include "ldmicro.h"
+#include "intcode.h"
+
+// not complete; just what I need
+typedef enum Pic16OpTag {
+ OP_VACANT,
+ OP_ADDWF,
+ OP_ANDWF,
+ OP_CALL,
+ OP_BSF,
+ OP_BCF,
+ OP_BTFSC,
+ OP_BTFSS,
+ OP_GOTO,
+ OP_CLRF,
+ OP_CLRWDT,
+ OP_COMF,
+ OP_DECF,
+ OP_DECFSZ,
+ OP_INCF,
+ OP_INCFSZ,
+ OP_IORWF,
+ OP_MOVLW,
+ OP_MOVF,
+ OP_MOVWF,
+ OP_NOP,
+ OP_RETFIE,
+ OP_RETURN,
+ OP_RLF,
+ OP_RRF,
+ OP_SUBLW,
+ OP_SUBWF,
+ OP_XORWF,
+} Pic16Op;
+
+#define DEST_F 1
+#define DEST_W 0
+
+#define STATUS_RP1 6
+#define STATUS_RP0 5
+#define STATUS_Z 2
+#define STATUS_C 0
+
+typedef struct Pic16InstructionTag {
+ Pic16Op op;
+ DWORD arg1;
+ DWORD arg2;
+} Pic16Instruction;
+
+#define MAX_PROGRAM_LEN 128*1024
+static Pic16Instruction PicProg[MAX_PROGRAM_LEN];
+static DWORD PicProgWriteP;
+
+// Scratch variables, for temporaries
+static DWORD Scratch0;
+static DWORD Scratch1;
+static DWORD Scratch2;
+static DWORD Scratch3;
+static DWORD Scratch4;
+static DWORD Scratch5;
+static DWORD Scratch6;
+static DWORD Scratch7;
+
+// The extra byte to program, for the EEPROM (because we can only set
+// up one byte to program at a time, and we will be writing two-byte
+// variables, in general).
+static DWORD EepromHighByte;
+static DWORD EepromHighByteWaitingAddr;
+static int EepromHighByteWaitingBit;
+
+// Subroutines to do multiply/divide
+static DWORD MultiplyRoutineAddress;
+static DWORD DivideRoutineAddress;
+static BOOL MultiplyNeeded;
+static BOOL DivideNeeded;
+
+// For yet unresolved references in jumps
+static DWORD FwdAddrCount;
+
+// As I start to support the paging; it is sometimes necessary to pick
+// out the high vs. low portions of the address, so that the high portion
+// goes in PCLATH, and the low portion is just used for the jump.
+#define FWD_LO(x) ((x) | 0x20000000)
+#define FWD_HI(x) ((x) | 0x40000000)
+
+// Some useful registers, which I think are mostly in the same place on
+// all the PIC16... devices.
+#define REG_INDF 0x00
+#define REG_STATUS 0x03
+#define REG_FSR 0x04
+#define REG_PCLATH 0x0a
+#define REG_INTCON 0x0b
+#define REG_PIR1 0x0c
+#define REG_PIE1 0x8c
+#define REG_TMR1L 0x0e
+#define REG_TMR1H 0x0f
+#define REG_T1CON 0x10
+#define REG_CCPR1L 0x15
+#define REG_CCPR1H 0x16
+#define REG_CCP1CON 0x17
+#define REG_CMCON 0x1f
+
+#define REG_TXSTA 0x98
+#define REG_RCSTA 0x18
+#define REG_SPBRG 0x99
+#define REG_TXREG 0x19
+#define REG_RCREG 0x1a
+
+#define REG_ADRESH 0x1e
+#define REG_ADRESL 0x9e
+#define REG_ADCON0 0x1f
+#define REG_ADCON1 0x9f
+
+#define REG_T2CON 0x12
+#define REG_CCPR2L 0x1b
+#define REG_CCP2CON 0x1d
+#define REG_PR2 0x92
+
+// These move around from device to device.
+static DWORD REG_EECON1;
+static DWORD REG_EECON2;
+static DWORD REG_EEDATA;
+static DWORD REG_EEADR;
+static DWORD REG_ANSEL;
+static DWORD REG_ANSELH;
+
+static int IntPc;
+
+static void CompileFromIntermediate(BOOL topLevel);
+
+//-----------------------------------------------------------------------------
+// A convenience function, whether we are using a particular MCU.
+//-----------------------------------------------------------------------------
+static BOOL McuIs(char *str)
+{
+ return strcmp(Prog.mcu->mcuName, str) == 0;
+}
+
+//-----------------------------------------------------------------------------
+// Wipe the program and set the write pointer back to the beginning.
+//-----------------------------------------------------------------------------
+static void WipeMemory(void)
+{
+ memset(PicProg, 0, sizeof(PicProg));
+ PicProgWriteP = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Store an instruction at the next spot in program memory. Error condition
+// if this spot is already filled. We don't actually assemble to binary yet;
+// there may be references to resolve.
+//-----------------------------------------------------------------------------
+static void Instruction(Pic16Op op, DWORD arg1, DWORD arg2)
+{
+ if(PicProg[PicProgWriteP].op != OP_VACANT) oops();
+
+ PicProg[PicProgWriteP].op = op;
+ PicProg[PicProgWriteP].arg1 = arg1;
+ PicProg[PicProgWriteP].arg2 = arg2;
+ PicProgWriteP++;
+}
+
+//-----------------------------------------------------------------------------
+// Allocate a unique descriptor for a forward reference. Later that forward
+// reference gets assigned to an absolute address, and we can go back and
+// fix up the reference.
+//-----------------------------------------------------------------------------
+static DWORD AllocFwdAddr(void)
+{
+ FwdAddrCount++;
+ return 0x80000000 | FwdAddrCount;
+}
+
+//-----------------------------------------------------------------------------
+// Go back and fix up the program given that the provided forward address
+// corresponds to the next instruction to be assembled.
+//-----------------------------------------------------------------------------
+static void FwdAddrIsNow(DWORD addr)
+{
+ if(!(addr & 0x80000000)) oops();
+
+ BOOL seen = FALSE;
+ DWORD i;
+ for(i = 0; i < PicProgWriteP; i++) {
+ if(PicProg[i].arg1 == addr) {
+ // Insist that they be in the same page, but otherwise assume
+ // that PCLATH has already been set up appropriately.
+ if((i >> 11) != (PicProgWriteP >> 11)) {
+ Error(_("Internal error relating to PIC paging; make program "
+ "smaller or reshuffle it."));
+ CompileError();
+ }
+ PicProg[i].arg1 = PicProgWriteP;
+ seen = TRUE;
+ } else if(PicProg[i].arg1 == FWD_LO(addr)) {
+ PicProg[i].arg1 = (PicProgWriteP & 0x7ff);
+ seen = TRUE;
+ } else if(PicProg[i].arg1 == FWD_HI(addr)) {
+ PicProg[i].arg1 = (PicProgWriteP >> 8);
+ }
+ }
+ if(!seen) oops();
+}
+
+//-----------------------------------------------------------------------------
+// Given an opcode and its operands, assemble the 14-bit instruction for the
+// PIC. Check that the operands do not have more bits set than is meaningful;
+// it is an internal error if they do.
+//-----------------------------------------------------------------------------
+static DWORD Assemble(Pic16Op op, DWORD arg1, DWORD arg2)
+{
+#define CHECK(v, bits) if((v) != ((v) & ((1 << (bits))-1))) oops()
+ switch(op) {
+ case OP_ADDWF:
+ CHECK(arg2, 1); CHECK(arg1, 7);
+ return (7 << 8) | (arg2 << 7) | arg1;
+
+ case OP_ANDWF:
+ CHECK(arg2, 1); CHECK(arg1, 7);
+ return (5 << 8) | (arg2 << 7) | arg1;
+
+ case OP_BSF:
+ CHECK(arg2, 3); CHECK(arg1, 7);
+ return (5 << 10) | (arg2 << 7) | arg1;
+
+ case OP_BCF:
+ CHECK(arg2, 3); CHECK(arg1, 7);
+ return (4 << 10) | (arg2 << 7) | arg1;
+
+ case OP_BTFSC:
+ CHECK(arg2, 3); CHECK(arg1, 7);
+ return (6 << 10) | (arg2 << 7) | arg1;
+
+ case OP_BTFSS:
+ CHECK(arg2, 3); CHECK(arg1, 7);
+ return (7 << 10) | (arg2 << 7) | arg1;
+
+ case OP_CLRF:
+ CHECK(arg1, 7); CHECK(arg2, 0);
+ return (3 << 7) | arg1;
+
+ case OP_CLRWDT:
+ return 0x0064;
+
+ case OP_COMF:
+ CHECK(arg2, 1); CHECK(arg1, 7);
+ return (9 << 8) | (arg2 << 7) | arg1;
+
+ case OP_DECF:
+ CHECK(arg1, 7); CHECK(arg2, 1);
+ return (3 << 8) | (arg2 << 7) | arg1;
+
+ case OP_DECFSZ:
+ CHECK(arg1, 7); CHECK(arg2, 1);
+ return (11 << 8) | (arg2 << 7) | arg1;
+
+ case OP_GOTO:
+ // Very special case: we will assume that the PCLATH stuff has
+ // been taken care of already.
+ arg1 &= 0x7ff;
+ CHECK(arg1, 11); CHECK(arg2, 0);
+ return (5 << 11) | arg1;
+
+ case OP_CALL:
+ CHECK(arg1, 11); CHECK(arg2, 0);
+ return (4 << 11) | arg1;
+
+ case OP_INCF:
+ CHECK(arg1, 7); CHECK(arg2, 1);
+ return (10 << 8) | (arg2 << 7) | arg1;
+
+ case OP_INCFSZ:
+ CHECK(arg1, 7); CHECK(arg2, 1);
+ return (15 << 8) | (arg2 << 7) | arg1;
+
+ case OP_IORWF:
+ CHECK(arg2, 1); CHECK(arg1, 7);
+ return (4 << 8) | (arg2 << 7) | arg1;
+
+ case OP_MOVLW:
+ CHECK(arg1, 8); CHECK(arg2, 0);
+ return (3 << 12) | arg1;
+
+ case OP_MOVF:
+ CHECK(arg1, 7); CHECK(arg2, 1);
+ return (8 << 8) | (arg2 << 7) | arg1;
+
+ case OP_MOVWF:
+ CHECK(arg1, 7); CHECK(arg2, 0);
+ return (1 << 7) | arg1;
+
+ case OP_NOP:
+ return 0x0000;
+
+ case OP_RETURN:
+ return 0x0008;
+
+ case OP_RETFIE:
+ return 0x0009;
+
+ case OP_RLF:
+ CHECK(arg1, 7); CHECK(arg2, 1);
+ return (13 << 8) | (arg2 << 7) | arg1;
+
+ case OP_RRF:
+ CHECK(arg1, 7); CHECK(arg2, 1);
+ return (12 << 8) | (arg2 << 7) | arg1;
+
+ case OP_SUBLW:
+ CHECK(arg1, 8); CHECK(arg2, 0);
+ return (15 << 9) | arg1;
+
+ case OP_SUBWF:
+ CHECK(arg1, 7); CHECK(arg2, 1);
+ return (2 << 8) | (arg2 << 7) | arg1;
+
+ case OP_XORWF:
+ CHECK(arg1, 7); CHECK(arg2, 1);
+ return (6 << 8) | (arg2 << 7) | arg1;
+
+ default:
+ oops();
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Write an intel IHEX format description of the program assembled so far.
+// This is where we actually do the assembly to binary format.
+//-----------------------------------------------------------------------------
+static void WriteHexFile(FILE *f)
+{
+ BYTE soFar[16];
+ int soFarCount = 0;
+ DWORD soFarStart = 0;
+
+ // always start from address 0
+ fprintf(f, ":020000040000FA\n");
+
+ DWORD i;
+ for(i = 0; i < PicProgWriteP; i++) {
+ DWORD w = Assemble(PicProg[i].op, PicProg[i].arg1, PicProg[i].arg2);
+
+ if(soFarCount == 0) soFarStart = i;
+ soFar[soFarCount++] = (BYTE)(w & 0xff);
+ soFar[soFarCount++] = (BYTE)(w >> 8);
+
+ if(soFarCount >= 0x10 || i == (PicProgWriteP-1)) {
+ StartIhex(f);
+ WriteIhex(f, soFarCount);
+ WriteIhex(f, (BYTE)((soFarStart*2) >> 8));
+ WriteIhex(f, (BYTE)((soFarStart*2) & 0xff));
+ WriteIhex(f, 0x00);
+ int j;
+ for(j = 0; j < soFarCount; j++) {
+ WriteIhex(f, soFar[j]);
+ }
+ FinishIhex(f);
+ soFarCount = 0;
+ }
+ }
+
+ StartIhex(f);
+ // Configuration words start at address 0x2007 in program memory; and the
+ // hex file addresses are by bytes, not words, so we start at 0x400e.
+ // There may be either 16 or 32 bits of conf word, depending on the part.
+ if(McuIs("Microchip PIC16F887 40-PDIP") ||
+ McuIs("Microchip PIC16F886 28-PDIP or 28-SOIC"))
+ {
+ WriteIhex(f, 0x04);
+ WriteIhex(f, 0x40);
+ WriteIhex(f, 0x0E);
+ WriteIhex(f, 0x00);
+ WriteIhex(f, (Prog.mcu->configurationWord >> 0) & 0xff);
+ WriteIhex(f, (Prog.mcu->configurationWord >> 8) & 0xff);
+ WriteIhex(f, (Prog.mcu->configurationWord >> 16) & 0xff);
+ WriteIhex(f, (Prog.mcu->configurationWord >> 24) & 0xff);
+ } else {
+ if(Prog.mcu->configurationWord & 0xffff0000) oops();
+ WriteIhex(f, 0x02);
+ WriteIhex(f, 0x40);
+ WriteIhex(f, 0x0E);
+ WriteIhex(f, 0x00);
+ WriteIhex(f, (Prog.mcu->configurationWord >> 0) & 0xff);
+ WriteIhex(f, (Prog.mcu->configurationWord >> 8) & 0xff);
+ }
+ FinishIhex(f);
+
+ // end of file record
+ fprintf(f, ":00000001FF\n");
+}
+
+//-----------------------------------------------------------------------------
+// Generate code to write an 8-bit value to a particular register. Takes care
+// of the bank switching if necessary; assumes that code is called in bank
+// 0.
+//-----------------------------------------------------------------------------
+static void WriteRegister(DWORD reg, BYTE val)
+{
+ if(reg & 0x080) Instruction(OP_BSF, REG_STATUS, STATUS_RP0);
+ if(reg & 0x100) Instruction(OP_BSF, REG_STATUS, STATUS_RP1);
+
+ Instruction(OP_MOVLW, val, 0);
+ Instruction(OP_MOVWF, (reg & 0x7f), 0);
+
+ if(reg & 0x080) Instruction(OP_BCF, REG_STATUS, STATUS_RP0);
+ if(reg & 0x100) Instruction(OP_BCF, REG_STATUS, STATUS_RP1);
+}
+
+//-----------------------------------------------------------------------------
+// Call a subroutine, that might be in an arbitrary page, and then put
+// PCLATH back where we want it.
+//-----------------------------------------------------------------------------
+static void CallWithPclath(DWORD addr)
+{
+ // Set up PCLATH for the jump, and then do it.
+ Instruction(OP_MOVLW, FWD_HI(addr), 0);
+ Instruction(OP_MOVWF, REG_PCLATH, 0);
+ Instruction(OP_CALL, FWD_LO(addr), 0);
+
+ // Restore PCLATH to something appropriate for our page. (We have
+ // already made fairly sure that we will never try to compile across
+ // a page boundary.)
+ Instruction(OP_MOVLW, (PicProgWriteP >> 8), 0);
+ Instruction(OP_MOVWF, REG_PCLATH, 0);
+}
+
+// Note that all of these are single instructions on the PIC; this is not the
+// case for their equivalents on the AVR!
+#define SetBit(reg, b) Instruction(OP_BSF, reg, b)
+#define ClearBit(reg, b) Instruction(OP_BCF, reg, b)
+#define IfBitClear(reg, b) Instruction(OP_BTFSS, reg, b)
+#define IfBitSet(reg, b) Instruction(OP_BTFSC, reg, b)
+static void CopyBit(DWORD addrDest, int bitDest, DWORD addrSrc, int bitSrc)
+{
+ IfBitSet(addrSrc, bitSrc);
+ SetBit(addrDest, bitDest);
+ IfBitClear(addrSrc, bitSrc);
+ ClearBit(addrDest, bitDest);
+}
+
+//-----------------------------------------------------------------------------
+// Handle an IF statement. Flow continues to the first instruction generated
+// by this function if the condition is true, else it jumps to the given
+// address (which is an FwdAddress, so not yet assigned). Called with IntPc
+// on the IF statement, returns with IntPc on the END IF.
+//-----------------------------------------------------------------------------
+static void CompileIfBody(DWORD condFalse)
+{
+ IntPc++;
+ CompileFromIntermediate(FALSE);
+ if(IntCode[IntPc].op == INT_ELSE) {
+ IntPc++;
+ DWORD endBlock = AllocFwdAddr();
+ Instruction(OP_GOTO, endBlock, 0);
+
+ FwdAddrIsNow(condFalse);
+ CompileFromIntermediate(FALSE);
+ FwdAddrIsNow(endBlock);
+ } else {
+ FwdAddrIsNow(condFalse);
+ }
+
+ if(IntCode[IntPc].op != INT_END_IF) oops();
+}
+
+//-----------------------------------------------------------------------------
+// Compile the intermediate code to PIC16 native code.
+//-----------------------------------------------------------------------------
+static void CompileFromIntermediate(BOOL topLevel)
+{
+ DWORD addr, addr2;
+ int bit, bit2;
+ DWORD addrl, addrh;
+ DWORD addrl2, addrh2;
+ DWORD addrl3, addrh3;
+
+ // Keep track of which 2k section we are using. When it looks like we
+ // are about to run out, fill with nops and move on to the next one.
+ DWORD section = 0;
+
+ for(; IntPc < IntCodeLen; IntPc++) {
+ // Try for a margin of about 400 words, which is a little bit
+ // wasteful but considering that the formatted output commands
+ // are huge, probably necessary. Of course if we are in our
+ // last section then it is silly to do that, either we make it
+ // or we're screwed...
+ if(topLevel && (((PicProgWriteP + 400) >> 11) != section) &&
+ ((PicProgWriteP + 400) < Prog.mcu->flashWords))
+ {
+ // Jump to the beginning of the next section
+ Instruction(OP_MOVLW, (PicProgWriteP >> 8) + (1<<3), 0);
+ Instruction(OP_MOVWF, REG_PCLATH, 0);
+ Instruction(OP_GOTO, 0, 0);
+ // Then, just burn the last of this section with NOPs.
+ while((PicProgWriteP >> 11) == section) {
+ Instruction(OP_MOVLW, 0xab, 0);
+ }
+ section = (PicProgWriteP >> 11);
+ // And now PCLATH is set up, so everything in our new section
+ // should just work
+ }
+ IntOp *a = &IntCode[IntPc];
+ switch(a->op) {
+ case INT_SET_BIT:
+ MemForSingleBit(a->name1, FALSE, &addr, &bit);
+ SetBit(addr, bit);
+ break;
+
+ case INT_CLEAR_BIT:
+ MemForSingleBit(a->name1, FALSE, &addr, &bit);
+ ClearBit(addr, bit);
+ break;
+
+ case INT_COPY_BIT_TO_BIT:
+ MemForSingleBit(a->name1, FALSE, &addr, &bit);
+ MemForSingleBit(a->name2, FALSE, &addr2, &bit2);
+ CopyBit(addr, bit, addr2, bit2);
+ break;
+
+ case INT_SET_VARIABLE_TO_LITERAL:
+ MemForVariable(a->name1, &addrl, &addrh);
+ WriteRegister(addrl, a->literal & 0xff);
+ WriteRegister(addrh, a->literal >> 8);
+ break;
+
+ case INT_INCREMENT_VARIABLE: {
+ MemForVariable(a->name1, &addrl, &addrh);
+ DWORD noCarry = AllocFwdAddr();
+ Instruction(OP_INCFSZ, addrl, DEST_F);
+ Instruction(OP_GOTO, noCarry, 0);
+ Instruction(OP_INCF, addrh, DEST_F);
+ FwdAddrIsNow(noCarry);
+ break;
+ }
+ case INT_IF_BIT_SET: {
+ DWORD condFalse = AllocFwdAddr();
+ MemForSingleBit(a->name1, TRUE, &addr, &bit);
+ IfBitClear(addr, bit);
+ Instruction(OP_GOTO, condFalse, 0);
+ CompileIfBody(condFalse);
+ break;
+ }
+ case INT_IF_BIT_CLEAR: {
+ DWORD condFalse = AllocFwdAddr();
+ MemForSingleBit(a->name1, TRUE, &addr, &bit);
+ IfBitSet(addr, bit);
+ Instruction(OP_GOTO, condFalse, 0);
+ CompileIfBody(condFalse);
+ break;
+ }
+ case INT_IF_VARIABLE_LES_LITERAL: {
+ DWORD notTrue = AllocFwdAddr();
+ DWORD isTrue = AllocFwdAddr();
+ DWORD lsbDecides = AllocFwdAddr();
+
+ // V = Rd7*(Rr7')*(R7') + (Rd7')*Rr7*R7 ; but only one of the
+ // product terms can be true, and we know which at compile
+ // time
+ BYTE litH = (a->literal >> 8);
+ BYTE litL = (a->literal & 0xff);
+
+ MemForVariable(a->name1, &addrl, &addrh);
+
+ // var - lit
+ Instruction(OP_MOVLW, litH, 0);
+ Instruction(OP_SUBWF, addrh, DEST_W);
+ IfBitSet(REG_STATUS, STATUS_Z);
+ Instruction(OP_GOTO, lsbDecides, 0);
+ Instruction(OP_MOVWF, Scratch0, 0);
+ if(litH & 0x80) {
+ Instruction(OP_COMF, addrh, DEST_W);
+ Instruction(OP_ANDWF, Scratch0, DEST_W);
+ Instruction(OP_XORWF, Scratch0, DEST_F);
+ } else {
+ Instruction(OP_COMF, Scratch0, DEST_W);
+ Instruction(OP_ANDWF, addrh, DEST_W);
+ Instruction(OP_XORWF, Scratch0, DEST_F);
+ }
+ IfBitSet(Scratch0, 7); // var - lit < 0, var < lit
+ Instruction(OP_GOTO, isTrue, 0);
+ Instruction(OP_GOTO, notTrue, 0);
+
+ FwdAddrIsNow(lsbDecides);
+
+ // var - lit < 0
+ // var < lit
+ Instruction(OP_MOVLW, litL, 0);
+ Instruction(OP_SUBWF, addrl, DEST_W);
+ IfBitClear(REG_STATUS, STATUS_C);
+ Instruction(OP_GOTO, isTrue, 0);
+ Instruction(OP_GOTO, notTrue, 0);
+
+ FwdAddrIsNow(isTrue);
+ CompileIfBody(notTrue);
+ break;
+ }
+ case INT_IF_VARIABLE_EQUALS_VARIABLE: {
+ DWORD notEqual = AllocFwdAddr();
+
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForVariable(a->name2, &addrl2, &addrh2);
+ Instruction(OP_MOVF, addrl, DEST_W);
+ Instruction(OP_SUBWF, addrl2, DEST_W);
+ IfBitClear(REG_STATUS, STATUS_Z);
+ Instruction(OP_GOTO, notEqual, 0);
+ Instruction(OP_MOVF, addrh, DEST_W);
+ Instruction(OP_SUBWF, addrh2, DEST_W);
+ IfBitClear(REG_STATUS, STATUS_Z);
+ Instruction(OP_GOTO, notEqual, 0);
+ CompileIfBody(notEqual);
+ break;
+ }
+ case INT_IF_VARIABLE_GRT_VARIABLE: {
+ DWORD notTrue = AllocFwdAddr();
+ DWORD isTrue = AllocFwdAddr();
+ DWORD lsbDecides = AllocFwdAddr();
+
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForVariable(a->name2, &addrl2, &addrh2);
+
+ // first, a signed comparison of the high octets, which is
+ // a huge pain on the PIC16
+ DWORD iu = addrh2, ju = addrh;
+ DWORD signa = Scratch0;
+ DWORD signb = Scratch1;
+
+ Instruction(OP_COMF, ju, DEST_W);
+ Instruction(OP_MOVWF, signb, 0);
+
+ Instruction(OP_ANDWF, iu, DEST_W);
+ Instruction(OP_MOVWF, signa, 0);
+
+ Instruction(OP_MOVF, iu, DEST_W);
+ Instruction(OP_IORWF, signb, DEST_F);
+ Instruction(OP_COMF, signb, DEST_F);
+
+ Instruction(OP_MOVF, ju, DEST_W);
+ Instruction(OP_SUBWF, iu, DEST_W);
+ IfBitSet(REG_STATUS, STATUS_Z);
+ Instruction(OP_GOTO, lsbDecides, 0);
+
+ Instruction(OP_ANDWF, signb, DEST_F);
+ Instruction(OP_MOVWF, Scratch2, 0);
+ Instruction(OP_COMF, Scratch2, DEST_W);
+ Instruction(OP_ANDWF, signa, DEST_W);
+ Instruction(OP_IORWF, signb, DEST_W);
+ Instruction(OP_XORWF, Scratch2, DEST_F);
+ IfBitSet(Scratch2, 7);
+ Instruction(OP_GOTO, isTrue, 0);
+
+ Instruction(OP_GOTO, notTrue, 0);
+
+ FwdAddrIsNow(lsbDecides);
+ Instruction(OP_MOVF, addrl, DEST_W);
+ Instruction(OP_SUBWF, addrl2, DEST_W);
+ IfBitClear(REG_STATUS, STATUS_C);
+ Instruction(OP_GOTO, isTrue, 0);
+
+ Instruction(OP_GOTO, notTrue, 0);
+
+ FwdAddrIsNow(isTrue);
+ CompileIfBody(notTrue);
+ break;
+ }
+ case INT_SET_VARIABLE_TO_VARIABLE:
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForVariable(a->name2, &addrl2, &addrh2);
+
+ Instruction(OP_MOVF, addrl2, DEST_W);
+ Instruction(OP_MOVWF, addrl, 0);
+
+ Instruction(OP_MOVF, addrh2, DEST_W);
+ Instruction(OP_MOVWF, addrh, 0);
+ break;
+
+ // The add and subtract routines must be written to return correct
+ // results if the destination and one of the operands happen to
+ // be the same registers (e.g. for B = A - B).
+
+ case INT_SET_VARIABLE_ADD:
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForVariable(a->name2, &addrl2, &addrh2);
+ MemForVariable(a->name3, &addrl3, &addrh3);
+
+ Instruction(OP_MOVF, addrl2, DEST_W);
+ Instruction(OP_ADDWF, addrl3, DEST_W);
+ Instruction(OP_MOVWF, addrl, 0);
+ ClearBit(Scratch0, 0);
+ IfBitSet(REG_STATUS, STATUS_C);
+ SetBit(Scratch0, 0);
+
+ Instruction(OP_MOVF, addrh2, DEST_W);
+ Instruction(OP_ADDWF, addrh3, DEST_W);
+ Instruction(OP_MOVWF, addrh, 0);
+ IfBitSet(Scratch0, 0);
+ Instruction(OP_INCF, addrh, DEST_F);
+ break;
+
+ case INT_SET_VARIABLE_SUBTRACT:
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForVariable(a->name2, &addrl2, &addrh2);
+ MemForVariable(a->name3, &addrl3, &addrh3);
+
+ Instruction(OP_MOVF, addrl3, DEST_W);
+ Instruction(OP_SUBWF, addrl2, DEST_W);
+ Instruction(OP_MOVWF, addrl, 0);
+ ClearBit(Scratch0, 0);
+ IfBitSet(REG_STATUS, STATUS_C);
+ SetBit(Scratch0, 0);
+
+ Instruction(OP_MOVF, addrh3, DEST_W);
+ Instruction(OP_SUBWF, addrh2, DEST_W);
+ Instruction(OP_MOVWF, addrh, 0);
+ IfBitClear(Scratch0, 0); // bit is carry / (not borrow)
+ Instruction(OP_DECF, addrh, DEST_F);
+ break;
+
+ case INT_SET_VARIABLE_MULTIPLY:
+ MultiplyNeeded = TRUE;
+
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForVariable(a->name2, &addrl2, &addrh2);
+ MemForVariable(a->name3, &addrl3, &addrh3);
+
+ Instruction(OP_MOVF, addrl2, DEST_W);
+ Instruction(OP_MOVWF, Scratch0, 0);
+ Instruction(OP_MOVF, addrh2, DEST_W);
+ Instruction(OP_MOVWF, Scratch1, 0);
+
+ Instruction(OP_MOVF, addrl3, DEST_W);
+ Instruction(OP_MOVWF, Scratch2, 0);
+ Instruction(OP_MOVF, addrh3, DEST_W);
+ Instruction(OP_MOVWF, Scratch3, 0);
+
+ CallWithPclath(MultiplyRoutineAddress);
+
+ Instruction(OP_MOVF, Scratch2, DEST_W);
+ Instruction(OP_MOVWF, addrl, 0);
+ Instruction(OP_MOVF, Scratch3, DEST_W);
+ Instruction(OP_MOVWF, addrh, 0);
+ break;
+
+ case INT_SET_VARIABLE_DIVIDE:
+ DivideNeeded = TRUE;
+
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForVariable(a->name2, &addrl2, &addrh2);
+ MemForVariable(a->name3, &addrl3, &addrh3);
+
+ Instruction(OP_MOVF, addrl2, DEST_W);
+ Instruction(OP_MOVWF, Scratch0, 0);
+ Instruction(OP_MOVF, addrh2, DEST_W);
+ Instruction(OP_MOVWF, Scratch1, 0);
+
+ Instruction(OP_MOVF, addrl3, DEST_W);
+ Instruction(OP_MOVWF, Scratch2, 0);
+ Instruction(OP_MOVF, addrh3, DEST_W);
+ Instruction(OP_MOVWF, Scratch3, 0);
+
+ CallWithPclath(DivideRoutineAddress);
+
+ Instruction(OP_MOVF, Scratch0, DEST_W);
+ Instruction(OP_MOVWF, addrl, 0);
+ Instruction(OP_MOVF, Scratch1, DEST_W);
+ Instruction(OP_MOVWF, addrh, 0);
+ break;
+
+ case INT_UART_SEND: {
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForSingleBit(a->name2, TRUE, &addr, &bit);
+
+ DWORD noSend = AllocFwdAddr();
+ IfBitClear(addr, bit);
+ Instruction(OP_GOTO, noSend, 0);
+
+ Instruction(OP_MOVF, addrl, DEST_W);
+ Instruction(OP_MOVWF, REG_TXREG, 0);
+
+ FwdAddrIsNow(noSend);
+ ClearBit(addr, bit);
+
+ DWORD notBusy = AllocFwdAddr();
+ Instruction(OP_BSF, REG_STATUS, STATUS_RP0);
+ Instruction(OP_BTFSC, REG_TXSTA ^ 0x80, 1);
+ Instruction(OP_GOTO, notBusy, 0);
+
+ Instruction(OP_BCF, REG_STATUS, STATUS_RP0);
+ SetBit(addr, bit);
+
+ FwdAddrIsNow(notBusy);
+ Instruction(OP_BCF, REG_STATUS, STATUS_RP0);
+
+ break;
+ }
+ case INT_UART_RECV: {
+ MemForVariable(a->name1, &addrl, &addrh);
+ MemForSingleBit(a->name2, TRUE, &addr, &bit);
+
+ ClearBit(addr, bit);
+
+ // If RCIF is still clear, then there's nothing to do; in that
+ // case jump to the end, and leave the rung-out clear.
+ DWORD done = AllocFwdAddr();
+ IfBitClear(REG_PIR1, 5);
+ Instruction(OP_GOTO, done, 0);
+
+ // RCIF is set, so we have a character. Read it now.
+ Instruction(OP_MOVF, REG_RCREG, DEST_W);
+ Instruction(OP_MOVWF, addrl, 0);
+ Instruction(OP_CLRF, addrh, 0);
+ // and set rung-out true
+ SetBit(addr, bit);
+
+ // And check for errors; need to reset the UART if yes.
+ DWORD yesError = AllocFwdAddr();
+ IfBitSet(REG_RCSTA, 1); // overrun error
+ Instruction(OP_GOTO, yesError, 0);
+ IfBitSet(REG_RCSTA, 2); // framing error
+ Instruction(OP_GOTO, yesError, 0);
+
+ // Neither FERR nor OERR is set, so we're good.
+ Instruction(OP_GOTO, done, 0);
+
+ FwdAddrIsNow(yesError);
+ // An error did occur, so flush the FIFO.
+ Instruction(OP_MOVF, REG_RCREG, DEST_W);
+ Instruction(OP_MOVF, REG_RCREG, DEST_W);
+ // And clear and then set CREN, to clear the error flags.
+ ClearBit(REG_RCSTA, 4);
+ SetBit(REG_RCSTA, 4);
+
+ FwdAddrIsNow(done);
+ break;
+ }
+ case INT_SET_PWM: {
+ int target = atoi(a->name2);
+
+ // So the PWM frequency is given by
+ // target = xtal/(4*prescale*pr2)
+ // xtal/target = 4*prescale*pr2
+ // and pr2 should be made as large as possible to keep
+ // resolution, so prescale should be as small as possible
+
+ int pr2;
+ int prescale;
+ for(prescale = 1;;) {
+ int dv = 4*prescale*target;
+ pr2 = (Prog.mcuClock + (dv/2))/dv;
+ if(pr2 < 3) {
+ Error(_("PWM frequency too fast."));
+ CompileError();
+ }
+ if(pr2 >= 256) {
+ if(prescale == 1) {
+ prescale = 4;
+ } else if(prescale == 4) {
+ prescale = 16;
+ } else {
+ Error(_("PWM frequency too slow."));
+ CompileError();
+ }
+ } else {
+ break;
+ }
+ }
+
+ // First scale the input variable from percent to timer units,
+ // with a multiply and then a divide.
+ MultiplyNeeded = TRUE; DivideNeeded = TRUE;
+ MemForVariable(a->name1, &addrl, &addrh);
+ Instruction(OP_MOVF, addrl, DEST_W);
+ Instruction(OP_MOVWF, Scratch0, 0);
+ Instruction(OP_CLRF, Scratch1, 0);
+
+ Instruction(OP_MOVLW, pr2, 0);
+ Instruction(OP_MOVWF, Scratch2, 0);
+ Instruction(OP_CLRF, Scratch3, 0);
+
+ CallWithPclath(MultiplyRoutineAddress);
+
+ Instruction(OP_MOVF, Scratch3, DEST_W);
+ Instruction(OP_MOVWF, Scratch1, 0);
+ Instruction(OP_MOVF, Scratch2, DEST_W);
+ Instruction(OP_MOVWF, Scratch0, 0);
+ Instruction(OP_MOVLW, 100, 0);
+ Instruction(OP_MOVWF, Scratch2, 0);
+ Instruction(OP_CLRF, Scratch3, 0);
+
+ CallWithPclath(DivideRoutineAddress);
+
+ Instruction(OP_MOVF, Scratch0, DEST_W);
+ Instruction(OP_MOVWF, REG_CCPR2L, 0);
+
+ // Only need to do the setup stuff once
+ MemForSingleBit("$pwm_init", FALSE, &addr, &bit);
+ DWORD skip = AllocFwdAddr();
+ IfBitSet(addr, bit);
+ Instruction(OP_GOTO, skip, 0);
+ SetBit(addr, bit);
+
+ // Set up the CCP2 and TMR2 peripherals.
+ WriteRegister(REG_PR2, (pr2-1));
+ WriteRegister(REG_CCP2CON, 0x0c); // PWM mode, ignore lsbs
+
+ BYTE t2con = (1 << 2); // timer 2 on
+ if(prescale == 1)
+ t2con |= 0;
+ else if(prescale == 4)
+ t2con |= 1;
+ else if(prescale == 16)
+ t2con |= 2;
+ else oops();
+ WriteRegister(REG_T2CON, t2con);
+
+ FwdAddrIsNow(skip);
+ break;
+ }
+
+// A quick helper macro to set the banksel bits correctly; this is necessary
+// because the EEwhatever registers are all over in the memory maps.
+#define EE_REG_BANKSEL(r) \
+ if((r) & 0x80) { \
+ if(!(m & 0x80)) { \
+ m |= 0x80; \
+ Instruction(OP_BSF, REG_STATUS, STATUS_RP0); \
+ } \
+ } else { \
+ if(m & 0x80) { \
+ m &= ~0x80; \
+ Instruction(OP_BCF, REG_STATUS, STATUS_RP0); \
+ } \
+ } \
+ if((r) & 0x100) { \
+ if(!(m & 0x100)) { \
+ m |= 0x100; \
+ Instruction(OP_BSF, REG_STATUS, STATUS_RP1); \
+ } \
+ } else { \
+ if(m & 0x100) { \
+ m &= ~0x100; \
+ Instruction(OP_BCF, REG_STATUS, STATUS_RP1); \
+ } \
+ }
+
+ case INT_EEPROM_BUSY_CHECK: {
+ DWORD isBusy = AllocFwdAddr();
+ DWORD done = AllocFwdAddr();
+ MemForSingleBit(a->name1, FALSE, &addr, &bit);
+
+ WORD m = 0;
+
+ EE_REG_BANKSEL(REG_EECON1);
+ IfBitSet(REG_EECON1 ^ m, 1);
+ Instruction(OP_GOTO, isBusy, 0);
+ EE_REG_BANKSEL(0);
+
+ IfBitClear(EepromHighByteWaitingAddr, EepromHighByteWaitingBit);
+ Instruction(OP_GOTO, done, 0);
+
+ // So there is not a write pending, but we have another
+ // character to transmit queued up.
+
+ EE_REG_BANKSEL(REG_EEADR);
+ Instruction(OP_INCF, REG_EEADR ^ m, DEST_F);
+ EE_REG_BANKSEL(0);
+ Instruction(OP_MOVF, EepromHighByte, DEST_W);
+ EE_REG_BANKSEL(REG_EEDATA);
+ Instruction(OP_MOVWF, REG_EEDATA ^ m, 0);
+ EE_REG_BANKSEL(REG_EECON1);
+ Instruction(OP_BCF, REG_EECON1 ^ m, 7);
+ Instruction(OP_BSF, REG_EECON1 ^ m, 2);
+ Instruction(OP_MOVLW, 0x55, 0);
+ Instruction(OP_MOVWF, REG_EECON2 ^ m, 0);
+ Instruction(OP_MOVLW, 0xaa, 0);
+ Instruction(OP_MOVWF, REG_EECON2 ^ m, 0);
+ Instruction(OP_BSF, REG_EECON1 ^ m, 1);
+
+ EE_REG_BANKSEL(0);
+
+ ClearBit(EepromHighByteWaitingAddr, EepromHighByteWaitingBit);
+
+ FwdAddrIsNow(isBusy);
+ // Have to do these explicitly; m is out of date due to jump.
+ Instruction(OP_BCF, REG_STATUS, STATUS_RP0);
+ Instruction(OP_BCF, REG_STATUS, STATUS_RP1);
+ SetBit(addr, bit);
+
+ FwdAddrIsNow(done);
+ break;
+ }
+ case INT_EEPROM_WRITE: {
+ MemForVariable(a->name1, &addrl, &addrh);
+
+ WORD m = 0;
+
+ SetBit(EepromHighByteWaitingAddr, EepromHighByteWaitingBit);
+ Instruction(OP_MOVF, addrh, DEST_W);
+ Instruction(OP_MOVWF, EepromHighByte, 0);
+
+ EE_REG_BANKSEL(REG_EEADR);
+ Instruction(OP_MOVLW, a->literal, 0);
+ Instruction(OP_MOVWF, REG_EEADR ^ m, 0);
+ EE_REG_BANKSEL(0);
+ Instruction(OP_MOVF, addrl, DEST_W);
+ EE_REG_BANKSEL(REG_EEDATA);
+ Instruction(OP_MOVWF, REG_EEDATA ^ m, 0);
+ EE_REG_BANKSEL(REG_EECON1);
+ Instruction(OP_BCF, REG_EECON1 ^ m, 7);
+ Instruction(OP_BSF, REG_EECON1 ^ m, 2);
+ Instruction(OP_MOVLW, 0x55, 0);
+ Instruction(OP_MOVWF, REG_EECON2 ^ m, 0);
+ Instruction(OP_MOVLW, 0xaa, 0);
+ Instruction(OP_MOVWF, REG_EECON2 ^ m, 0);
+ Instruction(OP_BSF, REG_EECON1 ^ m, 1);
+
+ EE_REG_BANKSEL(0);
+ break;
+ }
+ case INT_EEPROM_READ: {
+ int i;
+ MemForVariable(a->name1, &addrl, &addrh);
+ WORD m = 0;
+ for(i = 0; i < 2; i++) {
+ EE_REG_BANKSEL(REG_EEADR);
+ Instruction(OP_MOVLW, a->literal+i, 0);
+ Instruction(OP_MOVWF, REG_EEADR ^ m, 0);
+ EE_REG_BANKSEL(REG_EECON1);
+ Instruction(OP_BCF, REG_EECON1 ^ m, 7);
+ Instruction(OP_BSF, REG_EECON1 ^ m, 0);
+ EE_REG_BANKSEL(REG_EEDATA);
+ Instruction(OP_MOVF, REG_EEDATA ^ m , DEST_W);
+ EE_REG_BANKSEL(0);
+ if(i == 0) {
+ Instruction(OP_MOVWF, addrl, 0);
+ } else {
+ Instruction(OP_MOVWF, addrh, 0);
+ }
+ }
+ break;
+ }
+ case INT_READ_ADC: {
+ BYTE adcs;
+
+ MemForVariable(a->name1, &addrl, &addrh);
+
+ if(Prog.mcuClock > 5000000) {
+ adcs = 2; // 32*Tosc
+ } else if(Prog.mcuClock > 1250000) {
+ adcs = 1; // 8*Tosc
+ } else {
+ adcs = 0; // 2*Tosc
+ }
+
+ int goPos, chsPos;
+ if(McuIs("Microchip PIC16F887 40-PDIP") ||
+ McuIs("Microchip PIC16F886 28-PDIP or 28-SOIC"))
+ {
+ goPos = 1;
+ chsPos = 2;
+ } else {
+ goPos = 2;
+ chsPos = 3;
+ }
+ WriteRegister(REG_ADCON0, (BYTE)
+ ((adcs << 6) |
+ (MuxForAdcVariable(a->name1) << chsPos) |
+ (0 << goPos) | // don't start yet
+ // bit 1 unimplemented
+ (1 << 0)) // A/D peripheral on
+ );
+
+ WriteRegister(REG_ADCON1,
+ (1 << 7) | // right-justified
+ (0 << 0) // for now, all analog inputs
+ );
+ if(strcmp(Prog.mcu->mcuName,
+ "Microchip PIC16F88 18-PDIP or 18-SOIC")==0)
+ {
+ WriteRegister(REG_ANSEL, 0x7f);
+ }
+ if(McuIs("Microchip PIC16F887 40-PDIP") ||
+ McuIs("Microchip PIC16F886 28-PDIP or 28-SOIC"))
+ {
+ WriteRegister(REG_ANSEL, 0xff);
+ WriteRegister(REG_ANSELH, 0x3f);
+ }
+
+ // need to wait Tacq (about 20 us) for mux, S/H etc. to settle
+ int cyclesToWait = ((Prog.mcuClock / 4) * 20) / 1000000;
+ cyclesToWait /= 3;
+ if(cyclesToWait < 1) cyclesToWait = 1;
+
+ Instruction(OP_MOVLW, cyclesToWait, 0);
+ Instruction(OP_MOVWF, Scratch1, 0);
+ DWORD wait = PicProgWriteP;
+ Instruction(OP_DECFSZ, Scratch1, DEST_F);
+ Instruction(OP_GOTO, wait, 0);
+
+ SetBit(REG_ADCON0, goPos);
+ DWORD spin = PicProgWriteP;
+ IfBitSet(REG_ADCON0, goPos);
+ Instruction(OP_GOTO, spin, 0);
+
+ Instruction(OP_MOVF, REG_ADRESH, DEST_W);
+ Instruction(OP_MOVWF, addrh, 0);
+
+ Instruction(OP_BSF, REG_STATUS, STATUS_RP0);
+ Instruction(OP_MOVF, REG_ADRESL ^ 0x80, DEST_W);
+ Instruction(OP_BCF, REG_STATUS, STATUS_RP0);
+ Instruction(OP_MOVWF, addrl, 0);
+
+ // hook those pins back up to the digital inputs in case
+ // some of them are used that way
+ WriteRegister(REG_ADCON1,
+ (1 << 7) | // right-justify A/D result
+ (6 << 0) // all digital inputs
+ );
+ if(strcmp(Prog.mcu->mcuName,
+ "Microchip PIC16F88 18-PDIP or 18-SOIC")==0)
+ {
+ WriteRegister(REG_ANSEL, 0x00);
+ }
+ if(McuIs("Microchip PIC16F887 40-PDIP") ||
+ McuIs("Microchip PIC16F886 28-PDIP or 28-SOIC"))
+ {
+ WriteRegister(REG_ANSEL, 0x00);
+ WriteRegister(REG_ANSELH, 0x00);
+ }
+ break;
+ }
+ case INT_END_IF:
+ case INT_ELSE:
+ return;
+
+ case INT_SIMULATE_NODE_STATE:
+ case INT_COMMENT:
+ break;
+
+ default:
+ oops();
+ break;
+ }
+ if(((PicProgWriteP >> 11) != section) && topLevel) {
+ // This is particularly prone to happening in the last section,
+ // if the program doesn't fit (since we won't have attempted
+ // to add padding).
+ Error(_("Internal error relating to PIC paging; make program "
+ "smaller or reshuffle it."));
+ CompileError();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Configure Timer1 and Ccp1 to generate the periodic `cycle' interrupt
+// that triggers all the ladder logic processing. We will always use 16-bit
+// Timer1, with the prescaler configured appropriately.
+//-----------------------------------------------------------------------------
+static void ConfigureTimer1(int cycleTimeMicroseconds)
+{
+ int divisor = 1;
+ int countsPerCycle;
+
+ while(divisor < 16) {
+ int timerRate = (Prog.mcuClock / (4*divisor)); // hertz
+ double timerPeriod = 1e6 / timerRate; // timer period, us
+ countsPerCycle = (int)(cycleTimeMicroseconds / timerPeriod);
+
+ if(countsPerCycle < 1000) {
+ Error(_("Cycle time too fast; increase cycle time, or use faster "
+ "crystal."));
+ CompileError();
+ } else if(countsPerCycle > 0xffff) {
+ if(divisor >= 8) {
+ Error(
+ _("Cycle time too slow; decrease cycle time, or use slower "
+ "crystal."));
+ CompileError();
+ }
+ } else {
+ break;
+ }
+ divisor *= 2;
+ }
+
+ WriteRegister(REG_CCPR1L, countsPerCycle & 0xff);
+ WriteRegister(REG_CCPR1H, countsPerCycle >> 8);
+
+ WriteRegister(REG_TMR1L, 0);
+ WriteRegister(REG_TMR1H, 0);
+
+ BYTE t1con = 0;
+ // set up prescaler
+ if(divisor == 1) t1con |= 0x00;
+ else if(divisor == 2) t1con |= 0x10;
+ else if(divisor == 4) t1con |= 0x20;
+ else if(divisor == 8) t1con |= 0x30;
+ else oops();
+ // enable clock, internal source
+ t1con |= 0x01;
+ WriteRegister(REG_T1CON, t1con);
+
+ BYTE ccp1con;
+ // compare mode, reset TMR1 on trigger
+ ccp1con = 0x0b;
+ WriteRegister(REG_CCP1CON, ccp1con);
+}
+
+//-----------------------------------------------------------------------------
+// Write a subroutine to do a 16x16 signed multiply. One operand in
+// Scratch1:Scratch0, other in Scratch3:Scratch2, result in Scratch3:Scratch2.
+//-----------------------------------------------------------------------------
+static void WriteMultiplyRoutine(void)
+{
+ DWORD result3 = Scratch5;
+ DWORD result2 = Scratch4;
+ DWORD result1 = Scratch3;
+ DWORD result0 = Scratch2;
+
+ DWORD multiplicand0 = Scratch0;
+ DWORD multiplicand1 = Scratch1;
+
+ DWORD counter = Scratch6;
+
+ DWORD dontAdd = AllocFwdAddr();
+ DWORD top;
+
+ FwdAddrIsNow(MultiplyRoutineAddress);
+
+ Instruction(OP_CLRF, result3, 0);
+ Instruction(OP_CLRF, result2, 0);
+ Instruction(OP_BCF, REG_STATUS, STATUS_C);
+ Instruction(OP_RRF, result1, DEST_F);
+ Instruction(OP_RRF, result0, DEST_F);
+
+ Instruction(OP_MOVLW, 16, 0);
+ Instruction(OP_MOVWF, counter, 0);
+
+ top = PicProgWriteP;
+ Instruction(OP_BTFSS, REG_STATUS, STATUS_C);
+ Instruction(OP_GOTO, dontAdd, 0);
+ Instruction(OP_MOVF, multiplicand0, DEST_W);
+ Instruction(OP_ADDWF, result2, DEST_F);
+ Instruction(OP_BTFSC, REG_STATUS, STATUS_C);
+ Instruction(OP_INCF, result3, DEST_F);
+ Instruction(OP_MOVF, multiplicand1, DEST_W);
+ Instruction(OP_ADDWF, result3, DEST_F);
+ FwdAddrIsNow(dontAdd);
+
+
+ Instruction(OP_BCF, REG_STATUS, STATUS_C);
+ Instruction(OP_RRF, result3, DEST_F);
+ Instruction(OP_RRF, result2, DEST_F);
+ Instruction(OP_RRF, result1, DEST_F);
+ Instruction(OP_RRF, result0, DEST_F);
+
+ Instruction(OP_DECFSZ, counter, DEST_F);
+ Instruction(OP_GOTO, top, 0);
+
+ Instruction(OP_RETURN, 0, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Write a subroutine to do a 16/16 signed divide. Call with dividend in
+// Scratch1:0, divisor in Scratch3:2, and get the result in Scratch1:0.
+//-----------------------------------------------------------------------------
+static void WriteDivideRoutine(void)
+{
+ DWORD dividend0 = Scratch0;
+ DWORD dividend1 = Scratch1;
+
+ DWORD divisor0 = Scratch2;
+ DWORD divisor1 = Scratch3;
+
+ DWORD remainder0 = Scratch4;
+ DWORD remainder1 = Scratch5;
+
+ DWORD counter = Scratch6;
+ DWORD sign = Scratch7;
+
+ DWORD dontNegateDivisor = AllocFwdAddr();
+ DWORD dontNegateDividend = AllocFwdAddr();
+ DWORD done = AllocFwdAddr();
+ DWORD notNegative = AllocFwdAddr();
+ DWORD loop;
+
+ FwdAddrIsNow(DivideRoutineAddress);
+ Instruction(OP_MOVF, dividend1, DEST_W);
+ Instruction(OP_XORWF, divisor1, DEST_W);
+ Instruction(OP_MOVWF, sign, 0);
+
+ Instruction(OP_BTFSS, divisor1, 7);
+ Instruction(OP_GOTO, dontNegateDivisor, 0);
+ Instruction(OP_COMF, divisor0, DEST_F);
+ Instruction(OP_COMF, divisor1, DEST_F);
+ Instruction(OP_INCF, divisor0, DEST_F);
+ Instruction(OP_BTFSC, REG_STATUS, STATUS_Z);
+ Instruction(OP_INCF, divisor1, DEST_F);
+ FwdAddrIsNow(dontNegateDivisor);
+
+ Instruction(OP_BTFSS, dividend1, 7);
+ Instruction(OP_GOTO, dontNegateDividend, 0);
+ Instruction(OP_COMF, dividend0, DEST_F);
+ Instruction(OP_COMF, dividend1, DEST_F);
+ Instruction(OP_INCF, dividend0, DEST_F);
+ Instruction(OP_BTFSC, REG_STATUS, STATUS_Z);
+ Instruction(OP_INCF, dividend1, DEST_F);
+ FwdAddrIsNow(dontNegateDividend);
+
+ Instruction(OP_CLRF, remainder1, 0);
+ Instruction(OP_CLRF, remainder0, 0);
+
+ Instruction(OP_BCF, REG_STATUS, STATUS_C);
+
+ Instruction(OP_MOVLW, 17, 0);
+ Instruction(OP_MOVWF, counter, 0);
+
+ loop = PicProgWriteP;
+ Instruction(OP_RLF, dividend0, DEST_F);
+ Instruction(OP_RLF, dividend1, DEST_F);
+
+ Instruction(OP_DECF, counter, DEST_F);
+ Instruction(OP_BTFSC, REG_STATUS, STATUS_Z);
+ Instruction(OP_GOTO, done, 0);
+
+ Instruction(OP_RLF, remainder0, DEST_F);
+ Instruction(OP_RLF, remainder1, DEST_F);
+
+ Instruction(OP_MOVF, divisor0, DEST_W);
+ Instruction(OP_SUBWF, remainder0, DEST_F);
+ Instruction(OP_BTFSS, REG_STATUS, STATUS_C);
+ Instruction(OP_DECF, remainder1, DEST_F);
+ Instruction(OP_MOVF, divisor1, DEST_W);
+ Instruction(OP_SUBWF, remainder1, DEST_F);
+
+ Instruction(OP_BTFSS, remainder1, 7);
+ Instruction(OP_GOTO, notNegative, 0);
+
+ Instruction(OP_MOVF, divisor0, DEST_W);
+ Instruction(OP_ADDWF, remainder0, DEST_F);
+ Instruction(OP_BTFSC, REG_STATUS, STATUS_C);
+ Instruction(OP_INCF, remainder1, DEST_F);
+ Instruction(OP_MOVF, divisor1, DEST_W);
+ Instruction(OP_ADDWF, remainder1, DEST_F);
+
+ Instruction(OP_BCF, REG_STATUS, STATUS_C);
+ Instruction(OP_GOTO, loop, 0);
+
+ FwdAddrIsNow(notNegative);
+ Instruction(OP_BSF, REG_STATUS, STATUS_C);
+ Instruction(OP_GOTO, loop, 0);
+
+ FwdAddrIsNow(done);
+ Instruction(OP_BTFSS, sign, 7);
+ Instruction(OP_RETURN, 0, 0);
+
+ Instruction(OP_COMF, dividend0, DEST_F);
+ Instruction(OP_COMF, dividend1, DEST_F);
+ Instruction(OP_INCF, dividend0, DEST_F);
+ Instruction(OP_BTFSC, REG_STATUS, STATUS_Z);
+ Instruction(OP_INCF, dividend1, DEST_F);
+ Instruction(OP_RETURN, 0, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Compile the program to PIC16 code for the currently selected processor
+// and write it to the given file. Produce an error message if we cannot
+// write to the file, or if there is something inconsistent about the
+// program.
+//-----------------------------------------------------------------------------
+void CompilePic16(char *outFile)
+{
+ FILE *f = fopen(outFile, "w");
+ if(!f) {
+ Error(_("Couldn't open file '%s'"), outFile);
+ return;
+ }
+
+ if(setjmp(CompileErrorBuf) != 0) {
+ fclose(f);
+ return;
+ }
+
+ WipeMemory();
+
+ AllocStart();
+ Scratch0 = AllocOctetRam();
+ Scratch1 = AllocOctetRam();
+ Scratch2 = AllocOctetRam();
+ Scratch3 = AllocOctetRam();
+ Scratch4 = AllocOctetRam();
+ Scratch5 = AllocOctetRam();
+ Scratch6 = AllocOctetRam();
+ Scratch7 = AllocOctetRam();
+
+ // Allocate the register used to hold the high byte of the EEPROM word
+ // that's queued up to program, plus the bit to indicate that it is
+ // valid.
+ EepromHighByte = AllocOctetRam();
+ AllocBitRam(&EepromHighByteWaitingAddr, &EepromHighByteWaitingBit);
+
+ DWORD progStart = AllocFwdAddr();
+ // Our boot vectors; not necessary to do it like this, but it lets
+ // bootloaders rewrite the beginning of the program to do their magic.
+ // PCLATH is init to 0, but apparently some bootloaders want to see us
+ // initialize it again.
+ Instruction(OP_BCF, REG_PCLATH, 3);
+ Instruction(OP_BCF, REG_PCLATH, 4);
+ Instruction(OP_GOTO, progStart, 0);
+ Instruction(OP_NOP, 0, 0);
+ Instruction(OP_NOP, 0, 0);
+ Instruction(OP_NOP, 0, 0);
+ Instruction(OP_NOP, 0, 0);
+ Instruction(OP_NOP, 0, 0);
+ FwdAddrIsNow(progStart);
+
+ // Now zero out the RAM
+ Instruction(OP_MOVLW, Prog.mcu->ram[0].start + 8, 0);
+ Instruction(OP_MOVWF, REG_FSR, 0);
+ Instruction(OP_MOVLW, Prog.mcu->ram[0].len - 8, 0);
+ Instruction(OP_MOVWF, Scratch0, 0);
+
+ DWORD zeroMem = PicProgWriteP;
+ Instruction(OP_CLRF, REG_INDF, 0);
+ Instruction(OP_INCF, REG_FSR, DEST_F);
+ Instruction(OP_DECFSZ, Scratch0, DEST_F);
+ Instruction(OP_GOTO, zeroMem, 0);
+
+ DivideRoutineAddress = AllocFwdAddr();
+ DivideNeeded = FALSE;
+ MultiplyRoutineAddress = AllocFwdAddr();
+ MultiplyNeeded = FALSE;
+
+ ConfigureTimer1(Prog.cycleTime);
+
+ // Set up the TRISx registers (direction). 1 means tri-stated (input).
+ BYTE isInput[MAX_IO_PORTS], isOutput[MAX_IO_PORTS];
+ BuildDirectionRegisters(isInput, isOutput);
+
+ if(McuIs("Microchip PIC16F877 40-PDIP") ||
+ McuIs("Microchip PIC16F819 18-PDIP or 18-SOIC") ||
+ McuIs("Microchip PIC16F88 18-PDIP or 18-SOIC") ||
+ McuIs("Microchip PIC16F876 28-PDIP or 28-SOIC") ||
+ McuIs("Microchip PIC16F887 40-PDIP") ||
+ McuIs("Microchip PIC16F886 28-PDIP or 28-SOIC"))
+ {
+ REG_EECON1 = 0x18c;
+ REG_EECON2 = 0x18d;
+ REG_EEDATA = 0x10c;
+ REG_EEADR = 0x10d;
+ } else if(McuIs("Microchip PIC16F628 18-PDIP or 18-SOIC")) {
+ REG_EECON1 = 0x9c;
+ REG_EECON2 = 0x9d;
+ REG_EEDATA = 0x9a;
+ REG_EEADR = 0x9b;
+ } else {
+ oops();
+ }
+
+ if(McuIs("Microchip PIC16F887 40-PDIP") ||
+ McuIs("Microchip PIC16F886 28-PDIP or 28-SOIC"))
+ {
+ REG_ANSEL = 0x188;
+ REG_ANSELH = 0x189;
+ } else {
+ REG_ANSEL = 0x9b;
+ }
+
+ if(strcmp(Prog.mcu->mcuName, "Microchip PIC16F877 40-PDIP")==0) {
+ // This is a nasty special case; one of the extra bits in TRISE
+ // enables the PSP, and must be kept clear (set here as will be
+ // inverted).
+ isOutput[4] |= 0xf8;
+ }
+
+ if(strcmp(Prog.mcu->mcuName, "Microchip PIC16F877 40-PDIP")==0 ||
+ strcmp(Prog.mcu->mcuName, "Microchip PIC16F819 18-PDIP or 18-SOIC")==0 ||
+ strcmp(Prog.mcu->mcuName, "Microchip PIC16F876 28-PDIP or 28-SOIC")==0)
+ {
+ // The GPIOs that can also be A/D inputs default to being A/D
+ // inputs, so turn that around
+ WriteRegister(REG_ADCON1,
+ (1 << 7) | // right-justify A/D result
+ (6 << 0) // all digital inputs
+ );
+ }
+
+ if(strcmp(Prog.mcu->mcuName, "Microchip PIC16F88 18-PDIP or 18-SOIC")==0) {
+ WriteRegister(REG_ANSEL, 0x00); // all digital inputs
+ }
+
+ if(strcmp(Prog.mcu->mcuName, "Microchip PIC16F628 18-PDIP or 18-SOIC")==0) {
+ // This is also a nasty special case; the comparators on the
+ // PIC16F628 are enabled by default and need to be disabled, or
+ // else the PORTA GPIOs don't work.
+ WriteRegister(REG_CMCON, 0x07);
+ }
+
+ if(McuIs("Microchip PIC16F887 40-PDIP") ||
+ McuIs("Microchip PIC16F886 28-PDIP or 28-SOIC"))
+ {
+ WriteRegister(REG_ANSEL, 0x00); // all digital inputs
+ WriteRegister(REG_ANSELH, 0x00); // all digital inputs
+ }
+
+ if(PwmFunctionUsed()) {
+ // Need to clear TRIS bit corresponding to PWM pin
+ int i;
+ for(i = 0; i < Prog.mcu->pinCount; i++) {
+ if(Prog.mcu->pinInfo[i].pin == Prog.mcu->pwmNeedsPin) {
+ McuIoPinInfo *iop = &(Prog.mcu->pinInfo[i]);
+ isOutput[iop->port - 'A'] |= (1 << iop->bit);
+ break;
+ }
+ }
+ if(i == Prog.mcu->pinCount) oops();
+ }
+
+ int i;
+ for(i = 0; Prog.mcu->dirRegs[i] != 0; i++) {
+ WriteRegister(Prog.mcu->outputRegs[i], 0x00);
+ WriteRegister(Prog.mcu->dirRegs[i], ~isOutput[i]);
+ }
+
+ if(UartFunctionUsed()) {
+ if(Prog.baudRate == 0) {
+ Error(_("Zero baud rate not possible."));
+ fclose(f);
+ return;
+ }
+
+ // So now we should set up the UART. First let us calculate the
+ // baud rate; there is so little point in the fast baud rates that
+ // I won't even bother, so
+ // bps = Fosc/(64*(X+1))
+ // bps*64*(X + 1) = Fosc
+ // X = Fosc/(bps*64)-1
+ // and round, don't truncate
+ int divisor = (Prog.mcuClock + Prog.baudRate*32)/(Prog.baudRate*64) - 1;
+
+ double actual = Prog.mcuClock/(64.0*(divisor+1));
+ double percentErr = 100*(actual - Prog.baudRate)/Prog.baudRate;
+
+ if(fabs(percentErr) > 2) {
+ ComplainAboutBaudRateError(divisor, actual, percentErr);
+ }
+ if(divisor > 255) ComplainAboutBaudRateOverflow();
+
+ WriteRegister(REG_SPBRG, divisor);
+ WriteRegister(REG_TXSTA, 0x20); // only TXEN set
+ WriteRegister(REG_RCSTA, 0x90); // only SPEN, CREN set
+ }
+
+ DWORD top = PicProgWriteP;
+
+ IfBitClear(REG_PIR1, 2);
+ Instruction(OP_GOTO, PicProgWriteP - 1, 0);
+
+ Instruction(OP_BCF, REG_PIR1, 2);
+
+ Instruction(OP_CLRWDT, 0, 0);
+ IntPc = 0;
+ CompileFromIntermediate(TRUE);
+
+ MemCheckForErrorsPostCompile();
+
+ // This is probably a big jump, so give it PCLATH.
+ Instruction(OP_CLRF, REG_PCLATH, 0);
+ Instruction(OP_GOTO, top, 0);
+
+ // Once again, let us make sure not to put stuff on a page boundary
+ if((PicProgWriteP >> 11) != ((PicProgWriteP + 150) >> 11)) {
+ DWORD section = (PicProgWriteP >> 11);
+ // Just burn the last of this section with NOPs.
+ while((PicProgWriteP >> 11) == section) {
+ Instruction(OP_MOVLW, 0xab, 0);
+ }
+ }
+
+ if(MultiplyNeeded) WriteMultiplyRoutine();
+ if(DivideNeeded) WriteDivideRoutine();
+
+ WriteHexFile(f);
+ fclose(f);
+
+ char str[MAX_PATH+500];
+ sprintf(str, _("Compile successful; wrote IHEX for PIC16 to '%s'.\r\n\r\n"
+ "Configuration word (fuses) has been set for crystal oscillator, BOD "
+ "enabled, LVP disabled, PWRT enabled, all code protection off.\r\n\r\n"
+ "Used %d/%d words of program flash (chip %d%% full)."),
+ outFile, PicProgWriteP, Prog.mcu->flashWords,
+ (100*PicProgWriteP)/Prog.mcu->flashWords);
+ CompileSuccessfulMessage(str);
+}
diff --git a/ldmicro/reg/expected/avr-hw.hex b/ldmicro/reg/expected/avr-hw.hex
new file mode 100644
index 0000000..fb676da
--- /dev/null
+++ b/ldmicro/reg/expected/avr-hw.hex
@@ -0,0 +1,330 @@
+:1000000022C0189518951895189518951895189553
+:100010001895189518951895189518951895189578
+:100020001895189518951895189518951895189568
+:100030001895189518951895189518951895189558
+:10004000189518951895B0E0AEE500E10C93B0E076
+:10005000ADE50FEF0C93B1E1A0E000E020E030E16E
+:10006000A150B0400C93215030402223C9F73323D4
+:10007000B9F7B0E0A8E900E00C93B0E0A9E907E621
+:100080000C93B0E0AAE908E10C93B0E0AAE300E029
+:100090000C93B0E0ABE300E00C93B0E0A7E300E822
+:1000A0000C93B0E0A8E300E00C93B0E0A4E300E020
+:1000B0000C93B0E0A5E300E00C93B0E0A1E308E00E
+:1000C0000C93B0E0A2E300E00C93B0E0A2E200E009
+:1000D0000C93B0E0A3E201E00C93B0E0A1E600E0F5
+:1000E0000C93B0E0A2E600E00C93B0E0A4E600E0E0
+:1000F0000C93B0E0A5E600E00C93B0E0AFE400E0C4
+:100100000C93B0E0AEE409E00C93B0E0ABE40CE992
+:100110000C93B0E0AAE40EE30C93B0E0A7E500E195
+:100120000C93B0E0A6E50C9104FFFBCFB0E0A6E590
+:100130000C9100610C93A895B1E0A1E00C910260D4
+:100140000C93B1E0A1E00C91B1E0A1E01C9101FFA2
+:100150001B7F01FD14601C93B1E0A1E00C9102FF34
+:10016000EFC0B1E0A1E00C9103FD60C0B1E0A1E0FF
+:100170000C910F7E0C93B0E0ACE30C9101FD23C019
+:10018000B1E0A1E00C9100FF23C0B0E0AEE30C9120
+:10019000B0E0AFE31C91039509F413951C93B0E014
+:1001A000AEE30C93B1E0A0E00C91B0E0ADE30C93B2
+:1001B000B0E0ACE304E00C9306E00C93B1E0A1E006
+:1001C0000C910E7F0C93B1E0A1E00C9100610C93B7
+:1001D000B1E0A1E00C9104FD29C0B1E0A1E00C91D7
+:1001E00008600C93B0E0AFE300E00C93B0E0AEE346
+:1001F00000E00C93B0E0ACE301E00C93B0E0ADE3C1
+:100200000C91B1E0A2E00C93B0E0AFE300E00C93FE
+:10021000B0E0AEE301E00C93B0E0ACE301E00C939E
+:10022000B0E0ADE30C91B1E0A3E00C93B1E0A1E04C
+:100230000C910F7E0C93B0E0ACE30C9101FD23C058
+:10024000B1E0A1E00C9100FF23C0B0E0AEE30C915F
+:10025000B0E0AFE31C91039509F413951C93B0E053
+:10026000AEE30C93B1E0A0E00C91B0E0ADE30C93F1
+:10027000B0E0ACE304E00C9306E00C93B1E0A1E045
+:100280000C910E7F0C93B1E0A1E00C9100610C93F6
+:10029000B1E0A1E00C9104FD53C0B0E0AFE300E099
+:1002A0000C93B0E0AEE300E00C93B0E0ACE301E00F
+:1002B0000C93B0E0ADE30C91B1E0A4E00C93B0E09E
+:1002C000AFE300E00C93B0E0AEE301E00C93B0E0EC
+:1002D000ACE301E00C93B0E0ADE30C91B1E0A5E03C
+:1002E0000C93B1E0A4E00C91B1E0A5E01C91B1E069
+:1002F000A2E02C91B1E0A3E03C910217130709F4AE
+:100300001FC0B1E0A1E00C9101600C93B1E0A3E04B
+:100310000C91B1E0A0E00C93B0E0AFE300E00C93EF
+:10032000B0E0AEE300E00C93B1E0A2E00C91B0E0ED
+:10033000ADE30C93B0E0ACE304E00C9306E00C9367
+:10034000B1E0A1E00C91B1E0A1E01C9101FF1B7FA5
+:1003500001FD14601C93B1E0A1E00C9102FF1DC0EF
+:10036000B0E0A7E200E00C93B0E0A6E205E80C9351
+:10037000B0E0A6E205EC0C93B0E0A6E20C9106FD1D
+:10038000FBCFB0E0A4E20C91B1E0A6E00C93B0E0AA
+:10039000A5E20C91B1E0A7E00C93B1E0A1E00C91D3
+:1003A000B1E0A1E01C9101FF1B7F01FD14601C93D3
+:1003B000B1E0A1E00C910F7D0C93B1E0A1E00C91B4
+:1003C000B1E0A1E01C9102FF1F7B02FD10641C93B1
+:1003D000B0E0A1E20C9100FD05C0B1E0A1E00C91FC
+:1003E0000F7B0C93B1E0A1E00C9106FF05C0B1E0DA
+:1003F000A1E00C9100620C93B1E0A1E00C91B1E09E
+:10040000A1E01C9102FF1F7B02FD10641C93B1E070
+:10041000A8E000E00C93B1E0A9E002E00C93B1E0A9
+:10042000A8E00C91B1E0A9E01C91B1E0A6E02C910C
+:10043000B1E0A7E03C91201731072CF4B1E0A1E036
+:100440000C910F7B0C93B1E0A1E00C9106FF05C06D
+:10045000B1E0A1E00C9100620C93B1E0A1E00C913D
+:10046000B1E0A1E01C9105FF1B7F05FD14601C930A
+:10047000B1E0A1E00C91B1E0A1E01C9102FF1F7E70
+:1004800002FD10611C93B1E0A1E00C9107FF05C0D3
+:10049000B1E0A1E00C910B7F0C93B1E0A1E00C91D5
+:1004A000B1E0A1E01C9104FF1F7704FD10681C93CC
+:1004B000B1E0A1E00C9102FF1CC0B1E0A8E001E0B6
+:1004C0000C93B1E0A9E000E00C93B1E0A2E02C9124
+:1004D000B1E0A3E03C91B1E0A8E00C91B1E0A9E06B
+:1004E0001C91200F311FB1E0A2E02C93B1E0A3E0FA
+:1004F0003C93B1E0A1E00C91B1E0A1E01C9101FFBF
+:100500001B7F01FD14601C93B1E0AAE00C910E7FEB
+:100510000C93B1E0A1E00C91B1E0AAE01C9102FFC4
+:100520001D7F02FD12601C93B1E0A8E00AE00C936D
+:10053000B1E0A9E000E00C93B1E0A8E00C91B1E0DB
+:10054000A9E01C91B1E0A2E02C91B1E0A3E03C91C4
+:10055000201731070CF405C0B1E0AAE00C910D7F23
+:100560000C93B1E0AAE00C9101FF08C0B1E0ABE050
+:1005700004E10C93B1E0ACE000E00C93B1E0AAE040
+:100580000C9101FF05C0B1E0AAE00C9101600C9351
+:10059000B1E0A1E00C91B1E0AAE01C9102FF1D7F47
+:1005A00002FD12601C93B1E0A8E00AE00C93B1E0F8
+:1005B000A9E000E00C93B1E0A8E00C91B1E0A9E063
+:1005C0001C91B1E0A2E02C91B1E0A3E03C91201796
+:1005D00031072CF4B1E0AAE00C910D7F0C93B1E04F
+:1005E000AAE00C9101FF08C0B1E0ABE002E30C937C
+:1005F000B1E0ACE000E00C93B1E0AAE00C9101FFA7
+:1006000005C0B1E0AAE00C9101600C93B1E0A1E05B
+:100610000C91B1E0AAE01C9102FF1D7F02FD126067
+:100620001C93B1E0AAE00C9101FF21C0B1E0ABE066
+:100630000C9110E030E02FEFEAE0FAE00995132F7B
+:10064000022F30E024E6EBE1FAE00995B0E0A3E404
+:100650000C93B1E0AAE00C9102FD09C0B1E0AAE060
+:100660000C9104600C93B0E0A5E40AE60C93B1E0B1
+:10067000AAE00C9101FF05C0B1E0AAE00C91016075
+:100680000C93B1E0AAE00C91B1E0A1E01C9100FF55
+:100690001B7F00FD14601C93B1E0A1E00C91B1E060
+:1006A000A1E01C9101FF1B7F01FD14601C93B1E0D0
+:1006B000A1E00C9102FF1EC0B1E0ADE00C91B1E0F1
+:1006C000AEE01C9123E630E00217130794F4B1E08A
+:1006D000ADE00C91B1E0AEE01C91039509F41395E7
+:1006E0001C93B1E0ADE00C93B1E0A1E00C910B7F65
+:1006F0000C9308C0B1E0ADE000E00C93B1E0AEE0D7
+:1007000000E00C93B1E0A1E00C91B1E0A1E01C91FC
+:1007100002FF1F7E02FD10611C93B1E0AAE00C9164
+:1007200003FF05C0B1E0A1E00C910B7F0C93B1E099
+:10073000A1E00C91B1E0AAE01C9104FF177F04FD39
+:1007400018601C93B1E0A1E00C9102FF0DC0B1E074
+:10075000AAE00C9104FD08C0B1E0AFE000E00C930A
+:10076000B1E0A0E100E00C93B1E0A1E00C91B1E0B8
+:10077000AAE01C9102FF1F7E02FD10611C93B1E0F4
+:10078000AFE00C91B1E0A1E10C93B1E0A0E10C91DC
+:10079000B1E0A2E10C93B1E0AFE00C91B1E0A0E1D7
+:1007A0001C9120E130E0021713070CF408C0B1E0FF
+:1007B000A1E10FEF0C93B1E0A2E10FEF0C93B1E0D8
+:1007C000A1E00C910F7E0C93B1E0A1E00C9104FF2D
+:1007D00006C0B1E0A4E00C91B0E0ACE90C93B1E04C
+:1007E000A1E00C910F7E0C93B0E0ABE90C9105FDFC
+:1007F00005C0B1E0A1E00C9100610C93B1E0A1E073
+:100800000C9104FF08C0B1E0A1E10FEF0C93B1E03F
+:10081000A2E10FEF0C93B1E0A4E000E00C93B1E093
+:10082000A5E000E00C93B1E0A4E00C91B1E0A5E0FC
+:100830001C91B1E0A1E12C91B1E0A2E13C91021741
+:10084000130741F4B1E0A8E003E70C93B1E0A9E09D
+:1008500000E00C93B1E0A4E001E00C93B1E0A5E06E
+:1008600000E00C93B1E0A4E00C91B1E0A5E01C9194
+:10087000B1E0A1E12C91B1E0A2E13C910217130794
+:1008800041F4B1E0A8E001E60C93B1E0A9E000E09A
+:100890000C93B1E0A4E002E00C93B1E0A5E000E02D
+:1008A0000C93B1E0A4E00C91B1E0A5E01C91B1E0A3
+:1008B000A1E12C91B1E0A2E13C910217130741F4B0
+:1008C000B1E0A8E006E70C93B1E0A9E000E00C93EA
+:1008D000B1E0A4E003E00C93B1E0A5E000E00C93EC
+:1008E000B1E0A4E00C91B1E0A5E01C91B1E0A1E180
+:1008F0002C91B1E0A2E13C910217130741F4B1E061
+:10090000A8E005E60C93B1E0A9E000E00C93B1E0AB
+:10091000A4E004E00C93B1E0A5E000E00C93B1E0AA
+:10092000A4E00C91B1E0A5E01C91B1E0A1E12C9113
+:10093000B1E0A2E13C910217130741F4B1E0A8E055
+:1009400004E60C93B1E0A9E000E00C93B1E0A4E070
+:1009500005E00C93B1E0A5E000E00C93B1E0A4E069
+:100960000C91B1E0A5E01C91B1E0A1E12C91B1E0C6
+:10097000A2E13C910217130741F4B1E0A8E000E2C4
+:100980000C93B1E0A9E000E00C93B1E0A4E006E034
+:100990000C93B1E0A5E000E00C93B1E0A4E00C9171
+:1009A000B1E0A5E01C91B1E0A1E12C91B1E0A2E1A0
+:1009B0003C910217130741F4B1E0A8E00DE30C935A
+:1009C000B1E0A9E000E00C93B1E0A4E007E00C93F3
+:1009D000B1E0A5E000E00C93B1E0A4E00C91B1E03F
+:1009E000A5E01C91B1E0A1E12C91B1E0A2E13C9124
+:1009F0000217130741F4B1E0A8E000E20C93B1E064
+:100A0000A9E000E00C93B1E0A4E008E00C93B1E0B1
+:100A1000A5E000E00C93B1E0A1E00C910F7E0C93F7
+:100A2000B1E0A4E00C91B1E0A5E01C91B1E0A1E13E
+:100A30002C91B1E0A2E13C910217130729F4B1E037
+:100A4000A1E00C9100610C93B1E0A1E00C9104FFD6
+:100A500043C0B1E0A2E00C91B1E0A3E10C93B1E09E
+:100A6000A3E00C91B1E0A4E10C93B1E0A8E000E2B6
+:100A70000C93B1E0A9E000E00C93B1E0A2E00C918E
+:100A8000B1E0A3E01C9120E030E00217130724F549
+:100A9000B1E0A8E00DE20C93B1E0A9E000E00C9316
+:100AA000B1E0A4E000E00C93B1E0A5E000E00C931D
+:100AB000B1E0A4E02C91B1E0A5E03C91B1E0A2E06E
+:100AC0000C91B1E0A3E01C91201B310BB1E0A3E13C
+:100AD0002C93B1E0A4E13C93B1E0A4E009E00C93D5
+:100AE000B1E0A5E000E00C93B1E0A1E00C910F7E35
+:100AF0000C93B1E0A4E00C91B1E0A5E01C91B1E051
+:100B0000A1E12C91B1E0A2E13C910217130729F475
+:100B1000B1E0A1E00C9100610C93B1E0A1E00C9177
+:100B200004FF89C0B1E0AAE00C9100620C93B1E02F
+:100B3000A4E000E10C93B1E0A5E007E20C93B1E082
+:100B4000A4E02C91B1E0A5E03C91B1E0A3E10C91CF
+:100B5000B1E0A4E11C91EBE1FAE00995B1E0A8E075
+:100B60000C93B1E0A9E01C93B1E0A4E02C91B1E0BA
+:100B7000A5E03C91B1E0A8E00C91B1E0A9E01C91A6
+:100B8000EAE0FAE00995B1E0A4E02C93B1E0A5E039
+:100B90003C93B1E0A3E12C91B1E0A4E13C91B1E040
+:100BA000A4E00C91B1E0A5E01C91201B310BB1E059
+:100BB000A3E12C93B1E0A4E13C93B1E0A4E000E315
+:100BC0000C93B1E0A5E000E00C93B1E0A8E02C911B
+:100BD000B1E0A9E03C91B1E0A4E00C91B1E0A5E066
+:100BE0001C91200F311FB1E0A8E02C93B1E0A9E0E7
+:100BF0003C93B1E0A4E00C91B1E0A5E01C91B1E020
+:100C0000A8E02C91B1E0A9E03C910217130771F420
+:100C1000B1E0AAE00C9105FF08C0B1E0A8E000E255
+:100C20000C93B1E0A9E000E00C9305C0B1E0AAE0AC
+:100C30000C910F7D0C93B1E0A4E00AE00C93B1E0BD
+:100C4000A5E000E00C93B1E0A1E00C910F7E0C93C5
+:100C5000B1E0A4E00C91B1E0A5E01C91B1E0A1E10C
+:100C60002C91B1E0A2E13C910217130729F4B1E005
+:100C7000A1E00C9100610C93B1E0A1E00C9104FFA4
+:100C800084C0B1E0A4E008EE0C93B1E0A5E003E07D
+:100C90000C93B1E0A4E02C91B1E0A5E03C91B1E06F
+:100CA000A3E10C91B1E0A4E11C91EBE1FAE009951C
+:100CB000B1E0A8E00C93B1E0A9E01C93B1E0A4E09E
+:100CC0002C91B1E0A5E03C91B1E0A8E00C91B1E03D
+:100CD000A9E01C91EAE0FAE00995B1E0A4E02C93C8
+:100CE000B1E0A5E03C93B1E0A3E12C91B1E0A4E137
+:100CF0003C91B1E0A4E00C91B1E0A5E01C91201B77
+:100D0000310BB1E0A3E12C93B1E0A4E13C93B1E05D
+:100D1000A4E000E30C93B1E0A5E000E00C93B1E0A7
+:100D2000A8E02C91B1E0A9E03C91B1E0A4E00C91E5
+:100D3000B1E0A5E01C91200F311FB1E0A8E02C9399
+:100D4000B1E0A9E03C93B1E0A4E00C91B1E0A5E0F2
+:100D50001C91B1E0A8E02C91B1E0A9E03C91021710
+:100D6000130771F4B1E0AAE00C9105FF08C0B1E0EF
+:100D7000A8E000E20C93B1E0A9E000E00C9305C00C
+:100D8000B1E0AAE00C910F7D0C93B1E0A4E00BE080
+:100D90000C93B1E0A5E000E00C93B1E0A1E00C9170
+:100DA0000F7E0C93B1E0A4E00C91B1E0A5E01C91A2
+:100DB000B1E0A1E12C91B1E0A2E13C91021713074F
+:100DC00029F4B1E0A1E00C9100610C93B1E0A1E045
+:100DD0000C9104FF84C0B1E0A4E004E60C93B1E000
+:100DE000A5E000E00C93B1E0A4E02C91B1E0A5E017
+:100DF0003C91B1E0A3E10C91B1E0A4E11C91EBE1E5
+:100E0000FAE00995B1E0A8E00C93B1E0A9E01C93E9
+:100E1000B1E0A4E02C91B1E0A5E03C91B1E0A8E004
+:100E20000C91B1E0A9E01C91EAE0FAE00995B1E08B
+:100E3000A4E02C93B1E0A5E03C93B1E0A3E12C91B8
+:100E4000B1E0A4E13C91B1E0A4E00C91B1E0A5E0F7
+:100E50001C91201B310BB1E0A3E12C93B1E0A4E184
+:100E60003C93B1E0A4E000E30C93B1E0A5E000E026
+:100E70000C93B1E0A8E02C91B1E0A9E03C91B1E085
+:100E8000A4E00C91B1E0A5E01C91200F311FB1E06E
+:100E9000A8E02C93B1E0A9E03C93B1E0A4E00C9170
+:100EA000B1E0A5E01C91B1E0A8E02C91B1E0A9E08F
+:100EB0003C910217130771F4B1E0AAE00C9105FF11
+:100EC00008C0B1E0A8E000E20C93B1E0A9E000E0C6
+:100ED0000C9305C0B1E0AAE00C910F7D0C93B1E03A
+:100EE000A4E00CE00C93B1E0A5E000E00C93B1E0CD
+:100EF000A1E00C910F7E0C93B1E0A4E00C91B1E065
+:100F0000A5E01C91B1E0A1E12C91B1E0A2E13C91FE
+:100F10000217130729F4B1E0A1E00C9100610C93D2
+:100F2000B1E0A1E00C9104FF84C0B1E0A4E00AE0CC
+:100F30000C93B1E0A5E000E00C93B1E0A4E02C91AB
+:100F4000B1E0A5E03C91B1E0A3E10C91B1E0A4E1F6
+:100F50001C91EBE1FAE00995B1E0A8E00C93B1E057
+:100F6000A9E01C93B1E0A4E02C91B1E0A5E03C9194
+:100F7000B1E0A8E00C91B1E0A9E01C91EAE0FAE050
+:100F80000995B1E0A4E02C93B1E0A5E03C93B1E079
+:100F9000A3E12C91B1E0A4E13C91B1E0A4E00C917B
+:100FA000B1E0A5E01C91201B310BB1E0A3E12C9333
+:100FB000B1E0A4E13C93B1E0A4E000E30C93B1E024
+:100FC000A5E000E00C93B1E0A8E02C91B1E0A9E02D
+:100FD0003C91B1E0A4E00C91B1E0A5E01C91200FA0
+:100FE000311FB1E0A8E02C93B1E0A9E03C93B1E05F
+:100FF000A4E00C91B1E0A5E01C91B1E0A8E02C9137
+:10100000B1E0A9E03C910217130771F4B1E0AAE046
+:101010000C9105FF08C0B1E0A8E000E20C93B1E03C
+:10102000A9E000E00C9305C0B1E0AAE00C910F7DAF
+:101030000C93B1E0A4E00DE00C93B1E0A5E000E07A
+:101040000C93B1E0A1E00C910F7E0C93B1E0A4E011
+:101050000C91B1E0A5E01C91B1E0A1E12C91B1E0CF
+:10106000A2E13C910217130729F4B1E0A1E00C9131
+:1010700000610C93B1E0A1E00C9104FF62C0B1E00B
+:10108000A4E001E00C93B1E0A5E000E00C93B1E036
+:10109000A4E02C91B1E0A5E03C91B1E0A3E10C917A
+:1010A000B1E0A4E11C91EBE1FAE00995B1E0A8E020
+:1010B0000C93B1E0A9E01C93B1E0A4E02C91B1E065
+:1010C000A5E03C91B1E0A8E00C91B1E0A9E01C9151
+:1010D000EAE0FAE00995B1E0A4E02C93B1E0A5E0E4
+:1010E0003C93B1E0A3E12C91B1E0A4E13C91B1E0EB
+:1010F000A4E00C91B1E0A5E01C91201B310BB1E004
+:10110000A3E12C93B1E0A4E13C93B1E0A4E000E3BF
+:101110000C93B1E0A5E000E00C93B1E0A8E02C91C5
+:10112000B1E0A9E03C91B1E0A4E00C91B1E0A5E010
+:101130001C91200F311FB1E0A8E02C93B1E0A9E091
+:101140003C93B1E0A4E00EE00C93B1E0A5E000E038
+:101150000C93B1E0A4E00C91B1E0A5E01C91B1E0EA
+:10116000A1E12C91B1E0A2E13C910217130741F4F7
+:10117000B1E0A8E00DE00C93B1E0A9E000E00C9331
+:10118000B1E0A4E00FE00C93B1E0A5E000E00C9327
+:10119000B1E0A4E00C91B1E0A5E01C91B1E0A1E1C7
+:1011A0002C91B1E0A2E13C910217130741F4B1E0A8
+:1011B000A8E00AE00C93B1E0A9E000E00C93B1E0F4
+:1011C000A1E10C91B1E0A2E11C9120E030E0021716
+:1011D00013070CF42CC0B1E0A1E00C9100610C935A
+:1011E000B1E0A1E00C9104FF06C0B1E0A8E00C91D1
+:1011F000B0E0ACE90C93B1E0A1E00C910F7E0C9350
+:10120000B0E0ABE90C9105FD05C0B1E0A1E00C91A7
+:1012100000610C93B1E0AFE00C91B1E0A0E11C9152
+:10122000039509F413951C93B1E0AFE00C93B1E082
+:10123000A1E00C910B7F0C93B1E0AFE00C91B1E019
+:10124000A0E11C9120E130E0021713072CF4B1E07B
+:10125000A1E00C9104600C93B1E0A1E00C91B1E02D
+:10126000A1E01C9101FF1B7F01FD14601C93B1E004
+:10127000A1E00C9102FF08C0B1E0A5E108E70C93E2
+:10128000B1E0A6E100E00C93B1E0A1E00C91B1E087
+:10129000A1E01C9101FF1B7F01FD14601C93B1E0D4
+:1012A000A1E00C9102FF1EC0B1E0A7E10C91B1E0FA
+:1012B000A8E11C9127EC30E00217130794F4B1E089
+:1012C000A7E10C91B1E0A8E11C91039509F41395F5
+:1012D0001C93B1E0A7E10C93B1E0A1E00C910B7F6E
+:1012E0000C9308C0B1E0A7E100E00C93B1E0A8E1E5
+:1012F00000E00C93B1E0A1E00C91B1E0A1E01C9101
+:1013000002FF1F7E02FD10611C93B1E0AAE00C9168
+:1013100006FF05C0B1E0A1E00C910B7F0C93B1E09A
+:10132000A1E00C91B1E0AAE01C9104FF1F7B04FD39
+:1013300010641C93B1E0A1E00C9102FF06C0B1E083
+:10134000A5E10C91B0E0ACE90C93B1E0A1E00C9107
+:101350000B7F0C93B0E0ABE90C9105FD05C0B1E04B
+:10136000A1E00C9104600C93B1E0A1E00C91B1E01C
+:10137000A1E01C9101FF1B7F01FD14601C93B1E0F3
+:10138000A1E00C9102FF19C0B1E0A1E00C910B7F2C
+:101390000C93B0E0ABE90C9107FF0FC0B1E0A1E006
+:1013A0000C9104600C93B0E0ACE90C91B1E0A9E1C0
+:1013B0000C93B1E0AAE100E00C93B1E0A8E001E6F3
+:1013C0000C93B1E0A9E000E00C93B1E0A9E10C912D
+:1013D000B1E0AAE11C91B1E0A8E02C91B1E0A9E054
+:1013E0003C910217130709F405C0B1E0A1E00C918C
+:1013F0000B7F0C93B1E0A1E00C9102FF08C0B1E0BB
+:10140000A2E000E00C93B1E0A3E000E00C93E1E97E
+:10141000F0E00994551B441B60E110F4400F511F8C
+:1014200020FD401B20FD510B55954795379527957D
+:101430006A9599F70895D12ED32617FF04C0109509
+:1014400000950F5F1F4F37FF04C0309520952F5F29
+:101450003F4FEE24FF1841E1001F111F4A9539F458
+:10146000D7FE04C0109500950F5F1F4F0895EE1C26
+:10147000FF1CE21AF30A20F4E20EF31E8894ECCF6C
+:041480000894EACF13
+:00000001FF
diff --git a/ldmicro/reg/expected/contacts.hex b/ldmicro/reg/expected/contacts.hex
new file mode 100644
index 0000000..5721d1b
--- /dev/null
+++ b/ldmicro/reg/expected/contacts.hex
@@ -0,0 +1,21 @@
+:020000040000FA
+:100000008A110A1208280000000000000000000009
+:10001000283084005830A0008001840AA00B0C28EE
+:10002000103095002730960000308E0000308F0091
+:10003000013090000B309700831686309F008312AA
+:10004000003085008316DF30850083120030860083
+:100050008316FF3086008312003087008316FC3041
+:10006000870083120C1D32280C116400A914A918F2
+:100070002915A91C291105183E2829112919071429
+:10008000291D0710A9182915A91C2911A911291919
+:100090002916291D291285184E282912291E512892
+:1000A000A91529192916291D2912051958282912B7
+:1000B000291E5B28A915A9192915A91D2911291D72
+:1000C00062288516A9182915A91C291185196928DE
+:1000D0002911051E6C282911A91229192917291D72
+:1000E0002913291F74288512291F7728A916291971
+:1000F0002917291D2913291F7F2887108028871475
+:10010000291F8328A916A91A2915A91E29118A01B0
+:02011000322893
+:02400E00723FFF
+:00000001FF
diff --git a/ldmicro/reg/expected/integers.hex b/ldmicro/reg/expected/integers.hex
new file mode 100644
index 0000000..3ecfe77
--- /dev/null
+++ b/ldmicro/reg/expected/integers.hex
@@ -0,0 +1,134 @@
+:1000000022C0189518951895189518951895189553
+:100010001895189518951895189518951895189578
+:100020001895189518951895189518951895189568
+:100030001895189518951895189518951895189558
+:10004000189518951895B0E0AEE504E00C93B0E073
+:10005000ADE50FEF0C93B5E0A0E000E020E034E068
+:10006000A150B0400C93215030402223C9F73323D4
+:10007000B9F7B0E0AAE304E00C93B0E0ABE300E032
+:100080000C93B0E0A7E300E00C93B0E0A8E301E03C
+:100090000C93B0E0A4E300E00C93B0E0A5E300E033
+:1000A0000C93B0E0A1E300E40C93B0E0A2E300E025
+:1000B0000C93B0E0A6E200E00C93B0E0A7E200E011
+:1000C0000C93B0E0AFE400E00C93B0E0AEE40AE0E3
+:1000D0000C93B0E0ABE404EF0C93B0E0AAE402E2CE
+:1000E0000C93B0E0A9E500E40C93B0E0A8E50C9116
+:1000F00006FFFBCFB0E0A8E50C9100640C93A89537
+:10010000B1E0A1E00C9102600C93B1E0A1E00C9190
+:10011000B1E0A1E01C9101FF1B7F01FD14601C9365
+:10012000B1E0A1E00C91077F0C93B1E0A1E00C914C
+:10013000B1E0A1E01C9102FF1F7E02FD10611C9343
+:10014000B0E0A6E30C9100FD05C0B1E0A1E00C9188
+:100150000F7E0C93B1E0A1E00C9104FF08C0B1E068
+:10016000A2E00BE20C93B1E0A3E005E00C93B1E058
+:10017000A1E00C9104FF05C0B1E0A1E00C91086082
+:100180000C93B1E0A1E00C91B1E0A1E01C9102FF61
+:100190001F7E02FD10611C93B0E0A6E30C9100FFEE
+:1001A00005C0B1E0A1E00C910F7E0C93B1E0A1E09D
+:1001B0000C9104FF08C0B1E0A2E004EF0C93B1E0A1
+:1001C000A3E00FEF0C93B1E0A1E00C9104FF05C098
+:1001D000B1E0A1E00C9108600C93B1E0A1E00C91BA
+:1001E000B1E0A1E01C9103FF1B7F03FD14601C9391
+:1001F000B1E0A1E00C91B1E0A1E01C9101FF1B7FF7
+:1002000001FD14601C93B1E0A1E00C910F7D0C93F3
+:10021000B1E0A1E00C91B1E0A1E01C9102FF1F7BD5
+:1002200002FD10641C93B1E0A1E00C9106FF1CC01C
+:10023000B1E0A4E008EE0C93B1E0A5E003E00C937C
+:10024000B1E0A2E02C91B1E0A3E03C91B1E0A4E0E8
+:100250000C91B1E0A5E01C91200F311FB1E0A6E0A8
+:100260002C93B1E0A7E03C93B1E0A1E00C9106FF34
+:1002700005C0B1E0A1E00C9100620C93B1E0A1E0F7
+:100280000C91B1E0A1E01C9102FF1F7B02FD106404
+:100290001C93B1E0A1E00C9106FF1CC0B1E0A4E00A
+:1002A00005E00C93B1E0A5E000E00C93B1E0A2E022
+:1002B0002C91B1E0A3E03C91B1E0A4E00C91B1E05D
+:1002C000A5E01C91201B310BB1E0A8E02C93B1E01C
+:1002D000A9E03C93B1E0A1E00C9106FF05C0B1E0BC
+:1002E000A1E00C9100620C93B1E0A1E00C91B1E0AF
+:1002F000A1E01C9102FF1F7B02FD10641C93B1E082
+:10030000A1E00C9106FF1DC0B1E0A4E003E00C9356
+:10031000B1E0A5E000E00C93B1E0A2E02C91B1E0E7
+:10032000A3E03C91B1E0A4E00C91B1E0A5E01C9108
+:10033000ECEEF3E00995B1E0AAE02C93B1E0ABE07C
+:100340003C93B1E0A1E00C9106FF05C0B1E0A1E053
+:100350000C9100620C93B1E0A1E00C91B1E0A1E03E
+:100360001C9102FF1F7B02FD10641C93B1E0A1E011
+:100370000C9106FF1DC0B1E0A4E003E00C93B1E0D6
+:10038000A5E000E00C93B1E0A4E02C91B1E0A5E081
+:100390003C91B1E0AAE00C91B1E0ABE01C91EDEF33
+:1003A000F3E00995B1E0ACE00C93B1E0ADE01C9353
+:1003B000B1E0A1E00C9106FF05C0B1E0A1E00C9115
+:1003C00000620C93B1E0A1E00C91B1E0A1E01C91BE
+:1003D00005FF1B7F05FD14601C93B1E0A1E00C91AB
+:1003E000B1E0A1E01C9101FF1B7F01FD14601C9393
+:1003F000B1E0A1E00C910F770C93B1E0A1E00C917A
+:10040000B1E0AEE01C9102FF1E7F02FD11601C9363
+:10041000B1E0A4E00BE20C93B1E0A5E005E00C93A1
+:10042000B1E0A2E00C91B1E0A3E01C91B1E0A4E046
+:100430002C91B1E0A5E03C910217130709F405C027
+:10044000B1E0AEE00C910E7F0C93B1E0AEE00C9108
+:1004500000FF05C0B1E0A1E00C9100680C93B1E091
+:10046000A1E00C91B1E0AEE01C9102FF1E7F02FD05
+:1004700011601C93B1E0AEE00C9100FF05C0B1E04B
+:10048000A1E00C9100680C93B1E0A1E00C91B1E007
+:10049000A1E01C9107FF1B7F07FD14601C93B1E0D6
+:1004A000AEE00C910D7F0C93B1E0A1E00C91B1E0B6
+:1004B000AEE01C9102FF1B7F02FD14601C93B1E0B3
+:1004C000A4E000E00C93B1E0A5E000E00C93B1E003
+:1004D000A4E00C91B1E0A5E01C91B1E0A6E02C9164
+:1004E000B1E0A7E03C91201731070CF405C0B1E062
+:1004F000AEE00C910B7F0C93B1E0AEE00C9102FFEB
+:1005000005C0B1E0AEE00C9102600C93B1E0A1E057
+:100510000C91B1E0AEE01C9102FF1B7F02FD146064
+:100520001C93B1E0A4E006E20C93B1E0A5E005E085
+:100530000C93B1E0A8E00C91B1E0A9E01C91B1E00E
+:10054000A4E02C91B1E0A5E03C91201731072CF4F8
+:10055000B1E0AEE00C910B7F0C93B1E0AEE00C91FA
+:1005600002FF05C0B1E0AEE00C9102600C93B1E077
+:10057000A1E00C91B1E0AEE01C9102FF1B7F02FDF7
+:1005800014601C93B1E0AEE00C9102FF05C0B1E035
+:10059000AEE00C9102600C93B1E0AEE00C91B1E0E2
+:1005A000A1E01C9101FF1B7F01FD14601C93B1E0D1
+:1005B000AEE00C91077F0C93B1E0A1E00C91B1E0AB
+:1005C000AEE01C9102FF1F7E02FD10611C93B1E0A2
+:1005D000A4E000E20C93B1E0A5E00EE40C93B1E0DE
+:1005E000A6E00C91B1E0A7E01C91B1E0A4E02C9151
+:1005F000B1E0A5E03C91201731070CF405C0B1E053
+:10060000AEE00C910F7E0C93B1E0AEE00C9104FFD4
+:1006100005C0B1E0AEE00C9108600C93B1E0A1E040
+:100620000C91B1E0AEE01C9102FF1F7E02FD106153
+:100630001C93B1E0A4E001E80C93B1E0A5E00FE069
+:100640000C93B1E0A4E00C91B1E0A5E01C91B1E005
+:10065000AAE02C91B1E0ABE03C91201731072CF4DB
+:10066000B1E0AEE00C910F7E0C93B1E0AEE00C91E6
+:1006700004FF05C0B1E0AEE00C9108600C93B1E05E
+:10068000A1E00C91B1E0AEE01C9102FF1F7E02FDE3
+:1006900010611C93B1E0AEE00C910F7E0C93B1E0C1
+:1006A000AEE00C9104FF05C0B1E0AEE00C91086033
+:1006B0000C93B1E0AEE00C91B1E0A1E01C9103FF1E
+:1006C0001B7F03FD14601C93B1E0A1E00C91B0E02E
+:1006D000ABE31C9102FF1B7F02FD14601C93B1E091
+:1006E000A1E00C91B1E0A1E01C9101FF1B7F01FD95
+:1006F00014601C93B1E0A4E00CED0C93B1E0A5E014
+:100700000FEF0C93B1E0AAE00C91B1E0ABE01C91CB
+:10071000B1E0A4E02C91B1E0A5E03C9102171307F1
+:1007200029F4B1E0A1E00C910B7F0C93B1E0A4E0BF
+:1007300000E00C93B1E0A5E000E00C93B1E0A2E092
+:100740000C91B1E0A3E01C91B1E0A4E02C91B1E0E8
+:10075000A5E03C91201731070CF405C0B1E0A1E001
+:100760000C910B7F0C93B1E0A1E00C9102FF1EC035
+:10077000B1E0AFE00C91B1E0A0E11C9123E130E0E9
+:100780000217130794F4B1E0AFE00C91B1E0A0E1DF
+:100790001C91039509F413951C93B1E0AFE00C9301
+:1007A000B1E0A1E00C910B7F0C9308C0B1E0AFE089
+:1007B00000E00C93B1E0A0E100E00C93B1E0A1E017
+:1007C0000C91B0E0A2E31C9102FF1F7B02FD1064BC
+:1007D0001C93E5E7F0E00994551B441B60E110F41D
+:1007E000400F511F20FD401B20FD510B5595479593
+:1007F000379527956A9599F70895D12ED32617FF37
+:1008000004C0109500950F5F1F4F37FF04C030954F
+:1008100020952F5F3F4FEE24FF1841E1001F111F6D
+:100820004A9539F4D7FE04C0109500950F5F1F4F0D
+:100830000895EE1CFF1CE21AF30A20F4E20EF31EE8
+:080840008894ECCF0894EACF84
+:00000001FF
diff --git a/ldmicro/reg/expected/misc-ops.hex b/ldmicro/reg/expected/misc-ops.hex
new file mode 100644
index 0000000..7166fb0
--- /dev/null
+++ b/ldmicro/reg/expected/misc-ops.hex
@@ -0,0 +1,69 @@
+:020000040000FA
+:100000008A110A1208280000000000000000000009
+:10001000283084005830A0008001840AA00B0C28EE
+:10002000103095002730960000308E0000308F0091
+:10003000013090000B309700831686309F008312AA
+:10004000003085008316FD30850083120030860065
+:100050008316FF3086008312003087008316FD3040
+:10006000870083120C1D32280C116400A914A918F2
+:100070002915A91C291105183E282911A91842285B
+:10008000A91446282919A914291DA910A918291548
+:10009000A91C2911A9194D282911291D64280030EE
+:1000A0002B0203195A28A00020092B05A006A01B2B
+:1000B0005F28632831302A02031C5F286328AA0FB7
+:1000C0006228AB0A291168280030AA000030AB0072
+:1000D000291A6E283130AC000030AD0029162919DC
+:1000E000862800302D0203197C28A00020092D0548
+:1000F000A006A01B8128852831302C02031C8128F2
+:100100008528AC0F8428AD0A29158A280030AC0058
+:100110000030AD00291D8E28A9118F28A915A91816
+:100120002915A91C2911A91996282911291DB228B8
+:10013000A91AB2283008AE003108AF003208B0006A
+:100140003308B1003408B2003508B3003608B400F3
+:100150003708B5003808B6003908B7003A08B800C3
+:100160003B08B9002919A916291DA912A918291592
+:10017000A91C291129132919A917291DA913A91F78
+:10018000D82851309F00831680309F00831206309C
+:10019000A100A10BC9281F151F19CC281E08BD00DE
+:1001A00083161E088312BC00831686309F008312BC
+:1001B000A91FDB2829172919A917291DA913A91F68
+:1001C000E5283C08BA003D08BB00A91FE82829170C
+:1001D000291B2915291F2911A9182915A91C29111D
+:1001E0003E102919BE14291DBE10A919F828BE10E9
+:1001F000BE1C14293E191429BF0FFF28C00A003065
+:10020000400203190A29A00020094005A006A01BEE
+:100210000F29102905303F02031C0F29102914292A
+:100220000030BF000030C000BE183E15BE1C3E119D
+:10023000BE1C1B293E142919BE14291DBE10BE1C4C
+:1002400071290030C1000030C2003F084102031D87
+:10025000312940084202031D31290A30C300003011
+:10026000C4000130C1000030C2003F084102031D3C
+:10027000412940084202031D41294630C300003095
+:10028000C4000230C1000030C2003F084102031D1B
+:10029000512940084202031D51295030C30000304B
+:1002A000C4000330C1000030C2003F084102031DFA
+:1002B000612940084202031D61294630C300003015
+:1002C000C4000430C1000030C2003F084102031DD9
+:1002D000712940084202031D71290A30C300003011
+:1002E000C400BE1C74293E142919BE14291DBE1059
+:1002F000BE1C9E294308A000A1016430A200A301F6
+:1003000001308A00BB2101308A002308A1002208A5
+:10031000A0006430A200A30101308A00D221013084
+:100320008A0020089B00BE199E29BE1583166330E3
+:10033000920083120C309D0004309200BE1CA12953
+:100340003E143E1829153E1C2911A9182915A91C6F
+:100350002911A918AD29A914B1292919A914291DF0
+:10036000A910A9182915A91C291129198514291DB5
+:1003700085108A013228A501A4010310A30CA20C48
+:100380001030A600031CCA292008A4070318A50AD8
+:100390002108A5070310A50CA40CA30CA20CA60B06
+:1003A000C229080021082306A700A31FDC29A209EF
+:1003B000A309A20A0319A30AA11FE329A009A109FD
+:1003C000A00A0319A10AA501A40103101130A60077
+:1003D000A00DA10DA6030319012AA40DA50D220845
+:1003E000A402031CA5032308A502A51FFF292208B8
+:1003F000A4070318A50A2308A5070310E829031476
+:10040000E829A71F0800A009A109A00A0319A10A49
+:020410000800E2
+:02400E00723FFF
+:00000001FF
diff --git a/ldmicro/reg/expected/pic16-hw.hex b/ldmicro/reg/expected/pic16-hw.hex
new file mode 100644
index 0000000..c403acf
--- /dev/null
+++ b/ldmicro/reg/expected/pic16-hw.hex
@@ -0,0 +1,189 @@
+:020000040000FA
+:100000008A110A1208280000000000000000000009
+:10001000283084005830A0008001840AA00B0C28EE
+:10002000103095002730960000308E0000308F0091
+:10003000013090000B309700831686309F008312AA
+:10004000003085008316FF30850083120030860063
+:100050008316FF3086008312003087008316FD3040
+:1000600087008312003088008316FF3088008312D7
+:1000700000308900831607308900831283161930F7
+:1000800099008312831620309800831290309800D4
+:100090000C1D48280C116400A914A9182915A91CC5
+:1000A0002911291DD928A91989282912831603176E
+:1000B0008C186F2883120313291C722803178D0ACA
+:1000C0000313280803178C0083168C130C15553066
+:1000D0008D00AA308D008C14831203132910831213
+:1000E00003132916291A8928A915031700308D0032
+:1000F00083168C130C1483120C080313AA00031725
+:1001000001308D0083168C130C1483120C0803131A
+:10011000AB002912831603178C18A328831203132C
+:10012000291CA62803178D0A0313280803178C001F
+:1001300083168C130C1555308D00AA308D008C144D
+:10014000831203132910831203132916291AD9289D
+:10015000031700308D0083168C130C1483120C08C7
+:100160000313AC00031701308D0083168C130C149D
+:1001700083120C080313AD002C082A02031DC528A6
+:100180002D082B02031DC528D92829142B08A800E7
+:10019000031700308D0003132A0803178C00831601
+:1001A0008C130C1555308D00AA308D008C148312E1
+:1001B0000313A9182915A91C2911291DF62849304E
+:1001C0009F00831680309F0083120630A100A10B90
+:1001D000E7281F151F19EA281E08AF0083161E08FE
+:1001E0008312AE00831686309F008312A91829154A
+:1001F000A91C2911A91229192917291D2913051824
+:1002000002292913291F0529A91629192917291D8A
+:1002100029130030B0000230B1003109A1002F05D0
+:10022000A0002F08A104A10931082F0203192129D8
+:10023000A105A200220920052104A206A21B26294D
+:10024000272930082E02031C262927292913291FB4
+:100250002A29A916A91A2915A91E29112919291609
+:10026000291D2912A91F35292911291AA917291E63
+:10027000A913291D4A290130B0000030B1002A0815
+:100280003007AA002010031820142B083107AB00F8
+:100290002018AB0AA9182915A91C291132102919EF
+:1002A000B214291DB2100A30B0000030B10031097B
+:1002B000A1002B05A0002B08A104A10931082B02E5
+:1002C00003196B29A105A200220920052104A20619
+:1002D000A21B7029712930082A02031C7029712978
+:1002E0007229B210B21C78291430B3000030B40067
+:1002F000B21C7B2932142919B214291DB2100A30FC
+:10030000B0000030B1003109A1002B05A0002B087E
+:10031000A104A10931082B0203199729A105A20004
+:10032000220920052104A206A21B9C299D29300830
+:100330002A02031C9C299D29B210B21CA329323029
+:10034000B3000030B400B21CA62932142919B2142B
+:10035000291DB210B21CD0293308A000A1017D30A4
+:10036000A200A30105308A00812501308A002308FC
+:10037000A1002208A0006430A200A30105308A0079
+:10038000982501308A0020089B003219D0293215A7
+:1003900083167C30920083120C309D000530920051
+:1003A000B21CD329321432182915321C2911A9186C
+:1003B0002915A91C2911291DF2290030360203191B
+:1003C000E829A00020093605A006A01BED29F12987
+:1003D00063303502031CED29F129B50FF029B60A67
+:1003E0002911F6290030B5000030B6002919291668
+:1003F000291D2912B21DFD292911291AB215291EFC
+:10040000B211291D092A321A092A0030B70000301A
+:10041000B80029193216291D32123708B9003808D8
+:10042000BA000030380203191C2AA0002009380540
+:10043000A006A01B212A222A10303702031C212AE1
+:10044000222A262AFF30B900FF30BA002912291EBD
+:100450002B2A2C089900291283169818312A831206
+:1004600029168312291E382AFF30B900FF30BA003E
+:100470000030AC000030AD002C083902031D482AC2
+:100480002D083A02031D482A7330B0000030B10035
+:100490000130AC000030AD002C083902031D582A91
+:1004A0002D083A02031D582A6130B0000030B10017
+:1004B0000230AC000030AD002C083902031D682A60
+:1004C0002D083A02031D682A7630B0000030B100D2
+:1004D0000330AC000030AD002C083902031D782A2F
+:1004E0002D083A02031D782A6530B0000030B100B3
+:1004F0000430AC000030AD002C083902031D882AFE
+:100500002D083A02031D882A6430B0000030B10083
+:100510000530AC000030AD002C083902031D982ACC
+:100520002D083A02031D982A2030B0000030B10097
+:100530000630AC000030AD002C083902031DA82A9B
+:100540002D083A02031DA82A3D30B0000030B1004A
+:100550000730AC000030AD002C083902031DB82A6A
+:100560002D083A02031DB82A2030B0000030B10037
+:100570000830AC000030AD0029122C083902031DF0
+:10058000C62A2D083A02031DC62A2916291EF32A57
+:100590002A08BB002B08BC002030B0000030B1009E
+:1005A00000302B020319DB2AA00020092B05A0062E
+:1005B000A01BE02AF32A00302A02031CE02AF32AB7
+:1005C0002D30B0000030B1000030AC000030AD0084
+:1005D0002A082C02BB002010031820142B082D021F
+:1005E000BC00201CBC030930AC000030AD00291257
+:1005F0002C083902031D012B2D083A02031D012B83
+:100600002916291E542BB2161030AC002730AD002D
+:100610003B08A0003C08A1002C08A2002D08A30064
+:1006200005308A00982503308A002008B000210890
+:10063000B1002C08A0002D08A1003008A20031084C
+:10064000A30005308A00812503308A002208AC000F
+:100650002308AD002C083B02BB0020100318201417
+:100660002D083C02BC00201CBC033030AC00003024
+:10067000AD0030082C07B0002010031820143108FA
+:100680002D07B1002018B10A2C083002031D532B8E
+:100690002D083102031D532BB21E522B2030B00007
+:1006A0000030B100542BB2120A30AC000030AD0063
+:1006B00029122C083902031D622B2D083A02031D52
+:1006C000622B2916291EB42BE830AC000330AD0094
+:1006D0003B08A0003C08A1002C08A2002D08A300A4
+:1006E00005308A00982503308A002008B0002108D0
+:1006F000B1002C08A0002D08A1003008A20031088C
+:10070000A30005308A00812503308A002208AC004E
+:100710002308AD002C083B02BB0020100318201456
+:100720002D083C02BC00201CBC033030AC00003063
+:10073000AD0030082C07B000201003182014310839
+:100740002D07B1002018B10A2C083002031DB32B6D
+:100750002D083102031DB32BB21EB22B2030B00086
+:100760000030B100B42BB2120B30AC000030AD0041
+:1007700029122C083902031DC22B2D083A02031D31
+:10078000C22B2916291E142C6430AC000030AD0099
+:100790003B08A0003C08A1002C08A2002D08A300E3
+:1007A00005308A00982503308A002008B00021080F
+:1007B000B1002C08A0002D08A1003008A2003108CB
+:1007C000A30005308A00812503308A002208AC008E
+:1007D0002308AD002C083B02BB0020100318201496
+:1007E0002D083C02BC00201CBC033030AC000030A3
+:1007F000AD0030082C07B000201003182014310879
+:100800002D07B1002018B10A2C083002031D132C4B
+:100810002D083102031D132CB21E122C2030B00003
+:100820000030B100142CB2120C30AC000030AD001E
+:1008300029122C083902031D222C2D083A02031D0F
+:10084000222C2916291E742C0A30AC000030AD0071
+:100850003B08A0003C08A1002C08A2002D08A30022
+:1008600005308A00982504308A002008B00021084D
+:10087000B1002C08A0002D08A1003008A20031080A
+:10088000A30005308A00812504308A002208AC00CC
+:100890002308AD002C083B02BB00201003182014D5
+:1008A0002D083C02BC00201CBC033030AC000030E2
+:1008B000AD0030082C07B0002010031820143108B8
+:1008C0002D07B1002018B10A2C083002031D732C2B
+:1008D0002D083102031D732CB21E722C2030B00083
+:1008E0000030B100742CB2120D30AC000030AD00FD
+:1008F00029122C083902031D822C2D083A02031DEF
+:10090000822C2916291EC42C0130AC000030AD0009
+:100910003B08A0003C08A1002C08A2002D08A30061
+:1009200005308A00982504308A002008B00021088C
+:10093000B1002C08A0002D08A1003008A200310849
+:10094000A30005308A00812504308A002208AC000B
+:100950002308AD002C083B02BB0020100318201414
+:100960002D083C02BC00201CBC033030AC00003021
+:10097000AD0030082C07B0002010031820143108F7
+:100980002D07B1002018B10A0E30AC000030AD00C8
+:100990002C083902031DD42C2D083A02031DD42C37
+:1009A0000D30B0000030B1000F30AC000030AD00B1
+:1009B0002C083902031DE42C2D083A02031DE42CF7
+:1009C0000A30B0000030B10000303A020319EF2CB9
+:1009D000A00020093A05A006A01BF42CF52C00303D
+:1009E0003902031CF42CF52C042D2916291EFA2C8F
+:1009F00030089900291283169818002D83122916A1
+:100A00008312B70F042DB80A2911003038020319D8
+:100A1000102DA00020093805A006A01B152D162DAD
+:100A200010303702031C152D162D2915A91829156C
+:100A3000A91C2911291D202D7830BD000030BE00D1
+:100A4000A9182915A91C2911291D3B2D0030400288
+:100A50000319312DA00020094005A006A01B362D4A
+:100A60003A2DC7303F02031C362D3A2DBF0F392DCA
+:100A7000C00A29113F2D0030BF000030C0002919E5
+:100A80002916291D2912321F462D2911291A32171C
+:100A9000291E3213291D4E2D3D0899002911831658
+:100AA0009818542D831229158312A9182915A91CE9
+:100AB0002911291D6B2D29118C1E6B2D1A08C100BF
+:100AC000C20129159818672D1819672D6B2D1A0862
+:100AD0001A08181218166130B0000030B100410831
+:100AE0003002031D782D42083102031D782D792D27
+:100AF0002911291D7F2D0030AA000030AB008A018A
+:100B00004828A501A4010310A30CA20C1030A600D4
+:100B1000031C902D2008A4070318A50A2108A50787
+:100B20000310A50CA40CA30CA20CA60B882D080086
+:100B300021082306A700A31FA22DA209A309A20A28
+:100B40000319A30AA11FA92DA009A109A00A03192D
+:100B5000A10AA501A40103101130A600A00DA10D4A
+:100B6000A6030319C72DA40DA50D2208A402031C7A
+:100B7000A5032308A502A51FC52D2208A407031855
+:100B8000A50A2308A5070310AE2D0314AE2DA71F39
+:0E0B90000800A009A109A00A0319A10A080083
+:02400E00723FFF
+:00000001FF
diff --git a/ldmicro/reg/expected/timers-counters.hex b/ldmicro/reg/expected/timers-counters.hex
new file mode 100644
index 0000000..0e21b50
--- /dev/null
+++ b/ldmicro/reg/expected/timers-counters.hex
@@ -0,0 +1,119 @@
+:1000000022C0189518951895189518951895189553
+:100010001895189518951895189518951895189578
+:100020001895189518951895189518951895189568
+:100030001895189518951895189518951895189558
+:10004000189518951895B0E0AEE500E10C93B0E076
+:10005000ADE50FEF0C93B1E1A0E000E020E030E16E
+:10006000A150B0400C93215030402223C9F73323D4
+:10007000B9F7B0E0AAE300E20C93B0E0ABE300E034
+:100080000C93B0E0A7E300E00C93B0E0A8E300E03D
+:100090000C93B0E0A4E300E00C93B0E0A5E300E033
+:1000A0000C93B0E0A1E304E00C93B0E0A2E300E025
+:1000B0000C93B0E0A2E200E00C93B0E0A3E207E012
+:1000C0000C93B0E0A1E600E20C93B0E0A2E600E001
+:1000D0000C93B0E0A4E600E00C93B0E0A5E600E0ED
+:1000E0000C93B0E0AFE400E00C93B0E0AEE409E0C4
+:1000F0000C93B0E0ABE40CE90C93B0E0AAE40EE39F
+:100100000C93B0E0A7E500E10C93B0E0A6E50C91FC
+:1001100004FFFBCFB0E0A6E50C9100610C93A8951D
+:10012000B1E0A1E00C9102600C93B1E0A1E00C9170
+:10013000B1E0A1E01C9101FF1B7F01FD14601C9345
+:10014000B1E0A1E00C9103FD05C0B1E0A1E00C918C
+:100150000B7F0C93B1E0A1E00C9104FD08C0B1E06D
+:10016000A2E009E00C93B1E0A3E000E00C93B1E061
+:10017000A1E00C9100610C93B1E0A1E00C9102FDB3
+:100180001EC0B1E0A2E00C91B1E0A3E01C9129E017
+:1001900030E00217130794F4B1E0A2E00C91B1E053
+:1001A000A3E01C91039509F413951C93B1E0A2E020
+:1001B0000C93B1E0A1E00C9104600C9308C0B1E095
+:1001C000A2E000E00C93B1E0A3E000E00C93B1E00A
+:1001D000A1E00C9102FF1EC0B1E0A4E00C91B1E0DF
+:1001E000A5E01C9129E030E00217130794F4B1E078
+:1001F000A4E00C91B1E0A5E01C91039509F41395DE
+:100200001C93B1E0A4E00C93B1E0A1E00C910B7F52
+:100210000C9308C0B1E0A4E000E00C93B1E0A5E0CD
+:1002200000E00C93B1E0A1E00C9102FF06C0B1E048
+:10023000A1E00C91077F0C9305C0B1E0A1E00C9107
+:1002400008600C93B1E0A1E00C91B1E0A1E01C9139
+:1002500001FF1B7F01FD14601C93B1E0A1E00C9134
+:1002600003FD05C0B1E0A1E00C910B7F0C93B0E061
+:10027000A1E20C9102FD05C0B1E0A1E00C910B7F61
+:100280000C93B1E0A1E00C9102FF12C0B1E0A1E03B
+:100290000C9105FD0DC0B1E0A6E00C91B1E0A7E026
+:1002A0001C91039509F413951C93B1E0A6E00C93FF
+:1002B000B1E0A1E00C91B1E0A1E01C9102FF1F7D33
+:1002C00002FD10621C93B1E0A6E00C91B1E0A7E042
+:1002D0001C9124E130E00217130734F4B1E0A1E0EF
+:1002E0000C910B7F0C9305C0B1E0A1E00C91046070
+:1002F0000C93B1E0A8E00C91B1E0A9E01C9127EECD
+:1003000033E002171307C4F4B1E0A1E00C9102FF3F
+:100310000DC0B1E0A8E00C91B1E0A9E01C910395FB
+:1003200009F413951C93B1E0A8E00C93B1E0A1E0AF
+:100330000C910B7F0C9305C0B1E0A1E00C9104601F
+:100340000C93B1E0A1E00C91B0E0A2E61C9102FF99
+:100350001F7D02FD10621C93B1E0A1E00C91B1E0A1
+:10036000A1E01C9101FF1B7F01FD14601C93B1E013
+:10037000A1E00C9103FD05C0B1E0A1E00C910B7F61
+:100380000C93B0E0A1E20C9100FD05C0B1E0A1E04A
+:100390000C910B7F0C93B1E0A1E00C9102FF21C006
+:1003A000B1E0A1E00C9106FD1CC0B1E0AAE001E0C3
+:1003B0000C93B1E0ABE000E00C93B1E0A6E02C912F
+:1003C000B1E0A7E03C91B1E0AAE00C91B1E0ABE074
+:1003D0001C91201B310BB1E0A6E02C93B1E0A7E00B
+:1003E0003C93B1E0A1E00C91B1E0A1E01C9102FFCF
+:1003F0001F7B02FD10641C93B1E0A6E00C91B1E0FC
+:10040000A7E01C912AE030E00217130734F4B1E0B2
+:10041000A1E00C910B7F0C9305C0B1E0A1E00C9121
+:1004200004600C93B1E0A1E00C91B0E0A2E31C9158
+:1004300002FF1B7F02FD14601C93B1E0A1E00C9150
+:10044000B1E0A1E01C9101FF1B7F01FD14601C9332
+:10045000B0E0A1E20C9101FD05C0B1E0A1E00C917A
+:100460000B7F0C93B1E0A1E00C910F770C93B1E0FE
+:10047000A1E00C91B1E0ACE01C9102FF1E7F02FDF7
+:1004800011601C93B1E0ACE00C91B1E0ACE01C91C8
+:1004900000FF1D7F00FD12601C93B1E0ACE00C91E9
+:1004A00002FF05C0B1E0ACE00C910E7F0C93B1E00F
+:1004B000ACE00C91B1E0ACE01C9101FF1B7F01FDB1
+:1004C00014601C93B1E0ACE00C9100FF05C0B1E0FA
+:1004D000A1E00C9100680C93B1E0A1E00C91B1E0B7
+:1004E000ACE01C9102FF1E7F02FD11601C93B1E085
+:1004F000ACE00C91B1E0ACE01C9100FF1D7F00FD71
+:1005000012601C93B1E0ACE00C9100FD0BC0B1E0B7
+:10051000ACE00C9103FF05C0B1E0ACE00C910160D0
+:100520000C9305C0B1E0ACE00C910E7F0C93B1E0F0
+:10053000ACE00C91B1E0ACE01C9101FF177F01FD34
+:1005400018601C93B1E0ACE00C9100FF05C0B1E075
+:10055000A1E00C9100680C93B1E0A1E00C91B1E036
+:10056000A1E01C9107FF1B7F07FD14601C93B1E005
+:10057000A1E00C9102FF08C0B1E0A8E000E00C93FC
+:10058000B1E0A9E000E00C93B1E0A1E00C91B1E092
+:10059000A1E01C9101FF1B7F01FD14601C93B1E0E1
+:1005A000A1E00C9103FD05C0B1E0A1E00C910B7F2F
+:1005B0000C93B1E0A1E00C9102FF26C0B1E0ACE0E9
+:1005C0000C9104FD21C0B1E0ADE00C91B1E0AEE0D2
+:1005D0001C91039509F413951C93B1E0ADE00C93C5
+:1005E000B1E0ADE00C91B1E0AEE01C9128E030E06C
+:1005F000021713070CF408C0B1E0ADE000E00C9363
+:10060000B1E0AEE000E00C93B1E0A1E00C91B1E00C
+:10061000ACE01C9102FF1F7E02FD10611C93B1E053
+:10062000A1E00C91B1E0A1E01C9101FF1B7F01FD55
+:1006300014601C93B1E0AFE003E00C93B1E0A0E1E3
+:1006400000E00C93B1E0ADE00C91B1E0AEE01C91A4
+:10065000B1E0AFE02C91B1E0A0E13C9102171307AB
+:1006600009F405C0B1E0A1E00C910B7F0C93B1E05F
+:10067000A1E00C91B1E0ACE01C9102FF1D7F02FDF6
+:1006800012601C93B1E0A1E00C9102FD0BC0B1E03F
+:10069000ACE00C9105FF05C0B1E0A1E00C91046055
+:1006A0000C9305C0B1E0A1E00C910B7F0C93B1E07D
+:1006B000ACE00C91B1E0ACE01C9101FF1F7D01FDAD
+:1006C00010621C93B1E0ACE00C9106FD08C0B1E0F3
+:1006D000A1E103E10C93B1E0A2E100E00C93B1E0F1
+:1006E000ACE00C9100640C93B1E0A1E00C9102FD30
+:1006F0001EC0B1E0A1E10C91B1E0A2E11C9123E1A7
+:1007000030E00217130794F4B1E0A1E10C91B1E0DD
+:10071000A2E11C91039509F413951C93B1E0A1E1AA
+:100720000C93B1E0A1E00C9104600C9308C0B1E01F
+:10073000A1E100E00C93B1E0A2E100E00C93B1E094
+:10074000A1E00C91B0E0ABE31C9102FF1F7D02FD24
+:0A07500010621C93E5E8F0E0099444
+:00000001FF
diff --git a/ldmicro/reg/go.bat b/ldmicro/reg/go.bat
new file mode 100644
index 0000000..b62bc59
--- /dev/null
+++ b/ldmicro/reg/go.bat
@@ -0,0 +1 @@
+@perl run-tests.pl
diff --git a/ldmicro/reg/results/avr-hw.hex b/ldmicro/reg/results/avr-hw.hex
new file mode 100644
index 0000000..fb676da
--- /dev/null
+++ b/ldmicro/reg/results/avr-hw.hex
@@ -0,0 +1,330 @@
+:1000000022C0189518951895189518951895189553
+:100010001895189518951895189518951895189578
+:100020001895189518951895189518951895189568
+:100030001895189518951895189518951895189558
+:10004000189518951895B0E0AEE500E10C93B0E076
+:10005000ADE50FEF0C93B1E1A0E000E020E030E16E
+:10006000A150B0400C93215030402223C9F73323D4
+:10007000B9F7B0E0A8E900E00C93B0E0A9E907E621
+:100080000C93B0E0AAE908E10C93B0E0AAE300E029
+:100090000C93B0E0ABE300E00C93B0E0A7E300E822
+:1000A0000C93B0E0A8E300E00C93B0E0A4E300E020
+:1000B0000C93B0E0A5E300E00C93B0E0A1E308E00E
+:1000C0000C93B0E0A2E300E00C93B0E0A2E200E009
+:1000D0000C93B0E0A3E201E00C93B0E0A1E600E0F5
+:1000E0000C93B0E0A2E600E00C93B0E0A4E600E0E0
+:1000F0000C93B0E0A5E600E00C93B0E0AFE400E0C4
+:100100000C93B0E0AEE409E00C93B0E0ABE40CE992
+:100110000C93B0E0AAE40EE30C93B0E0A7E500E195
+:100120000C93B0E0A6E50C9104FFFBCFB0E0A6E590
+:100130000C9100610C93A895B1E0A1E00C910260D4
+:100140000C93B1E0A1E00C91B1E0A1E01C9101FFA2
+:100150001B7F01FD14601C93B1E0A1E00C9102FF34
+:10016000EFC0B1E0A1E00C9103FD60C0B1E0A1E0FF
+:100170000C910F7E0C93B0E0ACE30C9101FD23C019
+:10018000B1E0A1E00C9100FF23C0B0E0AEE30C9120
+:10019000B0E0AFE31C91039509F413951C93B0E014
+:1001A000AEE30C93B1E0A0E00C91B0E0ADE30C93B2
+:1001B000B0E0ACE304E00C9306E00C93B1E0A1E006
+:1001C0000C910E7F0C93B1E0A1E00C9100610C93B7
+:1001D000B1E0A1E00C9104FD29C0B1E0A1E00C91D7
+:1001E00008600C93B0E0AFE300E00C93B0E0AEE346
+:1001F00000E00C93B0E0ACE301E00C93B0E0ADE3C1
+:100200000C91B1E0A2E00C93B0E0AFE300E00C93FE
+:10021000B0E0AEE301E00C93B0E0ACE301E00C939E
+:10022000B0E0ADE30C91B1E0A3E00C93B1E0A1E04C
+:100230000C910F7E0C93B0E0ACE30C9101FD23C058
+:10024000B1E0A1E00C9100FF23C0B0E0AEE30C915F
+:10025000B0E0AFE31C91039509F413951C93B0E053
+:10026000AEE30C93B1E0A0E00C91B0E0ADE30C93F1
+:10027000B0E0ACE304E00C9306E00C93B1E0A1E045
+:100280000C910E7F0C93B1E0A1E00C9100610C93F6
+:10029000B1E0A1E00C9104FD53C0B0E0AFE300E099
+:1002A0000C93B0E0AEE300E00C93B0E0ACE301E00F
+:1002B0000C93B0E0ADE30C91B1E0A4E00C93B0E09E
+:1002C000AFE300E00C93B0E0AEE301E00C93B0E0EC
+:1002D000ACE301E00C93B0E0ADE30C91B1E0A5E03C
+:1002E0000C93B1E0A4E00C91B1E0A5E01C91B1E069
+:1002F000A2E02C91B1E0A3E03C910217130709F4AE
+:100300001FC0B1E0A1E00C9101600C93B1E0A3E04B
+:100310000C91B1E0A0E00C93B0E0AFE300E00C93EF
+:10032000B0E0AEE300E00C93B1E0A2E00C91B0E0ED
+:10033000ADE30C93B0E0ACE304E00C9306E00C9367
+:10034000B1E0A1E00C91B1E0A1E01C9101FF1B7FA5
+:1003500001FD14601C93B1E0A1E00C9102FF1DC0EF
+:10036000B0E0A7E200E00C93B0E0A6E205E80C9351
+:10037000B0E0A6E205EC0C93B0E0A6E20C9106FD1D
+:10038000FBCFB0E0A4E20C91B1E0A6E00C93B0E0AA
+:10039000A5E20C91B1E0A7E00C93B1E0A1E00C91D3
+:1003A000B1E0A1E01C9101FF1B7F01FD14601C93D3
+:1003B000B1E0A1E00C910F7D0C93B1E0A1E00C91B4
+:1003C000B1E0A1E01C9102FF1F7B02FD10641C93B1
+:1003D000B0E0A1E20C9100FD05C0B1E0A1E00C91FC
+:1003E0000F7B0C93B1E0A1E00C9106FF05C0B1E0DA
+:1003F000A1E00C9100620C93B1E0A1E00C91B1E09E
+:10040000A1E01C9102FF1F7B02FD10641C93B1E070
+:10041000A8E000E00C93B1E0A9E002E00C93B1E0A9
+:10042000A8E00C91B1E0A9E01C91B1E0A6E02C910C
+:10043000B1E0A7E03C91201731072CF4B1E0A1E036
+:100440000C910F7B0C93B1E0A1E00C9106FF05C06D
+:10045000B1E0A1E00C9100620C93B1E0A1E00C913D
+:10046000B1E0A1E01C9105FF1B7F05FD14601C930A
+:10047000B1E0A1E00C91B1E0A1E01C9102FF1F7E70
+:1004800002FD10611C93B1E0A1E00C9107FF05C0D3
+:10049000B1E0A1E00C910B7F0C93B1E0A1E00C91D5
+:1004A000B1E0A1E01C9104FF1F7704FD10681C93CC
+:1004B000B1E0A1E00C9102FF1CC0B1E0A8E001E0B6
+:1004C0000C93B1E0A9E000E00C93B1E0A2E02C9124
+:1004D000B1E0A3E03C91B1E0A8E00C91B1E0A9E06B
+:1004E0001C91200F311FB1E0A2E02C93B1E0A3E0FA
+:1004F0003C93B1E0A1E00C91B1E0A1E01C9101FFBF
+:100500001B7F01FD14601C93B1E0AAE00C910E7FEB
+:100510000C93B1E0A1E00C91B1E0AAE01C9102FFC4
+:100520001D7F02FD12601C93B1E0A8E00AE00C936D
+:10053000B1E0A9E000E00C93B1E0A8E00C91B1E0DB
+:10054000A9E01C91B1E0A2E02C91B1E0A3E03C91C4
+:10055000201731070CF405C0B1E0AAE00C910D7F23
+:100560000C93B1E0AAE00C9101FF08C0B1E0ABE050
+:1005700004E10C93B1E0ACE000E00C93B1E0AAE040
+:100580000C9101FF05C0B1E0AAE00C9101600C9351
+:10059000B1E0A1E00C91B1E0AAE01C9102FF1D7F47
+:1005A00002FD12601C93B1E0A8E00AE00C93B1E0F8
+:1005B000A9E000E00C93B1E0A8E00C91B1E0A9E063
+:1005C0001C91B1E0A2E02C91B1E0A3E03C91201796
+:1005D00031072CF4B1E0AAE00C910D7F0C93B1E04F
+:1005E000AAE00C9101FF08C0B1E0ABE002E30C937C
+:1005F000B1E0ACE000E00C93B1E0AAE00C9101FFA7
+:1006000005C0B1E0AAE00C9101600C93B1E0A1E05B
+:100610000C91B1E0AAE01C9102FF1D7F02FD126067
+:100620001C93B1E0AAE00C9101FF21C0B1E0ABE066
+:100630000C9110E030E02FEFEAE0FAE00995132F7B
+:10064000022F30E024E6EBE1FAE00995B0E0A3E404
+:100650000C93B1E0AAE00C9102FD09C0B1E0AAE060
+:100660000C9104600C93B0E0A5E40AE60C93B1E0B1
+:10067000AAE00C9101FF05C0B1E0AAE00C91016075
+:100680000C93B1E0AAE00C91B1E0A1E01C9100FF55
+:100690001B7F00FD14601C93B1E0A1E00C91B1E060
+:1006A000A1E01C9101FF1B7F01FD14601C93B1E0D0
+:1006B000A1E00C9102FF1EC0B1E0ADE00C91B1E0F1
+:1006C000AEE01C9123E630E00217130794F4B1E08A
+:1006D000ADE00C91B1E0AEE01C91039509F41395E7
+:1006E0001C93B1E0ADE00C93B1E0A1E00C910B7F65
+:1006F0000C9308C0B1E0ADE000E00C93B1E0AEE0D7
+:1007000000E00C93B1E0A1E00C91B1E0A1E01C91FC
+:1007100002FF1F7E02FD10611C93B1E0AAE00C9164
+:1007200003FF05C0B1E0A1E00C910B7F0C93B1E099
+:10073000A1E00C91B1E0AAE01C9104FF177F04FD39
+:1007400018601C93B1E0A1E00C9102FF0DC0B1E074
+:10075000AAE00C9104FD08C0B1E0AFE000E00C930A
+:10076000B1E0A0E100E00C93B1E0A1E00C91B1E0B8
+:10077000AAE01C9102FF1F7E02FD10611C93B1E0F4
+:10078000AFE00C91B1E0A1E10C93B1E0A0E10C91DC
+:10079000B1E0A2E10C93B1E0AFE00C91B1E0A0E1D7
+:1007A0001C9120E130E0021713070CF408C0B1E0FF
+:1007B000A1E10FEF0C93B1E0A2E10FEF0C93B1E0D8
+:1007C000A1E00C910F7E0C93B1E0A1E00C9104FF2D
+:1007D00006C0B1E0A4E00C91B0E0ACE90C93B1E04C
+:1007E000A1E00C910F7E0C93B0E0ABE90C9105FDFC
+:1007F00005C0B1E0A1E00C9100610C93B1E0A1E073
+:100800000C9104FF08C0B1E0A1E10FEF0C93B1E03F
+:10081000A2E10FEF0C93B1E0A4E000E00C93B1E093
+:10082000A5E000E00C93B1E0A4E00C91B1E0A5E0FC
+:100830001C91B1E0A1E12C91B1E0A2E13C91021741
+:10084000130741F4B1E0A8E003E70C93B1E0A9E09D
+:1008500000E00C93B1E0A4E001E00C93B1E0A5E06E
+:1008600000E00C93B1E0A4E00C91B1E0A5E01C9194
+:10087000B1E0A1E12C91B1E0A2E13C910217130794
+:1008800041F4B1E0A8E001E60C93B1E0A9E000E09A
+:100890000C93B1E0A4E002E00C93B1E0A5E000E02D
+:1008A0000C93B1E0A4E00C91B1E0A5E01C91B1E0A3
+:1008B000A1E12C91B1E0A2E13C910217130741F4B0
+:1008C000B1E0A8E006E70C93B1E0A9E000E00C93EA
+:1008D000B1E0A4E003E00C93B1E0A5E000E00C93EC
+:1008E000B1E0A4E00C91B1E0A5E01C91B1E0A1E180
+:1008F0002C91B1E0A2E13C910217130741F4B1E061
+:10090000A8E005E60C93B1E0A9E000E00C93B1E0AB
+:10091000A4E004E00C93B1E0A5E000E00C93B1E0AA
+:10092000A4E00C91B1E0A5E01C91B1E0A1E12C9113
+:10093000B1E0A2E13C910217130741F4B1E0A8E055
+:1009400004E60C93B1E0A9E000E00C93B1E0A4E070
+:1009500005E00C93B1E0A5E000E00C93B1E0A4E069
+:100960000C91B1E0A5E01C91B1E0A1E12C91B1E0C6
+:10097000A2E13C910217130741F4B1E0A8E000E2C4
+:100980000C93B1E0A9E000E00C93B1E0A4E006E034
+:100990000C93B1E0A5E000E00C93B1E0A4E00C9171
+:1009A000B1E0A5E01C91B1E0A1E12C91B1E0A2E1A0
+:1009B0003C910217130741F4B1E0A8E00DE30C935A
+:1009C000B1E0A9E000E00C93B1E0A4E007E00C93F3
+:1009D000B1E0A5E000E00C93B1E0A4E00C91B1E03F
+:1009E000A5E01C91B1E0A1E12C91B1E0A2E13C9124
+:1009F0000217130741F4B1E0A8E000E20C93B1E064
+:100A0000A9E000E00C93B1E0A4E008E00C93B1E0B1
+:100A1000A5E000E00C93B1E0A1E00C910F7E0C93F7
+:100A2000B1E0A4E00C91B1E0A5E01C91B1E0A1E13E
+:100A30002C91B1E0A2E13C910217130729F4B1E037
+:100A4000A1E00C9100610C93B1E0A1E00C9104FFD6
+:100A500043C0B1E0A2E00C91B1E0A3E10C93B1E09E
+:100A6000A3E00C91B1E0A4E10C93B1E0A8E000E2B6
+:100A70000C93B1E0A9E000E00C93B1E0A2E00C918E
+:100A8000B1E0A3E01C9120E030E00217130724F549
+:100A9000B1E0A8E00DE20C93B1E0A9E000E00C9316
+:100AA000B1E0A4E000E00C93B1E0A5E000E00C931D
+:100AB000B1E0A4E02C91B1E0A5E03C91B1E0A2E06E
+:100AC0000C91B1E0A3E01C91201B310BB1E0A3E13C
+:100AD0002C93B1E0A4E13C93B1E0A4E009E00C93D5
+:100AE000B1E0A5E000E00C93B1E0A1E00C910F7E35
+:100AF0000C93B1E0A4E00C91B1E0A5E01C91B1E051
+:100B0000A1E12C91B1E0A2E13C910217130729F475
+:100B1000B1E0A1E00C9100610C93B1E0A1E00C9177
+:100B200004FF89C0B1E0AAE00C9100620C93B1E02F
+:100B3000A4E000E10C93B1E0A5E007E20C93B1E082
+:100B4000A4E02C91B1E0A5E03C91B1E0A3E10C91CF
+:100B5000B1E0A4E11C91EBE1FAE00995B1E0A8E075
+:100B60000C93B1E0A9E01C93B1E0A4E02C91B1E0BA
+:100B7000A5E03C91B1E0A8E00C91B1E0A9E01C91A6
+:100B8000EAE0FAE00995B1E0A4E02C93B1E0A5E039
+:100B90003C93B1E0A3E12C91B1E0A4E13C91B1E040
+:100BA000A4E00C91B1E0A5E01C91201B310BB1E059
+:100BB000A3E12C93B1E0A4E13C93B1E0A4E000E315
+:100BC0000C93B1E0A5E000E00C93B1E0A8E02C911B
+:100BD000B1E0A9E03C91B1E0A4E00C91B1E0A5E066
+:100BE0001C91200F311FB1E0A8E02C93B1E0A9E0E7
+:100BF0003C93B1E0A4E00C91B1E0A5E01C91B1E020
+:100C0000A8E02C91B1E0A9E03C910217130771F420
+:100C1000B1E0AAE00C9105FF08C0B1E0A8E000E255
+:100C20000C93B1E0A9E000E00C9305C0B1E0AAE0AC
+:100C30000C910F7D0C93B1E0A4E00AE00C93B1E0BD
+:100C4000A5E000E00C93B1E0A1E00C910F7E0C93C5
+:100C5000B1E0A4E00C91B1E0A5E01C91B1E0A1E10C
+:100C60002C91B1E0A2E13C910217130729F4B1E005
+:100C7000A1E00C9100610C93B1E0A1E00C9104FFA4
+:100C800084C0B1E0A4E008EE0C93B1E0A5E003E07D
+:100C90000C93B1E0A4E02C91B1E0A5E03C91B1E06F
+:100CA000A3E10C91B1E0A4E11C91EBE1FAE009951C
+:100CB000B1E0A8E00C93B1E0A9E01C93B1E0A4E09E
+:100CC0002C91B1E0A5E03C91B1E0A8E00C91B1E03D
+:100CD000A9E01C91EAE0FAE00995B1E0A4E02C93C8
+:100CE000B1E0A5E03C93B1E0A3E12C91B1E0A4E137
+:100CF0003C91B1E0A4E00C91B1E0A5E01C91201B77
+:100D0000310BB1E0A3E12C93B1E0A4E13C93B1E05D
+:100D1000A4E000E30C93B1E0A5E000E00C93B1E0A7
+:100D2000A8E02C91B1E0A9E03C91B1E0A4E00C91E5
+:100D3000B1E0A5E01C91200F311FB1E0A8E02C9399
+:100D4000B1E0A9E03C93B1E0A4E00C91B1E0A5E0F2
+:100D50001C91B1E0A8E02C91B1E0A9E03C91021710
+:100D6000130771F4B1E0AAE00C9105FF08C0B1E0EF
+:100D7000A8E000E20C93B1E0A9E000E00C9305C00C
+:100D8000B1E0AAE00C910F7D0C93B1E0A4E00BE080
+:100D90000C93B1E0A5E000E00C93B1E0A1E00C9170
+:100DA0000F7E0C93B1E0A4E00C91B1E0A5E01C91A2
+:100DB000B1E0A1E12C91B1E0A2E13C91021713074F
+:100DC00029F4B1E0A1E00C9100610C93B1E0A1E045
+:100DD0000C9104FF84C0B1E0A4E004E60C93B1E000
+:100DE000A5E000E00C93B1E0A4E02C91B1E0A5E017
+:100DF0003C91B1E0A3E10C91B1E0A4E11C91EBE1E5
+:100E0000FAE00995B1E0A8E00C93B1E0A9E01C93E9
+:100E1000B1E0A4E02C91B1E0A5E03C91B1E0A8E004
+:100E20000C91B1E0A9E01C91EAE0FAE00995B1E08B
+:100E3000A4E02C93B1E0A5E03C93B1E0A3E12C91B8
+:100E4000B1E0A4E13C91B1E0A4E00C91B1E0A5E0F7
+:100E50001C91201B310BB1E0A3E12C93B1E0A4E184
+:100E60003C93B1E0A4E000E30C93B1E0A5E000E026
+:100E70000C93B1E0A8E02C91B1E0A9E03C91B1E085
+:100E8000A4E00C91B1E0A5E01C91200F311FB1E06E
+:100E9000A8E02C93B1E0A9E03C93B1E0A4E00C9170
+:100EA000B1E0A5E01C91B1E0A8E02C91B1E0A9E08F
+:100EB0003C910217130771F4B1E0AAE00C9105FF11
+:100EC00008C0B1E0A8E000E20C93B1E0A9E000E0C6
+:100ED0000C9305C0B1E0AAE00C910F7D0C93B1E03A
+:100EE000A4E00CE00C93B1E0A5E000E00C93B1E0CD
+:100EF000A1E00C910F7E0C93B1E0A4E00C91B1E065
+:100F0000A5E01C91B1E0A1E12C91B1E0A2E13C91FE
+:100F10000217130729F4B1E0A1E00C9100610C93D2
+:100F2000B1E0A1E00C9104FF84C0B1E0A4E00AE0CC
+:100F30000C93B1E0A5E000E00C93B1E0A4E02C91AB
+:100F4000B1E0A5E03C91B1E0A3E10C91B1E0A4E1F6
+:100F50001C91EBE1FAE00995B1E0A8E00C93B1E057
+:100F6000A9E01C93B1E0A4E02C91B1E0A5E03C9194
+:100F7000B1E0A8E00C91B1E0A9E01C91EAE0FAE050
+:100F80000995B1E0A4E02C93B1E0A5E03C93B1E079
+:100F9000A3E12C91B1E0A4E13C91B1E0A4E00C917B
+:100FA000B1E0A5E01C91201B310BB1E0A3E12C9333
+:100FB000B1E0A4E13C93B1E0A4E000E30C93B1E024
+:100FC000A5E000E00C93B1E0A8E02C91B1E0A9E02D
+:100FD0003C91B1E0A4E00C91B1E0A5E01C91200FA0
+:100FE000311FB1E0A8E02C93B1E0A9E03C93B1E05F
+:100FF000A4E00C91B1E0A5E01C91B1E0A8E02C9137
+:10100000B1E0A9E03C910217130771F4B1E0AAE046
+:101010000C9105FF08C0B1E0A8E000E20C93B1E03C
+:10102000A9E000E00C9305C0B1E0AAE00C910F7DAF
+:101030000C93B1E0A4E00DE00C93B1E0A5E000E07A
+:101040000C93B1E0A1E00C910F7E0C93B1E0A4E011
+:101050000C91B1E0A5E01C91B1E0A1E12C91B1E0CF
+:10106000A2E13C910217130729F4B1E0A1E00C9131
+:1010700000610C93B1E0A1E00C9104FF62C0B1E00B
+:10108000A4E001E00C93B1E0A5E000E00C93B1E036
+:10109000A4E02C91B1E0A5E03C91B1E0A3E10C917A
+:1010A000B1E0A4E11C91EBE1FAE00995B1E0A8E020
+:1010B0000C93B1E0A9E01C93B1E0A4E02C91B1E065
+:1010C000A5E03C91B1E0A8E00C91B1E0A9E01C9151
+:1010D000EAE0FAE00995B1E0A4E02C93B1E0A5E0E4
+:1010E0003C93B1E0A3E12C91B1E0A4E13C91B1E0EB
+:1010F000A4E00C91B1E0A5E01C91201B310BB1E004
+:10110000A3E12C93B1E0A4E13C93B1E0A4E000E3BF
+:101110000C93B1E0A5E000E00C93B1E0A8E02C91C5
+:10112000B1E0A9E03C91B1E0A4E00C91B1E0A5E010
+:101130001C91200F311FB1E0A8E02C93B1E0A9E091
+:101140003C93B1E0A4E00EE00C93B1E0A5E000E038
+:101150000C93B1E0A4E00C91B1E0A5E01C91B1E0EA
+:10116000A1E12C91B1E0A2E13C910217130741F4F7
+:10117000B1E0A8E00DE00C93B1E0A9E000E00C9331
+:10118000B1E0A4E00FE00C93B1E0A5E000E00C9327
+:10119000B1E0A4E00C91B1E0A5E01C91B1E0A1E1C7
+:1011A0002C91B1E0A2E13C910217130741F4B1E0A8
+:1011B000A8E00AE00C93B1E0A9E000E00C93B1E0F4
+:1011C000A1E10C91B1E0A2E11C9120E030E0021716
+:1011D00013070CF42CC0B1E0A1E00C9100610C935A
+:1011E000B1E0A1E00C9104FF06C0B1E0A8E00C91D1
+:1011F000B0E0ACE90C93B1E0A1E00C910F7E0C9350
+:10120000B0E0ABE90C9105FD05C0B1E0A1E00C91A7
+:1012100000610C93B1E0AFE00C91B1E0A0E11C9152
+:10122000039509F413951C93B1E0AFE00C93B1E082
+:10123000A1E00C910B7F0C93B1E0AFE00C91B1E019
+:10124000A0E11C9120E130E0021713072CF4B1E07B
+:10125000A1E00C9104600C93B1E0A1E00C91B1E02D
+:10126000A1E01C9101FF1B7F01FD14601C93B1E004
+:10127000A1E00C9102FF08C0B1E0A5E108E70C93E2
+:10128000B1E0A6E100E00C93B1E0A1E00C91B1E087
+:10129000A1E01C9101FF1B7F01FD14601C93B1E0D4
+:1012A000A1E00C9102FF1EC0B1E0A7E10C91B1E0FA
+:1012B000A8E11C9127EC30E00217130794F4B1E089
+:1012C000A7E10C91B1E0A8E11C91039509F41395F5
+:1012D0001C93B1E0A7E10C93B1E0A1E00C910B7F6E
+:1012E0000C9308C0B1E0A7E100E00C93B1E0A8E1E5
+:1012F00000E00C93B1E0A1E00C91B1E0A1E01C9101
+:1013000002FF1F7E02FD10611C93B1E0AAE00C9168
+:1013100006FF05C0B1E0A1E00C910B7F0C93B1E09A
+:10132000A1E00C91B1E0AAE01C9104FF1F7B04FD39
+:1013300010641C93B1E0A1E00C9102FF06C0B1E083
+:10134000A5E10C91B0E0ACE90C93B1E0A1E00C9107
+:101350000B7F0C93B0E0ABE90C9105FD05C0B1E04B
+:10136000A1E00C9104600C93B1E0A1E00C91B1E01C
+:10137000A1E01C9101FF1B7F01FD14601C93B1E0F3
+:10138000A1E00C9102FF19C0B1E0A1E00C910B7F2C
+:101390000C93B0E0ABE90C9107FF0FC0B1E0A1E006
+:1013A0000C9104600C93B0E0ACE90C91B1E0A9E1C0
+:1013B0000C93B1E0AAE100E00C93B1E0A8E001E6F3
+:1013C0000C93B1E0A9E000E00C93B1E0A9E10C912D
+:1013D000B1E0AAE11C91B1E0A8E02C91B1E0A9E054
+:1013E0003C910217130709F405C0B1E0A1E00C918C
+:1013F0000B7F0C93B1E0A1E00C9102FF08C0B1E0BB
+:10140000A2E000E00C93B1E0A3E000E00C93E1E97E
+:10141000F0E00994551B441B60E110F4400F511F8C
+:1014200020FD401B20FD510B55954795379527957D
+:101430006A9599F70895D12ED32617FF04C0109509
+:1014400000950F5F1F4F37FF04C0309520952F5F29
+:101450003F4FEE24FF1841E1001F111F4A9539F458
+:10146000D7FE04C0109500950F5F1F4F0895EE1C26
+:10147000FF1CE21AF30A20F4E20EF31E8894ECCF6C
+:041480000894EACF13
+:00000001FF
diff --git a/ldmicro/reg/results/contacts.hex b/ldmicro/reg/results/contacts.hex
new file mode 100644
index 0000000..5721d1b
--- /dev/null
+++ b/ldmicro/reg/results/contacts.hex
@@ -0,0 +1,21 @@
+:020000040000FA
+:100000008A110A1208280000000000000000000009
+:10001000283084005830A0008001840AA00B0C28EE
+:10002000103095002730960000308E0000308F0091
+:10003000013090000B309700831686309F008312AA
+:10004000003085008316DF30850083120030860083
+:100050008316FF3086008312003087008316FC3041
+:10006000870083120C1D32280C116400A914A918F2
+:100070002915A91C291105183E2829112919071429
+:10008000291D0710A9182915A91C2911A911291919
+:100090002916291D291285184E282912291E512892
+:1000A000A91529192916291D2912051958282912B7
+:1000B000291E5B28A915A9192915A91D2911291D72
+:1000C00062288516A9182915A91C291185196928DE
+:1000D0002911051E6C282911A91229192917291D72
+:1000E0002913291F74288512291F7728A916291971
+:1000F0002917291D2913291F7F2887108028871475
+:10010000291F8328A916A91A2915A91E29118A01B0
+:02011000322893
+:02400E00723FFF
+:00000001FF
diff --git a/ldmicro/reg/results/integers.hex b/ldmicro/reg/results/integers.hex
new file mode 100644
index 0000000..3ecfe77
--- /dev/null
+++ b/ldmicro/reg/results/integers.hex
@@ -0,0 +1,134 @@
+:1000000022C0189518951895189518951895189553
+:100010001895189518951895189518951895189578
+:100020001895189518951895189518951895189568
+:100030001895189518951895189518951895189558
+:10004000189518951895B0E0AEE504E00C93B0E073
+:10005000ADE50FEF0C93B5E0A0E000E020E034E068
+:10006000A150B0400C93215030402223C9F73323D4
+:10007000B9F7B0E0AAE304E00C93B0E0ABE300E032
+:100080000C93B0E0A7E300E00C93B0E0A8E301E03C
+:100090000C93B0E0A4E300E00C93B0E0A5E300E033
+:1000A0000C93B0E0A1E300E40C93B0E0A2E300E025
+:1000B0000C93B0E0A6E200E00C93B0E0A7E200E011
+:1000C0000C93B0E0AFE400E00C93B0E0AEE40AE0E3
+:1000D0000C93B0E0ABE404EF0C93B0E0AAE402E2CE
+:1000E0000C93B0E0A9E500E40C93B0E0A8E50C9116
+:1000F00006FFFBCFB0E0A8E50C9100640C93A89537
+:10010000B1E0A1E00C9102600C93B1E0A1E00C9190
+:10011000B1E0A1E01C9101FF1B7F01FD14601C9365
+:10012000B1E0A1E00C91077F0C93B1E0A1E00C914C
+:10013000B1E0A1E01C9102FF1F7E02FD10611C9343
+:10014000B0E0A6E30C9100FD05C0B1E0A1E00C9188
+:100150000F7E0C93B1E0A1E00C9104FF08C0B1E068
+:10016000A2E00BE20C93B1E0A3E005E00C93B1E058
+:10017000A1E00C9104FF05C0B1E0A1E00C91086082
+:100180000C93B1E0A1E00C91B1E0A1E01C9102FF61
+:100190001F7E02FD10611C93B0E0A6E30C9100FFEE
+:1001A00005C0B1E0A1E00C910F7E0C93B1E0A1E09D
+:1001B0000C9104FF08C0B1E0A2E004EF0C93B1E0A1
+:1001C000A3E00FEF0C93B1E0A1E00C9104FF05C098
+:1001D000B1E0A1E00C9108600C93B1E0A1E00C91BA
+:1001E000B1E0A1E01C9103FF1B7F03FD14601C9391
+:1001F000B1E0A1E00C91B1E0A1E01C9101FF1B7FF7
+:1002000001FD14601C93B1E0A1E00C910F7D0C93F3
+:10021000B1E0A1E00C91B1E0A1E01C9102FF1F7BD5
+:1002200002FD10641C93B1E0A1E00C9106FF1CC01C
+:10023000B1E0A4E008EE0C93B1E0A5E003E00C937C
+:10024000B1E0A2E02C91B1E0A3E03C91B1E0A4E0E8
+:100250000C91B1E0A5E01C91200F311FB1E0A6E0A8
+:100260002C93B1E0A7E03C93B1E0A1E00C9106FF34
+:1002700005C0B1E0A1E00C9100620C93B1E0A1E0F7
+:100280000C91B1E0A1E01C9102FF1F7B02FD106404
+:100290001C93B1E0A1E00C9106FF1CC0B1E0A4E00A
+:1002A00005E00C93B1E0A5E000E00C93B1E0A2E022
+:1002B0002C91B1E0A3E03C91B1E0A4E00C91B1E05D
+:1002C000A5E01C91201B310BB1E0A8E02C93B1E01C
+:1002D000A9E03C93B1E0A1E00C9106FF05C0B1E0BC
+:1002E000A1E00C9100620C93B1E0A1E00C91B1E0AF
+:1002F000A1E01C9102FF1F7B02FD10641C93B1E082
+:10030000A1E00C9106FF1DC0B1E0A4E003E00C9356
+:10031000B1E0A5E000E00C93B1E0A2E02C91B1E0E7
+:10032000A3E03C91B1E0A4E00C91B1E0A5E01C9108
+:10033000ECEEF3E00995B1E0AAE02C93B1E0ABE07C
+:100340003C93B1E0A1E00C9106FF05C0B1E0A1E053
+:100350000C9100620C93B1E0A1E00C91B1E0A1E03E
+:100360001C9102FF1F7B02FD10641C93B1E0A1E011
+:100370000C9106FF1DC0B1E0A4E003E00C93B1E0D6
+:10038000A5E000E00C93B1E0A4E02C91B1E0A5E081
+:100390003C91B1E0AAE00C91B1E0ABE01C91EDEF33
+:1003A000F3E00995B1E0ACE00C93B1E0ADE01C9353
+:1003B000B1E0A1E00C9106FF05C0B1E0A1E00C9115
+:1003C00000620C93B1E0A1E00C91B1E0A1E01C91BE
+:1003D00005FF1B7F05FD14601C93B1E0A1E00C91AB
+:1003E000B1E0A1E01C9101FF1B7F01FD14601C9393
+:1003F000B1E0A1E00C910F770C93B1E0A1E00C917A
+:10040000B1E0AEE01C9102FF1E7F02FD11601C9363
+:10041000B1E0A4E00BE20C93B1E0A5E005E00C93A1
+:10042000B1E0A2E00C91B1E0A3E01C91B1E0A4E046
+:100430002C91B1E0A5E03C910217130709F405C027
+:10044000B1E0AEE00C910E7F0C93B1E0AEE00C9108
+:1004500000FF05C0B1E0A1E00C9100680C93B1E091
+:10046000A1E00C91B1E0AEE01C9102FF1E7F02FD05
+:1004700011601C93B1E0AEE00C9100FF05C0B1E04B
+:10048000A1E00C9100680C93B1E0A1E00C91B1E007
+:10049000A1E01C9107FF1B7F07FD14601C93B1E0D6
+:1004A000AEE00C910D7F0C93B1E0A1E00C91B1E0B6
+:1004B000AEE01C9102FF1B7F02FD14601C93B1E0B3
+:1004C000A4E000E00C93B1E0A5E000E00C93B1E003
+:1004D000A4E00C91B1E0A5E01C91B1E0A6E02C9164
+:1004E000B1E0A7E03C91201731070CF405C0B1E062
+:1004F000AEE00C910B7F0C93B1E0AEE00C9102FFEB
+:1005000005C0B1E0AEE00C9102600C93B1E0A1E057
+:100510000C91B1E0AEE01C9102FF1B7F02FD146064
+:100520001C93B1E0A4E006E20C93B1E0A5E005E085
+:100530000C93B1E0A8E00C91B1E0A9E01C91B1E00E
+:10054000A4E02C91B1E0A5E03C91201731072CF4F8
+:10055000B1E0AEE00C910B7F0C93B1E0AEE00C91FA
+:1005600002FF05C0B1E0AEE00C9102600C93B1E077
+:10057000A1E00C91B1E0AEE01C9102FF1B7F02FDF7
+:1005800014601C93B1E0AEE00C9102FF05C0B1E035
+:10059000AEE00C9102600C93B1E0AEE00C91B1E0E2
+:1005A000A1E01C9101FF1B7F01FD14601C93B1E0D1
+:1005B000AEE00C91077F0C93B1E0A1E00C91B1E0AB
+:1005C000AEE01C9102FF1F7E02FD10611C93B1E0A2
+:1005D000A4E000E20C93B1E0A5E00EE40C93B1E0DE
+:1005E000A6E00C91B1E0A7E01C91B1E0A4E02C9151
+:1005F000B1E0A5E03C91201731070CF405C0B1E053
+:10060000AEE00C910F7E0C93B1E0AEE00C9104FFD4
+:1006100005C0B1E0AEE00C9108600C93B1E0A1E040
+:100620000C91B1E0AEE01C9102FF1F7E02FD106153
+:100630001C93B1E0A4E001E80C93B1E0A5E00FE069
+:100640000C93B1E0A4E00C91B1E0A5E01C91B1E005
+:10065000AAE02C91B1E0ABE03C91201731072CF4DB
+:10066000B1E0AEE00C910F7E0C93B1E0AEE00C91E6
+:1006700004FF05C0B1E0AEE00C9108600C93B1E05E
+:10068000A1E00C91B1E0AEE01C9102FF1F7E02FDE3
+:1006900010611C93B1E0AEE00C910F7E0C93B1E0C1
+:1006A000AEE00C9104FF05C0B1E0AEE00C91086033
+:1006B0000C93B1E0AEE00C91B1E0A1E01C9103FF1E
+:1006C0001B7F03FD14601C93B1E0A1E00C91B0E02E
+:1006D000ABE31C9102FF1B7F02FD14601C93B1E091
+:1006E000A1E00C91B1E0A1E01C9101FF1B7F01FD95
+:1006F00014601C93B1E0A4E00CED0C93B1E0A5E014
+:100700000FEF0C93B1E0AAE00C91B1E0ABE01C91CB
+:10071000B1E0A4E02C91B1E0A5E03C9102171307F1
+:1007200029F4B1E0A1E00C910B7F0C93B1E0A4E0BF
+:1007300000E00C93B1E0A5E000E00C93B1E0A2E092
+:100740000C91B1E0A3E01C91B1E0A4E02C91B1E0E8
+:10075000A5E03C91201731070CF405C0B1E0A1E001
+:100760000C910B7F0C93B1E0A1E00C9102FF1EC035
+:10077000B1E0AFE00C91B1E0A0E11C9123E130E0E9
+:100780000217130794F4B1E0AFE00C91B1E0A0E1DF
+:100790001C91039509F413951C93B1E0AFE00C9301
+:1007A000B1E0A1E00C910B7F0C9308C0B1E0AFE089
+:1007B00000E00C93B1E0A0E100E00C93B1E0A1E017
+:1007C0000C91B0E0A2E31C9102FF1F7B02FD1064BC
+:1007D0001C93E5E7F0E00994551B441B60E110F41D
+:1007E000400F511F20FD401B20FD510B5595479593
+:1007F000379527956A9599F70895D12ED32617FF37
+:1008000004C0109500950F5F1F4F37FF04C030954F
+:1008100020952F5F3F4FEE24FF1841E1001F111F6D
+:100820004A9539F4D7FE04C0109500950F5F1F4F0D
+:100830000895EE1CFF1CE21AF30A20F4E20EF31EE8
+:080840008894ECCF0894EACF84
+:00000001FF
diff --git a/ldmicro/reg/results/misc-ops.hex b/ldmicro/reg/results/misc-ops.hex
new file mode 100644
index 0000000..7166fb0
--- /dev/null
+++ b/ldmicro/reg/results/misc-ops.hex
@@ -0,0 +1,69 @@
+:020000040000FA
+:100000008A110A1208280000000000000000000009
+:10001000283084005830A0008001840AA00B0C28EE
+:10002000103095002730960000308E0000308F0091
+:10003000013090000B309700831686309F008312AA
+:10004000003085008316FD30850083120030860065
+:100050008316FF3086008312003087008316FD3040
+:10006000870083120C1D32280C116400A914A918F2
+:100070002915A91C291105183E282911A91842285B
+:10008000A91446282919A914291DA910A918291548
+:10009000A91C2911A9194D282911291D64280030EE
+:1000A0002B0203195A28A00020092B05A006A01B2B
+:1000B0005F28632831302A02031C5F286328AA0FB7
+:1000C0006228AB0A291168280030AA000030AB0072
+:1000D000291A6E283130AC000030AD0029162919DC
+:1000E000862800302D0203197C28A00020092D0548
+:1000F000A006A01B8128852831302C02031C8128F2
+:100100008528AC0F8428AD0A29158A280030AC0058
+:100110000030AD00291D8E28A9118F28A915A91816
+:100120002915A91C2911A91996282911291DB228B8
+:10013000A91AB2283008AE003108AF003208B0006A
+:100140003308B1003408B2003508B3003608B400F3
+:100150003708B5003808B6003908B7003A08B800C3
+:100160003B08B9002919A916291DA912A918291592
+:10017000A91C291129132919A917291DA913A91F78
+:10018000D82851309F00831680309F00831206309C
+:10019000A100A10BC9281F151F19CC281E08BD00DE
+:1001A00083161E088312BC00831686309F008312BC
+:1001B000A91FDB2829172919A917291DA913A91F68
+:1001C000E5283C08BA003D08BB00A91FE82829170C
+:1001D000291B2915291F2911A9182915A91C29111D
+:1001E0003E102919BE14291DBE10A919F828BE10E9
+:1001F000BE1C14293E191429BF0FFF28C00A003065
+:10020000400203190A29A00020094005A006A01BEE
+:100210000F29102905303F02031C0F29102914292A
+:100220000030BF000030C000BE183E15BE1C3E119D
+:10023000BE1C1B293E142919BE14291DBE10BE1C4C
+:1002400071290030C1000030C2003F084102031D87
+:10025000312940084202031D31290A30C300003011
+:10026000C4000130C1000030C2003F084102031D3C
+:10027000412940084202031D41294630C300003095
+:10028000C4000230C1000030C2003F084102031D1B
+:10029000512940084202031D51295030C30000304B
+:1002A000C4000330C1000030C2003F084102031DFA
+:1002B000612940084202031D61294630C300003015
+:1002C000C4000430C1000030C2003F084102031DD9
+:1002D000712940084202031D71290A30C300003011
+:1002E000C400BE1C74293E142919BE14291DBE1059
+:1002F000BE1C9E294308A000A1016430A200A301F6
+:1003000001308A00BB2101308A002308A1002208A5
+:10031000A0006430A200A30101308A00D221013084
+:100320008A0020089B00BE199E29BE1583166330E3
+:10033000920083120C309D0004309200BE1CA12953
+:100340003E143E1829153E1C2911A9182915A91C6F
+:100350002911A918AD29A914B1292919A914291DF0
+:10036000A910A9182915A91C291129198514291DB5
+:1003700085108A013228A501A4010310A30CA20C48
+:100380001030A600031CCA292008A4070318A50AD8
+:100390002108A5070310A50CA40CA30CA20CA60B06
+:1003A000C229080021082306A700A31FDC29A209EF
+:1003B000A309A20A0319A30AA11FE329A009A109FD
+:1003C000A00A0319A10AA501A40103101130A60077
+:1003D000A00DA10DA6030319012AA40DA50D220845
+:1003E000A402031CA5032308A502A51FFF292208B8
+:1003F000A4070318A50A2308A5070310E829031476
+:10040000E829A71F0800A009A109A00A0319A10A49
+:020410000800E2
+:02400E00723FFF
+:00000001FF
diff --git a/ldmicro/reg/results/pic16-hw.hex b/ldmicro/reg/results/pic16-hw.hex
new file mode 100644
index 0000000..c403acf
--- /dev/null
+++ b/ldmicro/reg/results/pic16-hw.hex
@@ -0,0 +1,189 @@
+:020000040000FA
+:100000008A110A1208280000000000000000000009
+:10001000283084005830A0008001840AA00B0C28EE
+:10002000103095002730960000308E0000308F0091
+:10003000013090000B309700831686309F008312AA
+:10004000003085008316FF30850083120030860063
+:100050008316FF3086008312003087008316FD3040
+:1000600087008312003088008316FF3088008312D7
+:1000700000308900831607308900831283161930F7
+:1000800099008312831620309800831290309800D4
+:100090000C1D48280C116400A914A9182915A91CC5
+:1000A0002911291DD928A91989282912831603176E
+:1000B0008C186F2883120313291C722803178D0ACA
+:1000C0000313280803178C0083168C130C15553066
+:1000D0008D00AA308D008C14831203132910831213
+:1000E00003132916291A8928A915031700308D0032
+:1000F00083168C130C1483120C080313AA00031725
+:1001000001308D0083168C130C1483120C0803131A
+:10011000AB002912831603178C18A328831203132C
+:10012000291CA62803178D0A0313280803178C001F
+:1001300083168C130C1555308D00AA308D008C144D
+:10014000831203132910831203132916291AD9289D
+:10015000031700308D0083168C130C1483120C08C7
+:100160000313AC00031701308D0083168C130C149D
+:1001700083120C080313AD002C082A02031DC528A6
+:100180002D082B02031DC528D92829142B08A800E7
+:10019000031700308D0003132A0803178C00831601
+:1001A0008C130C1555308D00AA308D008C148312E1
+:1001B0000313A9182915A91C2911291DF62849304E
+:1001C0009F00831680309F0083120630A100A10B90
+:1001D000E7281F151F19EA281E08AF0083161E08FE
+:1001E0008312AE00831686309F008312A91829154A
+:1001F000A91C2911A91229192917291D2913051824
+:1002000002292913291F0529A91629192917291D8A
+:1002100029130030B0000230B1003109A1002F05D0
+:10022000A0002F08A104A10931082F0203192129D8
+:10023000A105A200220920052104A206A21B26294D
+:10024000272930082E02031C262927292913291FB4
+:100250002A29A916A91A2915A91E29112919291609
+:10026000291D2912A91F35292911291AA917291E63
+:10027000A913291D4A290130B0000030B1002A0815
+:100280003007AA002010031820142B083107AB00F8
+:100290002018AB0AA9182915A91C291132102919EF
+:1002A000B214291DB2100A30B0000030B10031097B
+:1002B000A1002B05A0002B08A104A10931082B02E5
+:1002C00003196B29A105A200220920052104A20619
+:1002D000A21B7029712930082A02031C7029712978
+:1002E0007229B210B21C78291430B3000030B40067
+:1002F000B21C7B2932142919B214291DB2100A30FC
+:10030000B0000030B1003109A1002B05A0002B087E
+:10031000A104A10931082B0203199729A105A20004
+:10032000220920052104A206A21B9C299D29300830
+:100330002A02031C9C299D29B210B21CA329323029
+:10034000B3000030B400B21CA62932142919B2142B
+:10035000291DB210B21CD0293308A000A1017D30A4
+:10036000A200A30105308A00812501308A002308FC
+:10037000A1002208A0006430A200A30105308A0079
+:10038000982501308A0020089B003219D0293215A7
+:1003900083167C30920083120C309D000530920051
+:1003A000B21CD329321432182915321C2911A9186C
+:1003B0002915A91C2911291DF2290030360203191B
+:1003C000E829A00020093605A006A01BED29F12987
+:1003D00063303502031CED29F129B50FF029B60A67
+:1003E0002911F6290030B5000030B6002919291668
+:1003F000291D2912B21DFD292911291AB215291EFC
+:10040000B211291D092A321A092A0030B70000301A
+:10041000B80029193216291D32123708B9003808D8
+:10042000BA000030380203191C2AA0002009380540
+:10043000A006A01B212A222A10303702031C212AE1
+:10044000222A262AFF30B900FF30BA002912291EBD
+:100450002B2A2C089900291283169818312A831206
+:1004600029168312291E382AFF30B900FF30BA003E
+:100470000030AC000030AD002C083902031D482AC2
+:100480002D083A02031D482A7330B0000030B10035
+:100490000130AC000030AD002C083902031D582A91
+:1004A0002D083A02031D582A6130B0000030B10017
+:1004B0000230AC000030AD002C083902031D682A60
+:1004C0002D083A02031D682A7630B0000030B100D2
+:1004D0000330AC000030AD002C083902031D782A2F
+:1004E0002D083A02031D782A6530B0000030B100B3
+:1004F0000430AC000030AD002C083902031D882AFE
+:100500002D083A02031D882A6430B0000030B10083
+:100510000530AC000030AD002C083902031D982ACC
+:100520002D083A02031D982A2030B0000030B10097
+:100530000630AC000030AD002C083902031DA82A9B
+:100540002D083A02031DA82A3D30B0000030B1004A
+:100550000730AC000030AD002C083902031DB82A6A
+:100560002D083A02031DB82A2030B0000030B10037
+:100570000830AC000030AD0029122C083902031DF0
+:10058000C62A2D083A02031DC62A2916291EF32A57
+:100590002A08BB002B08BC002030B0000030B1009E
+:1005A00000302B020319DB2AA00020092B05A0062E
+:1005B000A01BE02AF32A00302A02031CE02AF32AB7
+:1005C0002D30B0000030B1000030AC000030AD0084
+:1005D0002A082C02BB002010031820142B082D021F
+:1005E000BC00201CBC030930AC000030AD00291257
+:1005F0002C083902031D012B2D083A02031D012B83
+:100600002916291E542BB2161030AC002730AD002D
+:100610003B08A0003C08A1002C08A2002D08A30064
+:1006200005308A00982503308A002008B000210890
+:10063000B1002C08A0002D08A1003008A20031084C
+:10064000A30005308A00812503308A002208AC000F
+:100650002308AD002C083B02BB0020100318201417
+:100660002D083C02BC00201CBC033030AC00003024
+:10067000AD0030082C07B0002010031820143108FA
+:100680002D07B1002018B10A2C083002031D532B8E
+:100690002D083102031D532BB21E522B2030B00007
+:1006A0000030B100542BB2120A30AC000030AD0063
+:1006B00029122C083902031D622B2D083A02031D52
+:1006C000622B2916291EB42BE830AC000330AD0094
+:1006D0003B08A0003C08A1002C08A2002D08A300A4
+:1006E00005308A00982503308A002008B0002108D0
+:1006F000B1002C08A0002D08A1003008A20031088C
+:10070000A30005308A00812503308A002208AC004E
+:100710002308AD002C083B02BB0020100318201456
+:100720002D083C02BC00201CBC033030AC00003063
+:10073000AD0030082C07B000201003182014310839
+:100740002D07B1002018B10A2C083002031DB32B6D
+:100750002D083102031DB32BB21EB22B2030B00086
+:100760000030B100B42BB2120B30AC000030AD0041
+:1007700029122C083902031DC22B2D083A02031D31
+:10078000C22B2916291E142C6430AC000030AD0099
+:100790003B08A0003C08A1002C08A2002D08A300E3
+:1007A00005308A00982503308A002008B00021080F
+:1007B000B1002C08A0002D08A1003008A2003108CB
+:1007C000A30005308A00812503308A002208AC008E
+:1007D0002308AD002C083B02BB0020100318201496
+:1007E0002D083C02BC00201CBC033030AC000030A3
+:1007F000AD0030082C07B000201003182014310879
+:100800002D07B1002018B10A2C083002031D132C4B
+:100810002D083102031D132CB21E122C2030B00003
+:100820000030B100142CB2120C30AC000030AD001E
+:1008300029122C083902031D222C2D083A02031D0F
+:10084000222C2916291E742C0A30AC000030AD0071
+:100850003B08A0003C08A1002C08A2002D08A30022
+:1008600005308A00982504308A002008B00021084D
+:10087000B1002C08A0002D08A1003008A20031080A
+:10088000A30005308A00812504308A002208AC00CC
+:100890002308AD002C083B02BB00201003182014D5
+:1008A0002D083C02BC00201CBC033030AC000030E2
+:1008B000AD0030082C07B0002010031820143108B8
+:1008C0002D07B1002018B10A2C083002031D732C2B
+:1008D0002D083102031D732CB21E722C2030B00083
+:1008E0000030B100742CB2120D30AC000030AD00FD
+:1008F00029122C083902031D822C2D083A02031DEF
+:10090000822C2916291EC42C0130AC000030AD0009
+:100910003B08A0003C08A1002C08A2002D08A30061
+:1009200005308A00982504308A002008B00021088C
+:10093000B1002C08A0002D08A1003008A200310849
+:10094000A30005308A00812504308A002208AC000B
+:100950002308AD002C083B02BB0020100318201414
+:100960002D083C02BC00201CBC033030AC00003021
+:10097000AD0030082C07B0002010031820143108F7
+:100980002D07B1002018B10A0E30AC000030AD00C8
+:100990002C083902031DD42C2D083A02031DD42C37
+:1009A0000D30B0000030B1000F30AC000030AD00B1
+:1009B0002C083902031DE42C2D083A02031DE42CF7
+:1009C0000A30B0000030B10000303A020319EF2CB9
+:1009D000A00020093A05A006A01BF42CF52C00303D
+:1009E0003902031CF42CF52C042D2916291EFA2C8F
+:1009F00030089900291283169818002D83122916A1
+:100A00008312B70F042DB80A2911003038020319D8
+:100A1000102DA00020093805A006A01B152D162DAD
+:100A200010303702031C152D162D2915A91829156C
+:100A3000A91C2911291D202D7830BD000030BE00D1
+:100A4000A9182915A91C2911291D3B2D0030400288
+:100A50000319312DA00020094005A006A01B362D4A
+:100A60003A2DC7303F02031C362D3A2DBF0F392DCA
+:100A7000C00A29113F2D0030BF000030C0002919E5
+:100A80002916291D2912321F462D2911291A32171C
+:100A9000291E3213291D4E2D3D0899002911831658
+:100AA0009818542D831229158312A9182915A91CE9
+:100AB0002911291D6B2D29118C1E6B2D1A08C100BF
+:100AC000C20129159818672D1819672D6B2D1A0862
+:100AD0001A08181218166130B0000030B100410831
+:100AE0003002031D782D42083102031D782D792D27
+:100AF0002911291D7F2D0030AA000030AB008A018A
+:100B00004828A501A4010310A30CA20C1030A600D4
+:100B1000031C902D2008A4070318A50A2108A50787
+:100B20000310A50CA40CA30CA20CA60B882D080086
+:100B300021082306A700A31FA22DA209A309A20A28
+:100B40000319A30AA11FA92DA009A109A00A03192D
+:100B5000A10AA501A40103101130A600A00DA10D4A
+:100B6000A6030319C72DA40DA50D2208A402031C7A
+:100B7000A5032308A502A51FC52D2208A407031855
+:100B8000A50A2308A5070310AE2D0314AE2DA71F39
+:0E0B90000800A009A109A00A0319A10A080083
+:02400E00723FFF
+:00000001FF
diff --git a/ldmicro/reg/results/timers-counters.hex b/ldmicro/reg/results/timers-counters.hex
new file mode 100644
index 0000000..0e21b50
--- /dev/null
+++ b/ldmicro/reg/results/timers-counters.hex
@@ -0,0 +1,119 @@
+:1000000022C0189518951895189518951895189553
+:100010001895189518951895189518951895189578
+:100020001895189518951895189518951895189568
+:100030001895189518951895189518951895189558
+:10004000189518951895B0E0AEE500E10C93B0E076
+:10005000ADE50FEF0C93B1E1A0E000E020E030E16E
+:10006000A150B0400C93215030402223C9F73323D4
+:10007000B9F7B0E0AAE300E20C93B0E0ABE300E034
+:100080000C93B0E0A7E300E00C93B0E0A8E300E03D
+:100090000C93B0E0A4E300E00C93B0E0A5E300E033
+:1000A0000C93B0E0A1E304E00C93B0E0A2E300E025
+:1000B0000C93B0E0A2E200E00C93B0E0A3E207E012
+:1000C0000C93B0E0A1E600E20C93B0E0A2E600E001
+:1000D0000C93B0E0A4E600E00C93B0E0A5E600E0ED
+:1000E0000C93B0E0AFE400E00C93B0E0AEE409E0C4
+:1000F0000C93B0E0ABE40CE90C93B0E0AAE40EE39F
+:100100000C93B0E0A7E500E10C93B0E0A6E50C91FC
+:1001100004FFFBCFB0E0A6E50C9100610C93A8951D
+:10012000B1E0A1E00C9102600C93B1E0A1E00C9170
+:10013000B1E0A1E01C9101FF1B7F01FD14601C9345
+:10014000B1E0A1E00C9103FD05C0B1E0A1E00C918C
+:100150000B7F0C93B1E0A1E00C9104FD08C0B1E06D
+:10016000A2E009E00C93B1E0A3E000E00C93B1E061
+:10017000A1E00C9100610C93B1E0A1E00C9102FDB3
+:100180001EC0B1E0A2E00C91B1E0A3E01C9129E017
+:1001900030E00217130794F4B1E0A2E00C91B1E053
+:1001A000A3E01C91039509F413951C93B1E0A2E020
+:1001B0000C93B1E0A1E00C9104600C9308C0B1E095
+:1001C000A2E000E00C93B1E0A3E000E00C93B1E00A
+:1001D000A1E00C9102FF1EC0B1E0A4E00C91B1E0DF
+:1001E000A5E01C9129E030E00217130794F4B1E078
+:1001F000A4E00C91B1E0A5E01C91039509F41395DE
+:100200001C93B1E0A4E00C93B1E0A1E00C910B7F52
+:100210000C9308C0B1E0A4E000E00C93B1E0A5E0CD
+:1002200000E00C93B1E0A1E00C9102FF06C0B1E048
+:10023000A1E00C91077F0C9305C0B1E0A1E00C9107
+:1002400008600C93B1E0A1E00C91B1E0A1E01C9139
+:1002500001FF1B7F01FD14601C93B1E0A1E00C9134
+:1002600003FD05C0B1E0A1E00C910B7F0C93B0E061
+:10027000A1E20C9102FD05C0B1E0A1E00C910B7F61
+:100280000C93B1E0A1E00C9102FF12C0B1E0A1E03B
+:100290000C9105FD0DC0B1E0A6E00C91B1E0A7E026
+:1002A0001C91039509F413951C93B1E0A6E00C93FF
+:1002B000B1E0A1E00C91B1E0A1E01C9102FF1F7D33
+:1002C00002FD10621C93B1E0A6E00C91B1E0A7E042
+:1002D0001C9124E130E00217130734F4B1E0A1E0EF
+:1002E0000C910B7F0C9305C0B1E0A1E00C91046070
+:1002F0000C93B1E0A8E00C91B1E0A9E01C9127EECD
+:1003000033E002171307C4F4B1E0A1E00C9102FF3F
+:100310000DC0B1E0A8E00C91B1E0A9E01C910395FB
+:1003200009F413951C93B1E0A8E00C93B1E0A1E0AF
+:100330000C910B7F0C9305C0B1E0A1E00C9104601F
+:100340000C93B1E0A1E00C91B0E0A2E61C9102FF99
+:100350001F7D02FD10621C93B1E0A1E00C91B1E0A1
+:10036000A1E01C9101FF1B7F01FD14601C93B1E013
+:10037000A1E00C9103FD05C0B1E0A1E00C910B7F61
+:100380000C93B0E0A1E20C9100FD05C0B1E0A1E04A
+:100390000C910B7F0C93B1E0A1E00C9102FF21C006
+:1003A000B1E0A1E00C9106FD1CC0B1E0AAE001E0C3
+:1003B0000C93B1E0ABE000E00C93B1E0A6E02C912F
+:1003C000B1E0A7E03C91B1E0AAE00C91B1E0ABE074
+:1003D0001C91201B310BB1E0A6E02C93B1E0A7E00B
+:1003E0003C93B1E0A1E00C91B1E0A1E01C9102FFCF
+:1003F0001F7B02FD10641C93B1E0A6E00C91B1E0FC
+:10040000A7E01C912AE030E00217130734F4B1E0B2
+:10041000A1E00C910B7F0C9305C0B1E0A1E00C9121
+:1004200004600C93B1E0A1E00C91B0E0A2E31C9158
+:1004300002FF1B7F02FD14601C93B1E0A1E00C9150
+:10044000B1E0A1E01C9101FF1B7F01FD14601C9332
+:10045000B0E0A1E20C9101FD05C0B1E0A1E00C917A
+:100460000B7F0C93B1E0A1E00C910F770C93B1E0FE
+:10047000A1E00C91B1E0ACE01C9102FF1E7F02FDF7
+:1004800011601C93B1E0ACE00C91B1E0ACE01C91C8
+:1004900000FF1D7F00FD12601C93B1E0ACE00C91E9
+:1004A00002FF05C0B1E0ACE00C910E7F0C93B1E00F
+:1004B000ACE00C91B1E0ACE01C9101FF1B7F01FDB1
+:1004C00014601C93B1E0ACE00C9100FF05C0B1E0FA
+:1004D000A1E00C9100680C93B1E0A1E00C91B1E0B7
+:1004E000ACE01C9102FF1E7F02FD11601C93B1E085
+:1004F000ACE00C91B1E0ACE01C9100FF1D7F00FD71
+:1005000012601C93B1E0ACE00C9100FD0BC0B1E0B7
+:10051000ACE00C9103FF05C0B1E0ACE00C910160D0
+:100520000C9305C0B1E0ACE00C910E7F0C93B1E0F0
+:10053000ACE00C91B1E0ACE01C9101FF177F01FD34
+:1005400018601C93B1E0ACE00C9100FF05C0B1E075
+:10055000A1E00C9100680C93B1E0A1E00C91B1E036
+:10056000A1E01C9107FF1B7F07FD14601C93B1E005
+:10057000A1E00C9102FF08C0B1E0A8E000E00C93FC
+:10058000B1E0A9E000E00C93B1E0A1E00C91B1E092
+:10059000A1E01C9101FF1B7F01FD14601C93B1E0E1
+:1005A000A1E00C9103FD05C0B1E0A1E00C910B7F2F
+:1005B0000C93B1E0A1E00C9102FF26C0B1E0ACE0E9
+:1005C0000C9104FD21C0B1E0ADE00C91B1E0AEE0D2
+:1005D0001C91039509F413951C93B1E0ADE00C93C5
+:1005E000B1E0ADE00C91B1E0AEE01C9128E030E06C
+:1005F000021713070CF408C0B1E0ADE000E00C9363
+:10060000B1E0AEE000E00C93B1E0A1E00C91B1E00C
+:10061000ACE01C9102FF1F7E02FD10611C93B1E053
+:10062000A1E00C91B1E0A1E01C9101FF1B7F01FD55
+:1006300014601C93B1E0AFE003E00C93B1E0A0E1E3
+:1006400000E00C93B1E0ADE00C91B1E0AEE01C91A4
+:10065000B1E0AFE02C91B1E0A0E13C9102171307AB
+:1006600009F405C0B1E0A1E00C910B7F0C93B1E05F
+:10067000A1E00C91B1E0ACE01C9102FF1D7F02FDF6
+:1006800012601C93B1E0A1E00C9102FD0BC0B1E03F
+:10069000ACE00C9105FF05C0B1E0A1E00C91046055
+:1006A0000C9305C0B1E0A1E00C910B7F0C93B1E07D
+:1006B000ACE00C91B1E0ACE01C9101FF1F7D01FDAD
+:1006C00010621C93B1E0ACE00C9106FD08C0B1E0F3
+:1006D000A1E103E10C93B1E0A2E100E00C93B1E0F1
+:1006E000ACE00C9100640C93B1E0A1E00C9102FD30
+:1006F0001EC0B1E0A1E10C91B1E0A2E11C9123E1A7
+:1007000030E00217130794F4B1E0A1E10C91B1E0DD
+:10071000A2E11C91039509F413951C93B1E0A1E1AA
+:100720000C93B1E0A1E00C9104600C9308C0B1E01F
+:10073000A1E100E00C93B1E0A2E100E00C93B1E094
+:10074000A1E00C91B0E0ABE31C9102FF1F7D02FD24
+:0A07500010621C93E5E8F0E0099444
+:00000001FF
diff --git a/ldmicro/reg/run-tests.pl b/ldmicro/reg/run-tests.pl
new file mode 100644
index 0000000..1220ca9
--- /dev/null
+++ b/ldmicro/reg/run-tests.pl
@@ -0,0 +1,33 @@
+#!/usr/bin/perl
+
+if (not -d 'results/') {
+ mkdir 'results';
+}
+
+$c = 0;
+for $test () {
+ $output = $test;
+ $output =~ s/^tests/results/;
+ $output =~ s/\.ld$/.hex/;
+
+ unlink $output;
+
+ $cmd = "../ldmicro.exe /c $test $output";
+ system $cmd;
+ $c++;
+}
+
+print "\ndifferences follow:\n";
+@diff = `diff -q results expected`;
+for(@diff) {
+ print " $_";
+}
+$fc = scalar @diff;
+print "($fc difference(s)/$c)\n";
+if($fc == 0) {
+ print "pass!\n";
+ exit(0);
+} else {
+ print "FAIL\n";
+ exit(-1);
+}
diff --git a/ldmicro/reg/tests/avr-hw.ld b/ldmicro/reg/tests/avr-hw.ld
new file mode 100644
index 0000000..f25da88
--- /dev/null
+++ b/ldmicro/reg/tests/avr-hw.ld
@@ -0,0 +1,61 @@
+LDmicro0.1
+MICRO=Atmel AVR ATmega128 64-TQFP
+CYCLE=10000
+CRYSTAL=4000000
+BAUD=2400
+COMPILED=C:\depot\ldmicro\reg\expected\avr-hw.hex
+
+IO LIST
+ Xinc at 2
+ Aanalog at 61
+END
+
+PROGRAM
+RUNG
+ COMMENT The more hardware-oriented ops (PWM, ADC, UART, EEPROM), test for the\r\nAVRs.
+END
+RUNG
+ PERSIST saved
+END
+RUNG
+ READ_ADC Aanalog
+END
+RUNG
+ PARALLEL
+ CONTACTS Xinc 0
+ GEQ Aanalog 512
+ END
+ OSR
+ ADD saved saved 1
+END
+RUNG
+ PARALLEL
+ SERIES
+ LES saved 10
+ MOVE duty 20
+ END
+ SERIES
+ GEQ saved 10
+ MOVE duty 50
+ END
+ SET_PWM duty 2000
+ END
+END
+RUNG
+ TON Tfmtd 1000000
+ OSR
+ FORMATTED_STRING saved 15 115 97 118 101 100 32 61 32 92 45 53 92 114 92 110
+END
+RUNG
+ MOVE char 'x'
+END
+RUNG
+ TON Tutx 2000000
+ OSR
+ UART_SEND char
+END
+RUNG
+ UART_RECV inchar
+ EQU inchar 'a'
+ MOVE saved 0
+END
diff --git a/ldmicro/reg/tests/contacts.ld b/ldmicro/reg/tests/contacts.ld
new file mode 100644
index 0000000..efe35bf
--- /dev/null
+++ b/ldmicro/reg/tests/contacts.ld
@@ -0,0 +1,41 @@
+LDmicro0.1
+MICRO=Microchip PIC16F876 28-PDIP or 28-SOIC
+CYCLE=10000
+CRYSTAL=4000000
+BAUD=2400
+COMPILED=C:\depot\ldmicro\reg\expected\contacts.hex
+
+IO LIST
+ Xa at 2
+ Xb at 3
+ Xc at 4
+ Xd at 5
+ Xe at 6
+ Yf at 11
+ Yg at 7
+ Yh at 12
+END
+
+PROGRAM
+RUNG
+ COMMENT All types of contacts (NO, NC), and all types of coils (normal, inverted, set,\r\nreset); also test series and parallel combinations.
+END
+RUNG
+ CONTACTS Xa 0
+ COIL Yf 0 0 0
+END
+RUNG
+ PARALLEL
+ CONTACTS Xb 0
+ CONTACTS Xc 0
+ END
+ COIL Yg 0 1 0
+END
+RUNG
+ CONTACTS Xd 0
+ CONTACTS Xe 1
+ PARALLEL
+ COIL Yg 0 0 1
+ COIL Yh 1 0 0
+ END
+END
diff --git a/ldmicro/reg/tests/integers.ld b/ldmicro/reg/tests/integers.ld
new file mode 100644
index 0000000..2beea69
--- /dev/null
+++ b/ldmicro/reg/tests/integers.ld
@@ -0,0 +1,61 @@
+LDmicro0.1
+MICRO=Atmel AVR ATmega162 40-PDIP
+CYCLE=50000
+CRYSTAL=10000000
+BAUD=2400
+COMPILED=C:\depot\ldmicro\reg\expected\integers.hex
+
+IO LIST
+ Xa at 1
+ Yno at 16
+ Yok at 37
+END
+
+PROGRAM
+RUNG
+ COMMENT Test integer variable manipulations: move, arithmetic, comparison. Also test\r\na 50 ms cycle time, and the ATmega162 (odd timer register locations).
+END
+RUNG
+ PARALLEL
+ SERIES
+ CONTACTS Xa 0
+ MOVE a 1323
+ END
+ SERIES
+ CONTACTS Xa 1
+ MOVE a -12
+ END
+ END
+END
+RUNG
+ PARALLEL
+ ADD c a 1000
+ SUB d a 5
+ MUL e a 3
+ DIV f e 3
+ END
+END
+RUNG
+ PARALLEL
+ EQU a 1323
+ SHORT
+ END
+ PARALLEL
+ LES c 0
+ LEQ d 1318
+ SHORT
+ END
+ SHORT
+ PARALLEL
+ GRT c 20000
+ GEQ e 3969
+ OPEN
+ END
+ COIL Yok 0 0 0
+END
+RUNG
+ NEQ e -36
+ GRT a 0
+ TON Ton 1000000
+ COIL Yno 0 0 0
+END
diff --git a/ldmicro/reg/tests/misc-ops.ld b/ldmicro/reg/tests/misc-ops.ld
new file mode 100644
index 0000000..5197d9c
--- /dev/null
+++ b/ldmicro/reg/tests/misc-ops.ld
@@ -0,0 +1,53 @@
+LDmicro0.1
+MICRO=Microchip PIC16F876 28-PDIP or 28-SOIC
+CYCLE=10000
+CRYSTAL=4000000
+BAUD=2400
+COMPILED=C:\depot\ldmicro\reg\expected\misc-ops.hex
+
+IO LIST
+ Xon at 2
+ Yalways at 3
+ Ain at 4
+END
+
+PROGRAM
+RUNG
+ COMMENT Little things: shift register, look-up table, master control relay.\r\n
+END
+RUNG
+ CONTACTS Xon 0
+ MASTER_RELAY
+END
+RUNG
+ CONTACTS Rosc 0
+ TON Ton 500000
+ TOF Tof 500000
+ COIL Rosc 1 0 0
+END
+RUNG
+ CONTACTS Rosc 0
+ SHIFT_REGISTER reg 7
+END
+RUNG
+ PARALLEL
+ READ_ADC Ain
+ MOVE reg0 Ain
+ END
+END
+RUNG
+ PARALLEL
+ SERIES
+ CONTACTS Rosc 0
+ CTC Ci 4
+ END
+ LOOK_UP_TABLE duty Ci 5 0 10 70 80 70 10
+ SET_PWM duty 10000
+ END
+END
+RUNG
+ MASTER_RELAY
+END
+RUNG
+ COIL Yalways 0 0 0
+END
diff --git a/ldmicro/reg/tests/pic16-hw.ld b/ldmicro/reg/tests/pic16-hw.ld
new file mode 100644
index 0000000..7a47f5f
--- /dev/null
+++ b/ldmicro/reg/tests/pic16-hw.ld
@@ -0,0 +1,61 @@
+LDmicro0.1
+MICRO=Microchip PIC16F877 40-PDIP
+CYCLE=10000
+CRYSTAL=4000000
+BAUD=2400
+COMPILED=C:\depot\ldmicro\reg\expected\pic-hw.hex
+
+IO LIST
+ Xinc at 2
+ Aanalog at 3
+END
+
+PROGRAM
+RUNG
+ COMMENT The more hardware-oriented ops (PWM, ADC, UART, EEPROM), test for the\r\nPIC16s.
+END
+RUNG
+ PERSIST saved
+END
+RUNG
+ READ_ADC Aanalog
+END
+RUNG
+ PARALLEL
+ CONTACTS Xinc 0
+ GEQ Aanalog 512
+ END
+ OSR
+ ADD saved saved 1
+END
+RUNG
+ PARALLEL
+ SERIES
+ LES saved 10
+ MOVE duty 20
+ END
+ SERIES
+ GEQ saved 10
+ MOVE duty 50
+ END
+ SET_PWM duty 2000
+ END
+END
+RUNG
+ TON Tfmtd 1000000
+ OSR
+ FORMATTED_STRING saved 15 115 97 118 101 100 32 61 32 92 45 53 92 114 92 110
+END
+RUNG
+ MOVE char 'x'
+END
+RUNG
+ TON Tutx 2000000
+ OSR
+ UART_SEND char
+END
+RUNG
+ UART_RECV inchar
+ EQU inchar 'a'
+ MOVE saved 0
+END
diff --git a/ldmicro/reg/tests/timers-counters.ld b/ldmicro/reg/tests/timers-counters.ld
new file mode 100644
index 0000000..606aac2
--- /dev/null
+++ b/ldmicro/reg/tests/timers-counters.ld
@@ -0,0 +1,57 @@
+LDmicro0.1
+MICRO=Atmel AVR ATmega128 64-TQFP
+CYCLE=10000
+CRYSTAL=4000000
+BAUD=2400
+COMPILED=C:\depot\ldmicro\reg\expected\timers-counters.ld
+
+IO LIST
+ Xdown at 2
+ Xres at 3
+ Xup at 4
+ Ydown at 27
+ Ypulse at 46
+ Yup at 56
+END
+
+PROGRAM
+RUNG
+ COMMENT Test all the timers (TON, TOF, RTO) and counters (CTC, CTU, CTD), and the\r\nreset (RES) instruction. Also test one-shots.
+END
+RUNG
+ CONTACTS Rosc 0
+ TOF Tof 100000
+ TON Ton 100000
+ COIL Rosc 1 0 0
+END
+RUNG
+ CONTACTS Rosc 0
+ CONTACTS Xup 0
+ CTU Ccnt 20
+ RTO Trto 10000000
+ COIL Yup 0 0 0
+END
+RUNG
+ CONTACTS Rosc 0
+ CONTACTS Xdown 0
+ CTD Ccnt 10
+ COIL Ydown 0 0 0
+END
+RUNG
+ CONTACTS Xres 0
+ PARALLEL
+ OSR
+ OSF
+ END
+ RES Trto
+END
+RUNG
+ CONTACTS Rosc 0
+ CTC Ccirc 7
+END
+RUNG
+ EQU Ccirc 3
+ OSF
+ TOF Tpulse 200000
+ COIL Ypulse 0 0 0
+END
diff --git a/ldmicro/resetdialog.cpp b/ldmicro/resetdialog.cpp
new file mode 100644
index 0000000..3c27935
--- /dev/null
+++ b/ldmicro/resetdialog.cpp
@@ -0,0 +1,148 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Dialog for setting the properties of a set of a RES reset element: name,
+// which can be that of either a timer or a counter.
+// Jonathan Westhues, Nov 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+static HWND ResetDialog;
+
+static HWND TypeTimerRadio;
+static HWND TypeCounterRadio;
+static HWND NameTextbox;
+
+static LONG_PTR PrevNameProc;
+
+//-----------------------------------------------------------------------------
+// Don't allow any characters other than A-Za-z0-9_ in the name.
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK MyNameProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam)
+{
+ if(msg == WM_CHAR) {
+ if(!(isalpha(wParam) || isdigit(wParam) || wParam == '_' ||
+ wParam == '\b'))
+ {
+ return 0;
+ }
+ }
+
+ return CallWindowProc((WNDPROC)PrevNameProc, hwnd, msg, wParam, lParam);
+}
+
+static void MakeControls(void)
+{
+ HWND grouper = CreateWindowEx(0, WC_BUTTON, _("Type"),
+ WS_CHILD | BS_GROUPBOX | WS_VISIBLE,
+ 7, 3, 120, 65, ResetDialog, NULL, Instance, NULL);
+ NiceFont(grouper);
+
+ TypeTimerRadio = CreateWindowEx(0, WC_BUTTON, _("Timer"),
+ WS_CHILD | BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE,
+ 16, 21, 100, 20, ResetDialog, NULL, Instance, NULL);
+ NiceFont(TypeTimerRadio);
+
+ TypeCounterRadio = CreateWindowEx(0, WC_BUTTON, _("Counter"),
+ WS_CHILD | BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE,
+ 16, 41, 100, 20, ResetDialog, NULL, Instance, NULL);
+ NiceFont(TypeCounterRadio);
+
+ HWND textLabel = CreateWindowEx(0, WC_STATIC, _("Name:"),
+ WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | SS_RIGHT,
+ 135, 16, 50, 21, ResetDialog, NULL, Instance, NULL);
+ NiceFont(textLabel);
+
+ NameTextbox = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "",
+ WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 190, 16, 115, 21, ResetDialog, NULL, Instance, NULL);
+ FixedFont(NameTextbox);
+
+ OkButton = CreateWindowEx(0, WC_BUTTON, _("OK"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_DEFPUSHBUTTON,
+ 321, 10, 70, 23, ResetDialog, NULL, Instance, NULL);
+ NiceFont(OkButton);
+
+ CancelButton = CreateWindowEx(0, WC_BUTTON, _("Cancel"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 321, 40, 70, 23, ResetDialog, NULL, Instance, NULL);
+ NiceFont(CancelButton);
+
+ PrevNameProc = SetWindowLongPtr(NameTextbox, GWLP_WNDPROC,
+ (LONG_PTR)MyNameProc);
+}
+
+void ShowResetDialog(char *name)
+{
+ ResetDialog = CreateWindowClient(0, "LDmicroDialog",
+ _("Reset"), WS_OVERLAPPED | WS_SYSMENU,
+ 100, 100, 404, 75, NULL, NULL, Instance, NULL);
+
+ MakeControls();
+
+ if(name[0] == 'T') {
+ SendMessage(TypeTimerRadio, BM_SETCHECK, BST_CHECKED, 0);
+ } else {
+ SendMessage(TypeCounterRadio, BM_SETCHECK, BST_CHECKED, 0);
+ }
+ SendMessage(NameTextbox, WM_SETTEXT, 0, (LPARAM)(name + 1));
+
+ EnableWindow(MainWindow, FALSE);
+ ShowWindow(ResetDialog, TRUE);
+ SetFocus(NameTextbox);
+ SendMessage(NameTextbox, EM_SETSEL, 0, -1);
+
+ MSG msg;
+ DWORD ret;
+ DialogDone = FALSE;
+ DialogCancel = FALSE;
+ while((ret = GetMessage(&msg, NULL, 0, 0)) && !DialogDone) {
+ if(msg.message == WM_KEYDOWN) {
+ if(msg.wParam == VK_RETURN) {
+ DialogDone = TRUE;
+ break;
+ } else if(msg.wParam == VK_ESCAPE) {
+ DialogDone = TRUE;
+ DialogCancel = TRUE;
+ break;
+ }
+ }
+
+ if(IsDialogMessage(ResetDialog, &msg)) continue;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ if(!DialogCancel) {
+ if(SendMessage(TypeTimerRadio, BM_GETSTATE, 0, 0) & BST_CHECKED) {
+ name[0] = 'T';
+ } else {
+ name[0] = 'C';
+ }
+ SendMessage(NameTextbox, WM_GETTEXT, (WPARAM)16, (LPARAM)(name+1));
+ }
+
+ EnableWindow(MainWindow, TRUE);
+ DestroyWindow(ResetDialog);
+}
diff --git a/ldmicro/sample/7seg-display.ld b/ldmicro/sample/7seg-display.ld
new file mode 100644
index 0000000..3bf5434
--- /dev/null
+++ b/ldmicro/sample/7seg-display.ld
@@ -0,0 +1,123 @@
+LDmicro0.1
+CYCLE=5000
+CRYSTAL=4000000
+BAUD=2400
+
+IO LIST
+ Ycom_digit0 at 0
+ Ycom_digit1 at 0
+ Ycom_digit2 at 0
+ Yseg_a at 0
+ Yseg_b at 0
+ Yseg_c at 0
+ Yseg_d at 0
+ Yseg_e at 0
+ Yseg_f at 0
+ Yseg_g at 0
+END
+
+PROGRAM
+RUNG
+ COMMENT Sample program: how to drive a multiplexed 7-segment LED display. This is\r\nfor a 3-digit common-cathode display but it is easy to modify.
+END
+RUNG
+ COMMENT With a 5 ms cycle time, this will oscillate at 100 Hz, which should be\r\nokay (33 Hz refresh rate, 1/3 duty cycle).
+END
+RUNG
+ CONTACTS Rdosc 1
+ COIL Rdosc 0 0 0
+END
+RUNG
+ CONTACTS Rdosc 0
+ CTC Cdigit 2
+END
+RUNG
+ COMMENT Use the Ycom_digitx lines to drive the base/gate of the NPN transistor/\r\nn-FET used to switch the cathode of digit x.
+END
+RUNG
+ PARALLEL
+ SERIES
+ EQU Cdigit 0
+ PARALLEL
+ COIL Ycom_digit0 0 0 0
+ MOVE digit digit0
+ END
+ END
+ SERIES
+ EQU Cdigit 1
+ PARALLEL
+ COIL Ycom_digit1 0 0 0
+ MOVE digit digit1
+ END
+ END
+ SERIES
+ EQU Cdigit 2
+ PARALLEL
+ COIL Ycom_digit2 0 0 0
+ MOVE digit digit2
+ END
+ END
+ END
+END
+RUNG
+ COMMENT You can drive the segment pins of the display directly from the GPIO pins,\r\nYseg_a to Yseg_b.
+END
+RUNG
+ PARALLEL
+ SERIES
+ NEQ digit 1
+ NEQ digit 4
+ COIL Yseg_a 0 0 0
+ END
+ SERIES
+ NEQ digit 5
+ NEQ digit 6
+ COIL Yseg_b 0 0 0
+ END
+ SERIES
+ NEQ digit 2
+ COIL Yseg_c 0 0 0
+ END
+ SERIES
+ NEQ digit 1
+ NEQ digit 4
+ NEQ digit 7
+ COIL Yseg_d 0 0 0
+ END
+ SERIES
+ PARALLEL
+ EQU digit 0
+ EQU digit 2
+ EQU digit 6
+ EQU digit 8
+ END
+ COIL Yseg_e 0 0 0
+ END
+ SERIES
+ NEQ digit 1
+ NEQ digit 2
+ NEQ digit 3
+ NEQ digit 7
+ COIL Yseg_f 0 0 0
+ END
+ SERIES
+ NEQ digit 0
+ NEQ digit 1
+ NEQ digit 7
+ COIL Yseg_g 0 0 0
+ END
+ END
+END
+RUNG
+ COMMENT Fill in your program here; just set the output that you want on digit0,\r\ndigit1, and digit2.
+END
+RUNG
+ PARALLEL
+ MOVE digit0 1
+ MOVE digit1 8
+ MOVE digit2 5
+ END
+END
+RUNG
+ COMMENT by Jonathan Westhues, June 2005
+END
diff --git a/ldmicro/sample/blink.ld b/ldmicro/sample/blink.ld
new file mode 100644
index 0000000..a9c6a51
--- /dev/null
+++ b/ldmicro/sample/blink.ld
@@ -0,0 +1,61 @@
+LDmicro0.1
+MICRO=Microchip PIC16F877 40-PDIP
+CYCLE=10000
+CRYSTAL=4000000
+COMPILED=C:\depot\ldmicro\pic.hex
+
+IO LIST
+ Xbutton at 21
+ Yled at 30
+END
+
+PROGRAM
+RUNG
+ CONTACTS Xbutton 1
+ CTC Cwhich 4
+END
+RUNG
+ CONTACTS Xbutton 1
+ OSF
+ TOF Tdblclk 500000
+ COIL Rdblclk 0 0 0
+END
+RUNG
+ CONTACTS Xbutton 1
+ CONTACTS Rdblclk 0
+ MOVE Cwhich 3
+END
+RUNG
+ CONTACTS Rfast 0
+ TOF Tffast 100000
+ TON Tnfast 100000
+ COIL Rfast 1 0 0
+END
+RUNG
+ CONTACTS Rslow 0
+ TON Tnslow 100000
+ TOF Tfslow 1000000
+ COIL Rslow 1 0 0
+END
+RUNG
+ PARALLEL
+ SERIES
+ EQU Cwhich 0
+ CONTACTS Rfast 0
+ END
+ SERIES
+ EQU Cwhich 1
+ CONTACTS Rslow 0
+ END
+ SERIES
+ EQU Cwhich 2
+ CONTACTS Rslow 1
+ END
+ EQU Cwhich 3
+ SERIES
+ EQU Cwhich 4
+ OPEN
+ END
+ END
+ COIL Yled 0 0 0
+END
diff --git a/ldmicro/sample/pic-adc.ld b/ldmicro/sample/pic-adc.ld
new file mode 100644
index 0000000..bae217f
--- /dev/null
+++ b/ldmicro/sample/pic-adc.ld
@@ -0,0 +1,158 @@
+LDmicro0.1
+MICRO=Microchip PIC16F877 40-PDIP
+CYCLE=10000
+CRYSTAL=4000000
+BAUD=2400
+COMPILED=c:\temp\asd.hex
+
+IO LIST
+ Xlimit at 2
+ Yled at 22
+ Ain at 3
+END
+
+PROGRAM
+RUNG
+ COMMENT Serial/ADC test program for PIC16F877: read an ADC input, convert from integer to string\r\n(three ASCII chars), then send it out over the serial port. Display a message at startup:
+END
+RUNG
+ PARALLEL
+ SERIES
+ EQU Cinit 2
+ MOVE initc 'a'
+ END
+ SERIES
+ EQU Cinit 3
+ MOVE initc 'd'
+ END
+ SERIES
+ EQU Cinit 4
+ MOVE initc 'c'
+ END
+ SERIES
+ PARALLEL
+ EQU Cinit 0
+ EQU Cinit 5
+ END
+ MOVE initc 13
+ END
+ SERIES
+ PARALLEL
+ EQU Cinit 1
+ EQU Cinit 6
+ END
+ MOVE initc 10
+ END
+ END
+END
+RUNG
+ TON Ttod 500000
+ CONTACTS Rinitdone 1
+ CONTACTS Rtxbusy 1
+ UART_SEND initc
+ COIL Rtxbusy 0 0 0
+END
+RUNG
+ CONTACTS Rtxbusy 0
+ CTU Cinit 7
+ COIL Rinitdone 0 0 0
+END
+RUNG
+ COMMENT Clock for the ADC read and a circular counter that sequences it. Just block the\r\noscillator until the init sequence completes.
+END
+RUNG
+ CONTACTS Rinitdone 0
+ CONTACTS Rosc 1
+ TOF Tct1 50000
+ TON Tct2 50000
+ COIL Rosc 0 0 0
+END
+RUNG
+ CONTACTS Rosc 0
+ CTC Cchar 4
+END
+RUNG
+ OSR
+ MOVE Cchar 3
+END
+RUNG
+ COMMENT Read the A/D input, then saturate the input so that we only have to display three digits.
+END
+RUNG
+ PARALLEL
+ READ_ADC Ain
+ SERIES
+ LEQ Ain 999
+ MOVE intval Ain
+ END
+ SERIES
+ GEQ Ain 1000
+ MOVE intval 999
+ END
+ END
+END
+RUNG
+ COMMENT Split the integer into the ones, tens, and hundreds digits. This is just cumbersome, but\r\nit does not waste much memory (the multiply and divide are subroutines in the PIC code).
+END
+RUNG
+ PARALLEL
+ DIV hundreds intval 100
+ MUL hundredsv hundreds 100
+ SUB wohundreds intval hundredsv
+ DIV tens wohundreds 10
+ MUL tensv tens 10
+ SUB ones wohundreds tensv
+ END
+END
+RUNG
+ COMMENT Now convert the ones, tens, and hundreds digits to ASCII characters by adding\r\nASCII character '0' (since the ASCII digits are contiguous in order).
+END
+RUNG
+ PARALLEL
+ ADD hunc hundreds '0'
+ ADD tensc tens '0'
+ ADD onesc ones '0'
+ END
+END
+RUNG
+ COMMENT Then send out the characters. For each line we must send out five characters:\r\nhundreds, tens, ones, ones, carriage return, newline; use a CTC to sequence.
+END
+RUNG
+ PARALLEL
+ SERIES
+ EQU Cchar 0
+ MOVE outch hunc
+ END
+ SERIES
+ EQU Cchar 1
+ MOVE outch tensc
+ END
+ SERIES
+ EQU Cchar 2
+ MOVE outch onesc
+ END
+ SERIES
+ EQU Cchar 3
+ MOVE outch 13
+ END
+ SERIES
+ EQU Cchar 4
+ MOVE outch 10
+ END
+ END
+END
+RUNG
+ COMMENT Time it off Rosc (10 characters/s) which is slow so ignore the busy out.
+END
+RUNG
+ CONTACTS Rosc 0
+ OSR
+ UART_SEND outch
+END
+RUNG
+ PARALLEL
+ CONTACTS Rosc 0
+ CONTACTS Xlimit 1
+ END
+ COIL Yled 0 0 0
+END
diff --git a/ldmicro/sample/serial.ld b/ldmicro/sample/serial.ld
new file mode 100644
index 0000000..d1b47ee
--- /dev/null
+++ b/ldmicro/sample/serial.ld
@@ -0,0 +1,92 @@
+LDmicro0.1
+MICRO=Microchip PIC16F877 40-PDIP
+CYCLE=10000
+CRYSTAL=4000000
+BAUD=2400
+COMPILED=c:\temp\ser.hex
+
+IO LIST
+ Yled at 22
+END
+
+PROGRAM
+RUNG
+ CONTACTS Rosc 1
+ TOF Tct1 250000
+ TON Tct2 250000
+ COIL Rosc 0 0 0
+END
+RUNG
+ CONTACTS Rfast 1
+ TOF Tct3 100000
+ TON Tct4 100000
+ COIL Rfast 0 0 0
+END
+RUNG
+ CONTACTS Rosc 0
+ CTC Cwhich 4
+END
+RUNG
+ PARALLEL
+ SERIES
+ EQU Cwhich 0
+ MOVE ch 'A'
+ END
+ SERIES
+ EQU Cwhich 1
+ MOVE ch 'b'
+ END
+ SERIES
+ EQU Cwhich 2
+ MOVE ch 'c'
+ END
+ SERIES
+ EQU Cwhich 3
+ MOVE ch 13
+ END
+ SERIES
+ EQU Cwhich 4
+ MOVE ch 10
+ END
+ END
+END
+RUNG
+ CONTACTS Rosc 0
+ OSF
+ UART_SEND ch
+END
+RUNG
+ UART_RECV inch
+ COIL Rjustgot 0 1 0
+END
+RUNG
+ CONTACTS Rjustgot 0
+ TON Tnew 1000000
+ COIL Rjustgot 0 0 1
+END
+RUNG
+ PARALLEL
+ SERIES
+ CONTACTS Rjustgot 0
+ CONTACTS Rfast 0
+ END
+ SERIES
+ CONTACTS Rjustgot 1
+ PARALLEL
+ SERIES
+ EQU inch 'o'
+ SHORT
+ END
+ SERIES
+ EQU inch 'b'
+ CONTACTS Rosc 0
+ END
+ SERIES
+ SHORT
+ OPEN
+ END
+ END
+ END
+ END
+ COIL Yled 0 0 0
+END
diff --git a/ldmicro/sample/traffic.ld b/ldmicro/sample/traffic.ld
new file mode 100644
index 0000000..697c6f0
--- /dev/null
+++ b/ldmicro/sample/traffic.ld
@@ -0,0 +1,73 @@
+LDmicro0.1
+MICRO=Atmel AVR ATmega128 64-TQFP
+CYCLE=10000
+CRYSTAL=4000000
+COMPILED=C:\depot\ldmicro\out.hex
+
+IO LIST
+ Xbutton1 at 46
+ Xbutton2 at 45
+ Ygreen at 49
+ Yred at 51
+ Ywait at 48
+ Yyellow at 50
+END
+
+PROGRAM
+RUNG
+ CONTACTS Rclock 1
+ TOF Ton 500000
+ TON Toff 500000
+ COIL Rclock 0 0 0
+END
+RUNG
+ CONTACTS Rclock 0
+ CTC Ccycle 19
+END
+RUNG
+ GEQ Ccycle 0
+ LES Ccycle 8
+ COIL Yred 0 0 0
+END
+RUNG
+ GEQ Ccycle 8
+ LES Ccycle 12
+ COIL Yyellow 0 0 0
+END
+RUNG
+ GEQ Ccycle 12
+ LES Ccycle 20
+ PARALLEL
+ COIL Ygreen 0 0 0
+ COIL Rwait 0 0 1
+ END
+END
+RUNG
+ PARALLEL
+ CONTACTS Xbutton1 1
+ CONTACTS Xbutton2 1
+ END
+ PARALLEL
+ SERIES
+ GEQ Ccycle 0
+ LES Ccycle 6
+ MOVE cycle 6
+ END
+ COIL Rwait 0 1 0
+ END
+END
+RUNG
+ CONTACTS Rwait 0
+ PARALLEL
+ SERIES
+ GEQ Ccycle 8
+ LES Ccycle 12
+ CONTACTS Rclock 0
+ END
+ SERIES
+ GEQ Ccycle 0
+ LES Ccycle 8
+ END
+ END
+ COIL Ywait 0 0 0
+END
diff --git a/ldmicro/sample/var-timer.ld b/ldmicro/sample/var-timer.ld
new file mode 100644
index 0000000..3df0842
--- /dev/null
+++ b/ldmicro/sample/var-timer.ld
@@ -0,0 +1,63 @@
+LDmicro0.1
+CYCLE=10000
+CRYSTAL=4000000
+BAUD=2400
+
+IO LIST
+ Xin at 0
+ Yout at 0
+ Apot at 0
+END
+
+PROGRAM
+RUNG
+ COMMENT This is the actual variable-delay TOF timer. When Rdel_input goes high, Rdel_output goes high \r\nt_max cycle times later. When Rdel_input goes low, Rdel_output goes low immediately.
+END
+RUNG
+ CONTACTS Rdel_input 0
+ LEQ t_count t_max
+ ADD t_count t_count 1
+END
+RUNG
+ CONTACTS Rdel_input 1
+ MOVE t_count 0
+END
+RUNG
+ COMMENT The extra branch is to make a delay of 0 cycles work correctly.
+END
+RUNG
+ PARALLEL
+ GRT t_count t_max
+ SERIES
+ CONTACTS Rdel_input 0
+ EQU t_max 0
+ END
+ END
+ COIL Rdel_output 0 0 0
+END
+RUNG
+ COMMENT \r\n
+END
+RUNG
+ COMMENT This is just an example of how to use the timer. The voltage attached to Apot (which goes\r\nfrom 0 V to Vdd) sets the delay between when Xin goes high and when Yout goes high.
+END
+RUNG
+ READ_ADC Apot
+END
+RUNG
+ COMMENT This only works with a cycle time of 10 ms. If we want the delay to be between 0 s and 5 s,\r\nthen it should be 0 to 500 cycle times; the ADC goes from 0 to 1023, so (roughly) divide by 2.
+END
+RUNG
+ DIV t_max Apot 2
+END
+RUNG
+ COMMENT Then just hook up the input and the output of the timer to pins. Of course\r\nthat could drive more complicated logic, not just a pin on the processor.
+END
+RUNG
+ CONTACTS Xin 0
+ COIL Rdel_input 0 0 0
+END
+RUNG
+ CONTACTS Rdel_output 0
+ COIL Yout 0 0 0
+END
diff --git a/ldmicro/schematic.cpp b/ldmicro/schematic.cpp
new file mode 100644
index 0000000..0046580
--- /dev/null
+++ b/ldmicro/schematic.cpp
@@ -0,0 +1,725 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// Stuff for editing the schematic, mostly tying in with the cursor somehow.
+// Actual manipulation of circuit elements happens in circuit.cpp, though.
+// Jonathan Westhues, Oct 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+// Not all options all available e.g. can't delete the only relay coil in
+// a rung, can't insert two coils in series, etc. Keep track of what is
+// allowed so we don't corrupt our program.
+BOOL CanInsertEnd;
+BOOL CanInsertOther;
+BOOL CanInsertComment;
+
+// Ladder logic program is laid out on a grid program; this matrix tells
+// us which leaf element is in which box on the grid, which allows us
+// to determine what element has just been selected when the user clicks
+// on something, for example.
+ElemLeaf *DisplayMatrix[DISPLAY_MATRIX_X_SIZE][DISPLAY_MATRIX_Y_SIZE];
+int DisplayMatrixWhich[DISPLAY_MATRIX_X_SIZE][DISPLAY_MATRIX_Y_SIZE];
+ElemLeaf *Selected;
+int SelectedWhich;
+
+ElemLeaf DisplayMatrixFiller;
+
+// Cursor within the ladder logic program. The paint routines figure out
+// where the cursor should go and calculate the coordinates (in pixels)
+// of the rectangle that describes it; then BlinkCursor just has to XOR
+// the requested rectangle at periodic intervals.
+PlcCursor Cursor;
+
+//-----------------------------------------------------------------------------
+// Find the address in the DisplayMatrix of the selected leaf element. Set
+// *gx and *gy if we succeed and return TRUE, else return FALSE.
+//-----------------------------------------------------------------------------
+BOOL FindSelected(int *gx, int *gy)
+{
+ if(!Selected) return FALSE;
+
+ int i, j;
+ for(i = 0; i < DISPLAY_MATRIX_X_SIZE; i++) {
+ for(j = 0; j < DISPLAY_MATRIX_Y_SIZE; j++) {
+ if(DisplayMatrix[i][j] == Selected) {
+ while(DisplayMatrix[i+1][j] == Selected) {
+ i++;
+ }
+ *gx = i;
+ *gy = j;
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Select the item in DisplayMatrix at (gx, gy), and then update everything
+// to reflect that. In particular change the enabled state of the menus so
+// that those items that do not apply are grayed, and scroll the element in
+// to view.
+//-----------------------------------------------------------------------------
+void SelectElement(int gx, int gy, int state)
+{
+ if(gx < 0 || gy < 0) {
+ if(!FindSelected(&gx, &gy)) {
+ return;
+ }
+ }
+
+ if(Selected) Selected->selectedState = SELECTED_NONE;
+
+ Selected = DisplayMatrix[gx][gy];
+ SelectedWhich = DisplayMatrixWhich[gx][gy];
+
+ if(SelectedWhich == ELEM_PLACEHOLDER) {
+ state = SELECTED_LEFT;
+ }
+
+ if((gy - ScrollYOffset) >= ScreenRowsAvailable()) {
+ ScrollYOffset = gy - ScreenRowsAvailable() + 1;
+ RefreshScrollbars();
+ }
+ if((gy - ScrollYOffset) < 0) {
+ ScrollYOffset = gy;
+ RefreshScrollbars();
+ }
+ if((gx - ScrollXOffset*POS_WIDTH*FONT_WIDTH) >= ScreenColsAvailable()) {
+ ScrollXOffset = gx*POS_WIDTH*FONT_WIDTH - ScreenColsAvailable();
+ RefreshScrollbars();
+ }
+ if((gx - ScrollXOffset*POS_WIDTH*FONT_WIDTH) < 0) {
+ ScrollXOffset = gx*POS_WIDTH*FONT_WIDTH;
+ RefreshScrollbars();
+ }
+
+ ok();
+ Selected->selectedState = state;
+ ok();
+
+ WhatCanWeDoFromCursorAndTopology();
+}
+
+//-----------------------------------------------------------------------------
+// Must be called every time the cursor moves or the cursor stays the same
+// but the circuit topology changes under it. Determines what we are allowed
+// to do: where coils can be added, etc.
+//-----------------------------------------------------------------------------
+void WhatCanWeDoFromCursorAndTopology(void)
+{
+ BOOL canNegate = FALSE, canNormal = FALSE;
+ BOOL canResetOnly = FALSE, canSetOnly = FALSE;
+ BOOL canPushUp = TRUE, canPushDown = TRUE;
+
+ BOOL canDelete = TRUE;
+
+ int i = RungContainingSelected();
+ if(i >= 0) {
+ if(i == 0) canPushUp = FALSE;
+ if(i == (Prog.numRungs-1)) canPushDown = FALSE;
+
+ if(Prog.rungs[i]->count == 1 &&
+ Prog.rungs[i]->contents[0].which == ELEM_PLACEHOLDER)
+ {
+ canDelete = FALSE;
+ }
+ }
+
+ CanInsertEnd = FALSE;
+ CanInsertOther = TRUE;
+
+ if(Selected &&
+ (SelectedWhich == ELEM_COIL ||
+ SelectedWhich == ELEM_RES ||
+ SelectedWhich == ELEM_ADD ||
+ SelectedWhich == ELEM_SUB ||
+ SelectedWhich == ELEM_MUL ||
+ SelectedWhich == ELEM_DIV ||
+ SelectedWhich == ELEM_CTC ||
+ SelectedWhich == ELEM_READ_ADC ||
+ SelectedWhich == ELEM_SET_PWM ||
+ SelectedWhich == ELEM_MASTER_RELAY ||
+ SelectedWhich == ELEM_SHIFT_REGISTER ||
+ SelectedWhich == ELEM_LOOK_UP_TABLE ||
+ SelectedWhich == ELEM_PIECEWISE_LINEAR ||
+ SelectedWhich == ELEM_PERSIST ||
+ SelectedWhich == ELEM_MOVE))
+ {
+ if(SelectedWhich == ELEM_COIL) {
+ canNegate = TRUE;
+ canNormal = TRUE;
+ canResetOnly = TRUE;
+ canSetOnly = TRUE;
+ }
+
+ if(Selected->selectedState == SELECTED_ABOVE ||
+ Selected->selectedState == SELECTED_BELOW)
+ {
+ CanInsertEnd = TRUE;
+ CanInsertOther = FALSE;
+ } else if(Selected->selectedState == SELECTED_RIGHT) {
+ CanInsertEnd = FALSE;
+ CanInsertOther = FALSE;
+ }
+ } else if(Selected) {
+ if(Selected->selectedState == SELECTED_RIGHT ||
+ SelectedWhich == ELEM_PLACEHOLDER)
+ {
+ CanInsertEnd = ItemIsLastInCircuit(Selected);
+ }
+ }
+ if(SelectedWhich == ELEM_CONTACTS) {
+ canNegate = TRUE;
+ canNormal = TRUE;
+ }
+ if(SelectedWhich == ELEM_PLACEHOLDER) {
+ // a comment must be the only element in its rung, and it will fill
+ // the rung entirely
+ CanInsertComment = TRUE;
+ } else {
+ CanInsertComment = FALSE;
+ }
+ if(SelectedWhich == ELEM_COMMENT) {
+ // if there's a comment there already then don't let anything else
+ // into the rung
+ CanInsertEnd = FALSE;
+ CanInsertOther = FALSE;
+ }
+ SetMenusEnabled(canNegate, canNormal, canResetOnly, canSetOnly, canDelete,
+ CanInsertEnd, CanInsertOther, canPushDown, canPushUp, CanInsertComment);
+}
+
+//-----------------------------------------------------------------------------
+// Rub out freed element from the DisplayMatrix, just so we don't confuse
+// ourselves too much (or access freed memory)...
+//-----------------------------------------------------------------------------
+void ForgetFromGrid(void *p)
+{
+ int i, j;
+ for(i = 0; i < DISPLAY_MATRIX_X_SIZE; i++) {
+ for(j = 0; j < DISPLAY_MATRIX_Y_SIZE; j++) {
+ if(DisplayMatrix[i][j] == p) {
+ DisplayMatrix[i][j] = NULL;
+ }
+ }
+ }
+ if(Selected == p) {
+ Selected = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Rub out everything from DisplayMatrix. If we don't do that before freeing
+// the program (e.g. when loading a new file) then there is a race condition
+// when we repaint.
+//-----------------------------------------------------------------------------
+void ForgetEverything(void)
+{
+ memset(DisplayMatrix, 0, sizeof(DisplayMatrix));
+ memset(DisplayMatrixWhich, 0, sizeof(DisplayMatrixWhich));
+ Selected = NULL;
+ SelectedWhich = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Select the top left element of the program. Returns TRUE if it was able
+// to do so, FALSE if not. The latter occurs given a completely empty
+// program.
+//-----------------------------------------------------------------------------
+BOOL MoveCursorTopLeft(void)
+{
+ int i, j;
+ // Let us first try to place it somewhere on-screen, so start at the
+ // vertical scroll offset, not the very top (to avoid placing the
+ // cursor in a position that would force us to scroll to put it in to
+ // view.)
+ for(i = 0; i < DISPLAY_MATRIX_X_SIZE; i++) {
+ for(j = ScrollYOffset;
+ j < DISPLAY_MATRIX_Y_SIZE && j < (ScrollYOffset+16); j++)
+ {
+ if(VALID_LEAF(DisplayMatrix[i][j])) {
+ SelectElement(i, j, SELECTED_LEFT);
+ return TRUE;
+ }
+ }
+ }
+
+ // If that didn't work, then try anywhere on the diagram before giving
+ // up entirely.
+ for(i = 0; i < DISPLAY_MATRIX_X_SIZE; i++) {
+ for(j = 0; j < 16; j++) {
+ if(VALID_LEAF(DisplayMatrix[i][j])) {
+ SelectElement(i, j, SELECTED_LEFT);
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Move the cursor in response to a keyboard command (arrow keys). Basically
+// we move the cursor within the currently selected element until we hit
+// the edge (e.g. left edge for VK_LEFT), and then we see if we can select
+// a new item that would let us keep going.
+//-----------------------------------------------------------------------------
+void MoveCursorKeyboard(int keyCode)
+{
+ if(!Selected || Selected->selectedState == SELECTED_NONE) {
+ MoveCursorTopLeft();
+ return;
+ }
+
+ switch(keyCode) {
+ case VK_LEFT: {
+ if(!Selected || Selected->selectedState == SELECTED_NONE) {
+ break;
+ }
+ if(Selected->selectedState != SELECTED_LEFT) {
+ SelectElement(-1, -1, SELECTED_LEFT);
+ break;
+ }
+ if(SelectedWhich == ELEM_COMMENT) break;
+ int i, j;
+ if(FindSelected(&i, &j)) {
+ i--;
+ while(i >= 0 && (!VALID_LEAF(DisplayMatrix[i][j]) ||
+ (DisplayMatrix[i][j] == Selected)))
+ {
+ i--;
+ }
+ if(i >= 0) {
+ SelectElement(i, j, SELECTED_RIGHT);
+ }
+ }
+ break;
+ }
+ case VK_RIGHT: {
+ if(!Selected || Selected->selectedState == SELECTED_NONE) {
+ break;
+ }
+ if(Selected->selectedState != SELECTED_RIGHT) {
+ SelectElement(-1, -1, SELECTED_RIGHT);
+ break;
+ }
+ if(SelectedWhich == ELEM_COMMENT) break;
+ int i, j;
+ if(FindSelected(&i, &j)) {
+ i++;
+ while(i < DISPLAY_MATRIX_X_SIZE &&
+ !VALID_LEAF(DisplayMatrix[i][j]))
+ {
+ i++;
+ }
+ if(i != DISPLAY_MATRIX_X_SIZE) {
+ SelectElement(i, j, SELECTED_LEFT);
+ }
+ }
+ break;
+ }
+ case VK_UP: {
+ if(!Selected || Selected->selectedState == SELECTED_NONE) {
+ break;
+ }
+ if(Selected->selectedState != SELECTED_ABOVE &&
+ SelectedWhich != ELEM_PLACEHOLDER)
+ {
+ SelectElement(-1, -1, SELECTED_ABOVE);
+ break;
+ }
+ int i, j;
+ if(FindSelected(&i, &j)) {
+ j--;
+ while(j >= 0 && !VALID_LEAF(DisplayMatrix[i][j]))
+ j--;
+ if(j >= 0) {
+ SelectElement(i, j, SELECTED_BELOW);
+ }
+ }
+ break;
+ }
+ case VK_DOWN: {
+ if(!Selected || Selected->selectedState == SELECTED_NONE) {
+ break;
+ }
+ if(Selected->selectedState != SELECTED_BELOW &&
+ SelectedWhich != ELEM_PLACEHOLDER)
+ {
+ SelectElement(-1, -1, SELECTED_BELOW);
+ break;
+ }
+ int i, j;
+ if(FindSelected(&i, &j)) {
+ j++;
+ while(j < DISPLAY_MATRIX_Y_SIZE &&
+ !VALID_LEAF(DisplayMatrix[i][j]))
+ {
+ j++;
+ }
+ if(j != DISPLAY_MATRIX_Y_SIZE) {
+ SelectElement(i, j, SELECTED_ABOVE);
+ } else if(ScrollYOffsetMax - ScrollYOffset < 3) {
+ // special case: scroll the end marker into view
+ ScrollYOffset = ScrollYOffsetMax;
+ RefreshScrollbars();
+ }
+ }
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Edit the selected element. Pop up the appropriate modal dialog box to do
+// this.
+//-----------------------------------------------------------------------------
+void EditSelectedElement(void)
+{
+ if(!Selected || Selected->selectedState == SELECTED_NONE) return;
+
+ switch(SelectedWhich) {
+ case ELEM_COMMENT:
+ ShowCommentDialog(Selected->d.comment.str);
+ break;
+
+ case ELEM_CONTACTS:
+ ShowContactsDialog(&(Selected->d.contacts.negated),
+ Selected->d.contacts.name);
+ break;
+
+ case ELEM_COIL:
+ ShowCoilDialog(&(Selected->d.coil.negated),
+ &(Selected->d.coil.setOnly), &(Selected->d.coil.resetOnly),
+ Selected->d.coil.name);
+ break;
+
+ case ELEM_TON:
+ case ELEM_TOF:
+ case ELEM_RTO:
+ ShowTimerDialog(SelectedWhich, &(Selected->d.timer.delay),
+ Selected->d.timer.name);
+ break;
+
+ case ELEM_CTU:
+ case ELEM_CTD:
+ case ELEM_CTC:
+ ShowCounterDialog(SelectedWhich, &(Selected->d.counter.max),
+ Selected->d.counter.name);
+ break;
+
+ case ELEM_EQU:
+ case ELEM_NEQ:
+ case ELEM_GRT:
+ case ELEM_GEQ:
+ case ELEM_LES:
+ case ELEM_LEQ:
+ ShowCmpDialog(SelectedWhich, Selected->d.cmp.op1,
+ Selected->d.cmp.op2);
+ break;
+
+ case ELEM_ADD:
+ case ELEM_SUB:
+ case ELEM_MUL:
+ case ELEM_DIV:
+ ShowMathDialog(SelectedWhich, Selected->d.math.dest,
+ Selected->d.math.op1, Selected->d.math.op2);
+ break;
+
+ case ELEM_RES:
+ ShowResetDialog(Selected->d.reset.name);
+ break;
+
+ case ELEM_MOVE:
+ ShowMoveDialog(Selected->d.move.dest, Selected->d.move.src);
+ break;
+
+ case ELEM_SET_PWM:
+ ShowSetPwmDialog(Selected->d.setPwm.name,
+ &(Selected->d.setPwm.targetFreq));
+ break;
+
+ case ELEM_READ_ADC:
+ ShowReadAdcDialog(Selected->d.readAdc.name+1);
+ break;
+
+ case ELEM_UART_RECV:
+ case ELEM_UART_SEND:
+ ShowUartDialog(SelectedWhich, Selected->d.uart.name);
+ break;
+
+ case ELEM_PERSIST:
+ ShowPersistDialog(Selected->d.persist.var);
+ break;
+
+ case ELEM_SHIFT_REGISTER:
+ ShowShiftRegisterDialog(Selected->d.shiftRegister.name,
+ &(Selected->d.shiftRegister.stages));
+ break;
+
+ case ELEM_FORMATTED_STRING:
+ ShowFormattedStringDialog(Selected->d.fmtdStr.var,
+ Selected->d.fmtdStr.string);
+ break;
+
+ case ELEM_PIECEWISE_LINEAR:
+ ShowPiecewiseLinearDialog(Selected);
+ break;
+
+ case ELEM_LOOK_UP_TABLE:
+ ShowLookUpTableDialog(Selected);
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Edit the element under the mouse cursor. This will typically be the
+// selected element, since the first click would have selected it. If there
+// is no element under the mouser cursor then do nothing; do not edit the
+// selected element (which is elsewhere on screen), as that would be
+// confusing.
+//-----------------------------------------------------------------------------
+void EditElementMouseDoubleclick(int x, int y)
+{
+ x += ScrollXOffset;
+
+ y += FONT_HEIGHT/2;
+
+ int gx = (x - X_PADDING)/(POS_WIDTH*FONT_WIDTH);
+ int gy = (y - Y_PADDING)/(POS_HEIGHT*FONT_HEIGHT);
+
+ gy += ScrollYOffset;
+
+ if(InSimulationMode) {
+ ElemLeaf *l = DisplayMatrix[gx][gy];
+ if(l && DisplayMatrixWhich[gx][gy] == ELEM_CONTACTS) {
+ char *name = l->d.contacts.name;
+ if(name[0] == 'X') {
+ SimulationToggleContact(name);
+ }
+ } else if(l && DisplayMatrixWhich[gx][gy] == ELEM_READ_ADC) {
+ ShowAnalogSliderPopup(l->d.readAdc.name);
+ }
+ } else {
+ if(DisplayMatrix[gx][gy] == Selected) {
+ EditSelectedElement();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Move the cursor in response to a mouse click. If they clicked in a leaf
+// element box then figure out which edge they're closest too (with appropriate
+// fudge factors to make all edges equally easy to click on) and put the
+// cursor there.
+//-----------------------------------------------------------------------------
+void MoveCursorMouseClick(int x, int y)
+{
+ x += ScrollXOffset;
+
+ y += FONT_HEIGHT/2;
+
+ int gx0 = (x - X_PADDING)/(POS_WIDTH*FONT_WIDTH);
+ int gy0 = (y - Y_PADDING)/(POS_HEIGHT*FONT_HEIGHT);
+
+ int gx = gx0;
+ int gy = gy0 + ScrollYOffset;
+
+ if(VALID_LEAF(DisplayMatrix[gx][gy])) {
+ int i, j;
+ for(i = 0; i < DISPLAY_MATRIX_X_SIZE; i++) {
+ for(j = 0; j < DISPLAY_MATRIX_Y_SIZE; j++) {
+ if(DisplayMatrix[i][j])
+ DisplayMatrix[i][j]->selectedState = SELECTED_NONE;
+ }
+ }
+ int dx = x - (gx0*POS_WIDTH*FONT_WIDTH + X_PADDING);
+ int dy = y - (gy0*POS_HEIGHT*FONT_HEIGHT + Y_PADDING);
+
+ int dtop = dy;
+ int dbottom = POS_HEIGHT*FONT_HEIGHT - dy;
+ int dleft = dx;
+ int dright = POS_WIDTH*FONT_WIDTH - dx;
+
+ int extra = 1;
+ if(DisplayMatrixWhich[gx][gy] == ELEM_COMMENT) {
+ dleft += gx*POS_WIDTH*FONT_WIDTH;
+ dright += (ColsAvailable - gx - 1)*POS_WIDTH*FONT_WIDTH;
+ extra = ColsAvailable;
+ } else {
+ if((gx > 0) && (DisplayMatrix[gx-1][gy] == DisplayMatrix[gx][gy])) {
+ dleft += POS_WIDTH*FONT_WIDTH;
+ extra = 2;
+ }
+ if((gx < (DISPLAY_MATRIX_X_SIZE-1)) &&
+ (DisplayMatrix[gx+1][gy] == DisplayMatrix[gx][gy]))
+ {
+ dright += POS_WIDTH*FONT_WIDTH;
+ extra = 2;
+ }
+ }
+
+ int decideX = (dright - dleft);
+ int decideY = (dtop - dbottom);
+
+ decideY = decideY*3*extra;
+
+ int state;
+ if(abs(decideY) > abs(decideX)) {
+ if(decideY > 0) {
+ state = SELECTED_BELOW;
+ } else {
+ state = SELECTED_ABOVE;
+ }
+ } else {
+ if(decideX > 0) {
+ state = SELECTED_LEFT;
+ } else {
+ state = SELECTED_RIGHT;
+ }
+ }
+ SelectElement(gx, gy, state);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Place the cursor as near to the given point on the grid as possible. Used
+// after deleting an element, for example.
+//-----------------------------------------------------------------------------
+void MoveCursorNear(int gx, int gy)
+{
+ int out = 0;
+
+ for(out = 0; out < 8; out++) {
+ if(gx - out >= 0) {
+ if(VALID_LEAF(DisplayMatrix[gx-out][gy])) {
+ SelectElement(gx-out, gy, SELECTED_RIGHT);
+ return;
+ }
+ }
+ if(gx + out < DISPLAY_MATRIX_X_SIZE) {
+ if(VALID_LEAF(DisplayMatrix[gx+out][gy])) {
+ SelectElement(gx+out, gy, SELECTED_LEFT);
+ return;
+ }
+ }
+ if(gy - out >= 0) {
+ if(VALID_LEAF(DisplayMatrix[gx][gy-out])) {
+ SelectElement(gx, gy-out, SELECTED_BELOW);
+ return;
+ }
+ }
+ if(gy + out < DISPLAY_MATRIX_Y_SIZE) {
+ if(VALID_LEAF(DisplayMatrix[gx][gy+out])) {
+ SelectElement(gx, gy+out, SELECTED_ABOVE);
+ return;
+ }
+ }
+
+ if(out == 1) {
+ // Now see if we have a straight shot to the right; might be far
+ // if we have to go up to a coil or other end of line element.
+ int across;
+ for(across = 1; gx+across < DISPLAY_MATRIX_X_SIZE; across++) {
+ if(VALID_LEAF(DisplayMatrix[gx+across][gy])) {
+ SelectElement(gx+across, gy, SELECTED_LEFT);
+ return;
+ }
+ if(!DisplayMatrix[gx+across][gy]) break;
+ }
+ }
+ }
+
+ MoveCursorTopLeft();
+}
+
+//-----------------------------------------------------------------------------
+// Negate the selected item, if this is meaningful.
+//-----------------------------------------------------------------------------
+void NegateSelected(void)
+{
+ switch(SelectedWhich) {
+ case ELEM_CONTACTS:
+ Selected->d.contacts.negated = TRUE;
+ break;
+
+ case ELEM_COIL: {
+ ElemCoil *c = &Selected->d.coil;
+ c->negated = TRUE;
+ c->resetOnly = FALSE;
+ c->setOnly = FALSE;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Make the item selected normal: not negated, not set/reset only.
+//-----------------------------------------------------------------------------
+void MakeNormalSelected(void)
+{
+ switch(SelectedWhich) {
+ case ELEM_CONTACTS:
+ Selected->d.contacts.negated = FALSE;
+ break;
+
+ case ELEM_COIL: {
+ ElemCoil *c = &Selected->d.coil;
+ c->negated = FALSE;
+ c->setOnly = FALSE;
+ c->resetOnly = FALSE;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Make the selected item set-only, if it is a coil.
+//-----------------------------------------------------------------------------
+void MakeSetOnlySelected(void)
+{
+ if(SelectedWhich != ELEM_COIL) return;
+
+ ElemCoil *c = &Selected->d.coil;
+ c->setOnly = TRUE;
+ c->resetOnly = FALSE;
+ c->negated = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Make the selected item reset-only, if it is a coil.
+//-----------------------------------------------------------------------------
+void MakeResetOnlySelected(void)
+{
+ if(SelectedWhich != ELEM_COIL) return;
+
+ ElemCoil *c = &Selected->d.coil;
+ c->resetOnly = TRUE;
+ c->setOnly = FALSE;
+ c->negated = FALSE;
+}
diff --git a/ldmicro/simpledialog.cpp b/ldmicro/simpledialog.cpp
new file mode 100644
index 0000000..e8f4ccb
--- /dev/null
+++ b/ldmicro/simpledialog.cpp
@@ -0,0 +1,422 @@
+//-----------------------------------------------------------------------------
+// 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 .
+//------
+//
+// All the simple dialogs that just require a row of text boxes: timer name
+// and delay, counter name and count, move and arithmetic destination and
+// operands. Try to reuse code a bit.
+// Jonathan Westhues, Nov 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+static HWND SimpleDialog;
+
+#define MAX_BOXES 5
+
+static HWND Textboxes[MAX_BOXES];
+static HWND Labels[MAX_BOXES];
+
+static LONG_PTR PrevAlnumOnlyProc[MAX_BOXES];
+static LONG_PTR PrevNumOnlyProc[MAX_BOXES];
+
+static BOOL NoCheckingOnBox[MAX_BOXES];
+
+//-----------------------------------------------------------------------------
+// Don't allow any characters other than -A-Za-z0-9_ in the box.
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK MyAlnumOnlyProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam)
+{
+ if(msg == WM_CHAR) {
+ if(!(isalpha(wParam) || isdigit(wParam) || wParam == '_' ||
+ wParam == '\b' || wParam == '-' || wParam == '\''))
+ {
+ return 0;
+ }
+ }
+
+ int i;
+ for(i = 0; i < MAX_BOXES; i++) {
+ if(hwnd == Textboxes[i]) {
+ return CallWindowProc((WNDPROC)PrevAlnumOnlyProc[i], hwnd, msg,
+ wParam, lParam);
+ }
+ }
+ oops();
+}
+
+//-----------------------------------------------------------------------------
+// Don't allow any characters other than -0-9. in the box.
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK MyNumOnlyProc(HWND hwnd, UINT msg, WPARAM wParam,
+ LPARAM lParam)
+{
+ if(msg == WM_CHAR) {
+ if(!(isdigit(wParam) || wParam == '.' || wParam == '\b'
+ || wParam == '-'))
+ {
+ return 0;
+ }
+ }
+
+ int i;
+ for(i = 0; i < MAX_BOXES; i++) {
+ if(hwnd == Textboxes[i]) {
+ return CallWindowProc((WNDPROC)PrevNumOnlyProc[i], hwnd, msg,
+ wParam, lParam);
+ }
+ }
+ oops();
+}
+
+static void MakeControls(int boxes, char **labels, DWORD fixedFontMask)
+{
+ int i;
+ HDC hdc = GetDC(SimpleDialog);
+ SelectObject(hdc, MyNiceFont);
+
+ SIZE si;
+
+ int maxLen = 0;
+ for(i = 0; i < boxes; i++) {
+ GetTextExtentPoint32(hdc, labels[i], strlen(labels[i]), &si);
+ if(si.cx > maxLen) maxLen = si.cx;
+ }
+
+ int adj;
+ if(maxLen > 70) {
+ adj = maxLen - 70;
+ } else {
+ adj = 0;
+ }
+
+ for(i = 0; i < boxes; i++) {
+ GetTextExtentPoint32(hdc, labels[i], strlen(labels[i]), &si);
+
+ Labels[i] = CreateWindowEx(0, WC_STATIC, labels[i],
+ WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
+ (80 + adj) - si.cx - 4, 13 + i*30, si.cx, 21,
+ SimpleDialog, NULL, Instance, NULL);
+ NiceFont(Labels[i]);
+
+ Textboxes[i] = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "",
+ WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS |
+ WS_VISIBLE,
+ 80 + adj, 12 + 30*i, 120 - adj, 21,
+ SimpleDialog, NULL, Instance, NULL);
+
+ if(fixedFontMask & (1 << i)) {
+ FixedFont(Textboxes[i]);
+ } else {
+ NiceFont(Textboxes[i]);
+ }
+ }
+ ReleaseDC(SimpleDialog, hdc);
+
+ OkButton = CreateWindowEx(0, WC_BUTTON, _("OK"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_DEFPUSHBUTTON,
+ 218, 11, 70, 23, SimpleDialog, NULL, Instance, NULL);
+ NiceFont(OkButton);
+
+ CancelButton = CreateWindowEx(0, WC_BUTTON, _("Cancel"),
+ WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 218, 41, 70, 23, SimpleDialog, NULL, Instance, NULL);
+ NiceFont(CancelButton);
+}
+
+BOOL ShowSimpleDialog(char *title, int boxes, char **labels, DWORD numOnlyMask,
+ DWORD alnumOnlyMask, DWORD fixedFontMask, char **dests)
+{
+ BOOL didCancel;
+
+ if(boxes > MAX_BOXES) oops();
+
+ SimpleDialog = CreateWindowClient(0, "LDmicroDialog", title,
+ WS_OVERLAPPED | WS_SYSMENU,
+ 100, 100, 304, 15 + 30*(boxes < 2 ? 2 : boxes), NULL, NULL,
+ Instance, NULL);
+
+ MakeControls(boxes, labels, fixedFontMask);
+
+ int i;
+ for(i = 0; i < boxes; i++) {
+ SendMessage(Textboxes[i], WM_SETTEXT, 0, (LPARAM)dests[i]);
+
+ if(numOnlyMask & (1 << i)) {
+ PrevNumOnlyProc[i] = SetWindowLongPtr(Textboxes[i], GWLP_WNDPROC,
+ (LONG_PTR)MyNumOnlyProc);
+ }
+ if(alnumOnlyMask & (1 << i)) {
+ PrevAlnumOnlyProc[i] = SetWindowLongPtr(Textboxes[i], GWLP_WNDPROC,
+ (LONG_PTR)MyAlnumOnlyProc);
+ }
+ }
+
+ EnableWindow(MainWindow, FALSE);
+ ShowWindow(SimpleDialog, TRUE);
+ SetFocus(Textboxes[0]);
+ SendMessage(Textboxes[0], EM_SETSEL, 0, -1);
+
+ MSG msg;
+ DWORD ret;
+ DialogDone = FALSE;
+ DialogCancel = FALSE;
+ while((ret = GetMessage(&msg, NULL, 0, 0)) && !DialogDone) {
+ if(msg.message == WM_KEYDOWN) {
+ if(msg.wParam == VK_RETURN) {
+ DialogDone = TRUE;
+ break;
+ } else if(msg.wParam == VK_ESCAPE) {
+ DialogDone = TRUE;
+ DialogCancel = TRUE;
+ break;
+ }
+ }
+
+ if(IsDialogMessage(SimpleDialog, &msg)) continue;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ didCancel = DialogCancel;
+
+ if(!didCancel) {
+ for(i = 0; i < boxes; i++) {
+ if(NoCheckingOnBox[i]) {
+ char get[64];
+ SendMessage(Textboxes[i], WM_GETTEXT, 60, (LPARAM)get);
+ strcpy(dests[i], get);
+ } else {
+ char get[20];
+ SendMessage(Textboxes[i], WM_GETTEXT, 15, (LPARAM)get);
+
+ if( (!strchr(get, '\'')) ||
+ (get[0] == '\'' && get[2] == '\'' && strlen(get)==3) )
+ {
+ if(strlen(get) == 0) {
+ Error(_("Empty textbox; not permitted."));
+ } else {
+ strcpy(dests[i], get);
+ }
+ } else {
+ Error(_("Bad use of quotes: <%s>"), get);
+ }
+ }
+ }
+ }
+
+ EnableWindow(MainWindow, TRUE);
+ DestroyWindow(SimpleDialog);
+
+ return !didCancel;
+}
+
+void ShowTimerDialog(int which, int *delay, char *name)
+{
+ char *s;
+ switch(which) {
+ case ELEM_TON: s = _("Turn-On Delay"); break;
+ case ELEM_TOF: s = _("Turn-Off Delay"); break;
+ case ELEM_RTO: s = _("Retentive Turn-On Delay"); break;
+ default: oops(); break;
+ }
+
+ char *labels[] = { _("Name:"), _("Delay (ms):") };
+
+ char delBuf[16];
+ char nameBuf[16];
+ sprintf(delBuf, "%.3f", (*delay / 1000.0));
+ strcpy(nameBuf, name+1);
+ char *dests[] = { nameBuf, delBuf };
+
+ if(ShowSimpleDialog(s, 2, labels, (1 << 1), (1 << 0), (1 << 0), dests)) {
+ name[0] = 'T';
+ strcpy(name+1, nameBuf);
+ double del = atof(delBuf);
+ if(del > 2140000) { // 2**31/1000, don't overflow signed int
+ Error(_("Delay too long; maximum is 2**31 us."));
+ } else if(del <= 0) {
+ Error(_("Delay cannot be zero or negative."));
+ } else {
+ *delay = (int)(1000*del + 0.5);
+ }
+ }
+}
+
+void ShowCounterDialog(int which, int *maxV, char *name)
+{
+ char *title;
+
+ switch(which) {
+ case ELEM_CTU: title = _("Count Up"); break;
+ case ELEM_CTD: title = _("Count Down"); break;
+ case ELEM_CTC: title = _("Circular Counter"); break;
+
+ default: oops();
+ }
+
+ char *labels[] = { _("Name:"), (which == ELEM_CTC ? _("Max value:") :
+ _("True if >= :")) };
+ char maxS[128];
+ sprintf(maxS, "%d", *maxV);
+ char *dests[] = { name+1, maxS };
+ ShowSimpleDialog(title, 2, labels, 0x2, 0x1, 0x1, dests);
+ *maxV = atoi(maxS);
+}
+
+void ShowCmpDialog(int which, char *op1, char *op2)
+{
+ char *title;
+ char *l2;
+ switch(which) {
+ case ELEM_EQU:
+ title = _("If Equals");
+ l2 = "= :";
+ break;
+
+ case ELEM_NEQ:
+ title = _("If Not Equals");
+ l2 = "/= :";
+ break;
+
+ case ELEM_GRT:
+ title = _("If Greater Than");
+ l2 = "> :";
+ break;
+
+ case ELEM_GEQ:
+ title = _("If Greater Than or Equal To");
+ l2 = ">= :";
+ break;
+
+ case ELEM_LES:
+ title = _("If Less Than");
+ l2 = "< :";
+ break;
+
+ case ELEM_LEQ:
+ title = _("If Less Than or Equal To");
+ l2 = "<= :";
+ break;
+
+ default:
+ oops();
+ }
+ char *labels[] = { _("'Closed' if:"), l2 };
+ char *dests[] = { op1, op2 };
+ ShowSimpleDialog(title, 2, labels, 0, 0x3, 0x3, dests);
+}
+
+void ShowMoveDialog(char *dest, char *src)
+{
+ char *labels[] = { _("Destination:"), _("Source:") };
+ char *dests[] = { dest, src };
+ ShowSimpleDialog(_("Move"), 2, labels, 0, 0x3, 0x3, dests);
+}
+
+void ShowReadAdcDialog(char *name)
+{
+ char *labels[] = { _("Destination:") };
+ char *dests[] = { name };
+ ShowSimpleDialog(_("Read A/D Converter"), 1, labels, 0, 0x1, 0x1, dests);
+}
+
+void ShowSetPwmDialog(char *name, int *targetFreq)
+{
+ char freq[100];
+ sprintf(freq, "%d", *targetFreq);
+
+ char *labels[] = { _("Duty cycle var:"), _("Frequency (Hz):") };
+ char *dests[] = { name, freq };
+ ShowSimpleDialog(_("Set PWM Duty Cycle"), 2, labels, 0x2, 0x1, 0x1, dests);
+
+ *targetFreq = atoi(freq);
+}
+
+void ShowUartDialog(int which, char *name)
+{
+ char *labels[] = { (which == ELEM_UART_RECV) ? _("Destination:") :
+ _("Source:") };
+ char *dests[] = { name };
+
+ ShowSimpleDialog((which == ELEM_UART_RECV) ? _("Receive from UART") :
+ _("Send to UART"), 1, labels, 0, 0x1, 0x1, dests);
+}
+
+void ShowMathDialog(int which, char *dest, char *op1, char *op2)
+{
+ char *l2, *title;
+ if(which == ELEM_ADD) {
+ l2 = "+ :";
+ title = _("Add");
+ } else if(which == ELEM_SUB) {
+ l2 = "- :";
+ title = _("Subtract");
+ } else if(which == ELEM_MUL) {
+ l2 = "* :";
+ title = _("Multiply");
+ } else if(which == ELEM_DIV) {
+ l2 = "/ :";
+ title = _("Divide");
+ } else oops();
+
+ char *labels[] = { _("Destination:"), _("is set := :"), l2 };
+ char *dests[] = { dest, op1, op2 };
+ ShowSimpleDialog(title, 3, labels, 0, 0x7, 0x7, dests);
+}
+
+void ShowShiftRegisterDialog(char *name, int *stages)
+{
+ char stagesStr[20];
+ sprintf(stagesStr, "%d", *stages);
+
+ char *labels[] = { _("Name:"), _("Stages:") };
+ char *dests[] = { name, stagesStr };
+ ShowSimpleDialog(_("Shift Register"), 2, labels, 0x2, 0x1, 0x1, dests);
+
+ *stages = atoi(stagesStr);
+
+ if(*stages <= 0 || *stages >= 200) {
+ Error(_("Not a reasonable size for a shift register."));
+ *stages = 1;
+ }
+}
+
+void ShowFormattedStringDialog(char *var, char *string)
+{
+ char *labels[] = { _("Variable:"), _("String:") };
+ char *dests[] = { var, string };
+ NoCheckingOnBox[0] = TRUE;
+ NoCheckingOnBox[1] = TRUE;
+ ShowSimpleDialog(_("Formatted String Over UART"), 2, labels, 0x0,
+ 0x1, 0x3, dests);
+ NoCheckingOnBox[0] = FALSE;
+ NoCheckingOnBox[1] = FALSE;
+}
+
+void ShowPersistDialog(char *var)
+{
+ char *labels[] = { _("Variable:") };
+ char *dests[] = { var };
+ ShowSimpleDialog(_("Make Persistent"), 1, labels, 0, 1, 1, dests);
+}
diff --git a/ldmicro/simulate.cpp b/ldmicro/simulate.cpp
new file mode 100644
index 0000000..7a4f2a8
--- /dev/null
+++ b/ldmicro/simulate.cpp
@@ -0,0 +1,972 @@
+//-----------------------------------------------------------------------------
+// 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 to simulate the logic interactively, for testing purposes. We can
+// simulate in real time, triggering off a Windows timer, or we can
+// single-cycle it. The GUI acts differently in simulation mode, to show the
+// status of all the signals graphically, show how much time is left on the
+// timers, etc.
+// Jonathan Westhues, Nov 2004
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+#include
+#include
+
+#include "ldmicro.h"
+#include "intcode.h"
+#include "freeze.h"
+
+static struct {
+ char name[MAX_NAME_LEN];
+ BOOL powered;
+} SingleBitItems[MAX_IO];
+static int SingleBitItemsCount;
+
+static struct {
+ char name[MAX_NAME_LEN];
+ SWORD val;
+ DWORD usedFlags;
+} Variables[MAX_IO];
+static int VariablesCount;
+
+static struct {
+ char name[MAX_NAME_LEN];
+ SWORD val;
+} AdcShadows[MAX_IO];
+static int AdcShadowsCount;
+
+#define VAR_FLAG_TON 0x00000001
+#define VAR_FLAG_TOF 0x00000002
+#define VAR_FLAG_RTO 0x00000004
+#define VAR_FLAG_CTU 0x00000008
+#define VAR_FLAG_CTD 0x00000010
+#define VAR_FLAG_CTC 0x00000020
+#define VAR_FLAG_RES 0x00000040
+#define VAR_FLAG_ANY 0x00000080
+
+#define VAR_FLAG_OTHERWISE_FORGOTTEN 0x80000000
+
+
+// Schematic-drawing code needs to know whether we're in simulation mode or
+// note, as that changes how everything is drawn; also UI code, to disable
+// editing during simulation.
+BOOL InSimulationMode;
+
+// Don't want to redraw the screen unless necessary; track whether a coil
+// changed state or a timer output switched to see if anything could have
+// changed (not just coil, as we show the intermediate steps too).
+static BOOL NeedRedraw;
+// Have to let the effects of a coil change in cycle k appear in cycle k+1,
+// or set by the UI code to indicate that user manually changed an Xfoo
+// input.
+BOOL SimulateRedrawAfterNextCycle;
+
+// Don't want to set a timer every 100 us to simulate a 100 us cycle
+// time...but we can cycle multiple times per timer interrupt and it will
+// be almost as good, as long as everything runs fast.
+static int CyclesPerTimerTick;
+
+// Program counter as we evaluate the intermediate code.
+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 LONG_PTR PrevTextProc;
+
+static int QueuedUartCharacter = -1;
+static int SimulateUartTxCountdown = 0;
+
+static void AppendToUartSimulationTextControl(BYTE b);
+
+static void SimulateIntCode(void);
+static char *MarkUsedVariable(char *name, DWORD flag);
+
+//-----------------------------------------------------------------------------
+// Query the state of a single-bit element (relay, digital in, digital out).
+// 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;
+}
+
+//-----------------------------------------------------------------------------
+// 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++;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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();
+}
+
+//-----------------------------------------------------------------------------
+// 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);
+}
+
+//-----------------------------------------------------------------------------
+// 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);
+}
+
+//-----------------------------------------------------------------------------
+// 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++;
+}
+
+//-----------------------------------------------------------------------------
+// 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;
+}
+
+//-----------------------------------------------------------------------------
+// Mark how a variable is used; a series of flags that we can OR together,
+// then we can check to make sure that only valid combinations have been used
+// (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;
+
+ 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
+// not be two TONs with the same name. On the other hand, it would be okay
+// 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;
+ }
+
+ 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();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Evaluate a circuit, calling ourselves recursively to evaluate if/else
+// constructs. Updates the on/off state of all the leaf elements in our
+// 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;
+
+ 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);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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;
+
+ if(Simulating) return;
+ Simulating = TRUE;
+
+ NeedRedraw = FALSE;
+
+ if(SimulateUartTxCountdown > 0) {
+ SimulateUartTxCountdown--;
+ } else {
+ SimulateUartTxCountdown = 0;
+ }
+
+ IntPc = 0;
+ SimulateIntCode();
+
+ if(NeedRedraw || SimulateRedrawAfterNextCycle || forceRefresh) {
+ InvalidateRect(MainWindow, NULL, FALSE);
+ ListView_RedrawItems(IoList, 0, Prog.io.count - 1);
+ }
+
+ SimulateRedrawAfterNextCycle = FALSE;
+ if(NeedRedraw) SimulateRedrawAfterNextCycle = TRUE;
+
+ Simulating = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Start the timer that we use to trigger PLC cycles in approximately real
+// time. Independently of the given cycle time, just go at 40 Hz, since that
+// 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;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Clear out all the parameters relating to the previous simulation.
+//-----------------------------------------------------------------------------
+void ClearSimulationData(void)
+{
+ VariablesCount = 0;
+ SingleBitItemsCount = 0;
+ AdcShadowsCount = 0;
+ QueuedUartCharacter = -1;
+ SimulateUartTxCountdown = 0;
+
+ CheckVariableNames();
+
+ SimulateRedrawAfterNextCycle = TRUE;
+
+ if(!GenerateIntermediateCode()) {
+ ToggleSimulationMode();
+ return;
+ }
+
+ 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;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// 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);
+}
+
+//-----------------------------------------------------------------------------
+// Dialog proc for the popup that lets you interact with the UART stuff.
+//-----------------------------------------------------------------------------
+static LRESULT CALLBACK UartSimulationProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_DESTROY:
+ DestroyUartSimulationWindow();
+ break;
+
+ case WM_CLOSE:
+ break;
+
+ case WM_SIZE:
+ MoveWindow(UartSimulationTextControl, 0, 0, LOWORD(lParam),
+ HIWORD(lParam), TRUE);
+ break;
+
+ case WM_ACTIVATE:
+ if(wParam != WA_INACTIVE) {
+ SetFocus(UartSimulationTextControl);
+ }
+ break;
+
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// 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);
+}
+
+//-----------------------------------------------------------------------------
+// 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);
+}
+
+//-----------------------------------------------------------------------------
+// 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;
+
+ DWORD TerminalX, TerminalY, TerminalW, TerminalH;
+ RECT r;
+
+ GetClientRect(UartSimulationWindow, &r);
+ TerminalW = r.right - r.left;
+ TerminalH = r.bottom - r.top;
+
+ GetWindowRect(UartSimulationWindow, &r);
+ TerminalX = r.left;
+ TerminalY = r.top;
+
+ FreezeDWORD(TerminalX);
+ FreezeDWORD(TerminalY);
+ FreezeDWORD(TerminalW);
+ FreezeDWORD(TerminalH);
+
+ 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);
+ }
+
+#define MAX_SCROLLBACK 256
+ char buf[MAX_SCROLLBACK];
+
+ SendMessage(UartSimulationTextControl, WM_GETTEXT, (WPARAM)sizeof(buf),
+ (LPARAM)buf);
+
+ int overBy = (strlen(buf) + strlen(append) + 1) - sizeof(buf);
+ if(overBy > 0) {
+ memmove(buf, buf + overBy, strlen(buf));
+ }
+ strcat(buf, append);
+
+ SendMessage(UartSimulationTextControl, WM_SETTEXT, 0, (LPARAM)buf);
+ SendMessage(UartSimulationTextControl, EM_LINESCROLL, 0, (LPARAM)INT_MAX);
+}
diff --git a/ldmicro/txt2c.pl b/ldmicro/txt2c.pl
new file mode 100644
index 0000000..4a8ea3b
--- /dev/null
+++ b/ldmicro/txt2c.pl
@@ -0,0 +1,45 @@
+#!/usr/bin/perl
+
+print <
+EOT
+
+for $manual () {
+
+ if($manual eq 'manual.txt') {
+ $name = "HelpText";
+ # Some languages don't have translated manuals yet, so use English
+ $ifdef = "#if defined(LDLANG_EN) || defined(LDLANG_ES) || defined(LDLANG_IT) || " .
+ "defined(LDLANG_PT)";
+ } elsif($manual =~ /manual-(.)(.)\.txt/) {
+ $p = uc($1) . lc($2);
+ $ifdef = "#ifdef LDLANG_" . uc($1 . $2);
+ $name = "HelpText$p";
+ } else {
+ die;
+ }
+
+ print <) {
+ chomp;
+ s/\\/\\\\/g;
+ s/"/\\"/g;
+
+ print qq{ "$_",\n};
+ }
+ close IN;
+
+ print <.
+//------
+//
+// Routines to maintain the stack of recent versions of the program that we
+// use for the undo/redo feature. Don't be smart at all; keep deep copies of
+// the entire program at all times.
+// Jonathan Westhues, split May 2005
+//-----------------------------------------------------------------------------
+#include
+#include
+#include
+
+#include "ldmicro.h"
+
+// Store a `deep copy' of the entire program before every change, in a
+// circular buffer so that the first one scrolls out as soon as the buffer
+// is full and we try to push another one.
+#define MAX_LEVELS_UNDO 32
+typedef struct ProgramStackTag {
+ PlcProgram prog[MAX_LEVELS_UNDO];
+ struct {
+ int gx;
+ int gy;
+ } cursor[MAX_LEVELS_UNDO];
+ int write;
+ int count;
+} ProgramStack;
+
+static struct {
+ ProgramStack undo;
+ ProgramStack redo;
+} Undo;
+
+//-----------------------------------------------------------------------------
+// Make a `deep copy' of a circuit. Used for making a copy of the program
+// whenever we change it, for undo purposes. Fast enough that we shouldn't
+// need to be smart.
+//-----------------------------------------------------------------------------
+static void *DeepCopy(int which, void *any)
+{
+ switch(which) {
+ CASE_LEAF {
+ ElemLeaf *l = AllocLeaf();
+ memcpy(l, any, sizeof(*l));
+ l->selectedState = SELECTED_NONE;
+ return l;
+ }
+ case ELEM_SERIES_SUBCKT: {
+ int i;
+ ElemSubcktSeries *n = AllocSubcktSeries();
+ ElemSubcktSeries *s = (ElemSubcktSeries *)any;
+ n->count = s->count;
+ for(i = 0; i < s->count; i++) {
+ n->contents[i].which = s->contents[i].which;
+ n->contents[i].d.any = DeepCopy(s->contents[i].which,
+ s->contents[i].d.any);
+ }
+ return n;
+ }
+ case ELEM_PARALLEL_SUBCKT: {
+ int i;
+ ElemSubcktParallel *n = AllocSubcktParallel();
+ ElemSubcktParallel *p = (ElemSubcktParallel *)any;
+ n->count = p->count;
+ for(i = 0; i < p->count; i++) {
+ n->contents[i].which = p->contents[i].which;
+ n->contents[i].d.any = DeepCopy(p->contents[i].which,
+ p->contents[i].d.any);
+ }
+ return n;
+ }
+ default:
+ oops();
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Empty out a ProgramStack data structure, either .undo or .redo: set the
+// count to zero and free all the program copies in it.
+//-----------------------------------------------------------------------------
+static void EmptyProgramStack(ProgramStack *ps)
+{
+ while(ps->count > 0) {
+ int a = (ps->write - 1);
+ if(a < 0) a += MAX_LEVELS_UNDO;
+ ps->write = a;
+ (ps->count)--;
+
+ int i;
+ for(i = 0; i < ps->prog[ps->write].numRungs; i++) {
+ FreeCircuit(ELEM_SERIES_SUBCKT, ps->prog[ps->write].rungs[i]);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Push the current program onto a program stack. Can either make a deep or
+// a shallow copy of the linked data structures.
+//-----------------------------------------------------------------------------
+static void PushProgramStack(ProgramStack *ps, BOOL deepCopy)
+{
+ if(ps->count == MAX_LEVELS_UNDO) {
+ int i;
+ for(i = 0; i < ps->prog[ps->write].numRungs; i++) {
+ FreeCircuit(ELEM_SERIES_SUBCKT,
+ ps->prog[ps->write].rungs[i]);
+ }
+ } else {
+ (ps->count)++;
+ }
+
+ memcpy(&(ps->prog[ps->write]), &Prog, sizeof(Prog));
+ if(deepCopy) {
+ int i;
+ for(i = 0; i < Prog.numRungs; i++) {
+ ps->prog[ps->write].rungs[i] =
+ (ElemSubcktSeries *)DeepCopy(ELEM_SERIES_SUBCKT, Prog.rungs[i]);
+ }
+ }
+
+ int gx, gy;
+ if(FindSelected(&gx, &gy)) {
+ ps->cursor[ps->write].gx = gx;
+ ps->cursor[ps->write].gy = gy;
+ } else {
+ ps->cursor[ps->write].gx = -1;
+ ps->cursor[ps->write].gy = -1;
+ }
+
+ int a = (ps->write + 1);
+ if(a >= MAX_LEVELS_UNDO) a -= MAX_LEVELS_UNDO;
+ ps->write = a;
+}
+
+//-----------------------------------------------------------------------------
+// Pop a program stack onto the current program. Always does a shallow copy.
+// Internal error if the stack was empty.
+//-----------------------------------------------------------------------------
+static void PopProgramStack(ProgramStack *ps)
+{
+ int a = (ps->write - 1);
+ if(a < 0) a += MAX_LEVELS_UNDO;
+ ps->write = a;
+ (ps->count)--;
+
+ memcpy(&Prog, &ps->prog[ps->write], sizeof(Prog));
+
+ SelectedGxAfterNextPaint = ps->cursor[ps->write].gx;
+ SelectedGyAfterNextPaint = ps->cursor[ps->write].gy;
+}
+
+//-----------------------------------------------------------------------------
+// Push a copy of the PLC program onto the undo history, replacing (and
+// freeing) the oldest one if necessary.
+//-----------------------------------------------------------------------------
+void UndoRemember(void)
+{
+ // can't redo after modifying the program
+ EmptyProgramStack(&(Undo.redo));
+ PushProgramStack(&(Undo.undo), TRUE);
+
+ SetUndoEnabled(TRUE, FALSE);
+}
+
+//-----------------------------------------------------------------------------
+// Pop the undo history one level, or do nothing if we're out of levels of
+// undo. This means that we push the current program on the redo stack, and
+// pop the undo stack onto the current program.
+//-----------------------------------------------------------------------------
+void UndoUndo(void)
+{
+ if(Undo.undo.count <= 0) return;
+
+ ForgetEverything();
+
+ PushProgramStack(&(Undo.redo), FALSE);
+ PopProgramStack(&(Undo.undo));
+
+ if(Undo.undo.count > 0) {
+ SetUndoEnabled(TRUE, TRUE);
+ } else {
+ SetUndoEnabled(FALSE, TRUE);
+ }
+ RefreshControlsToSettings();
+ RefreshScrollbars();
+ InvalidateRect(MainWindow, NULL, FALSE);
+}
+
+//-----------------------------------------------------------------------------
+// Redo an undone operation. Push the current program onto the undo stack,
+// and pop the redo stack into the current program.
+//-----------------------------------------------------------------------------
+void UndoRedo(void)
+{
+ if(Undo.redo.count <= 0) return;
+
+ ForgetEverything();
+
+ PushProgramStack(&(Undo.undo), FALSE);
+ PopProgramStack(&(Undo.redo));
+
+ if(Undo.redo.count > 0) {
+ SetUndoEnabled(TRUE, TRUE);
+ } else {
+ SetUndoEnabled(TRUE, FALSE);
+ }
+ RefreshControlsToSettings();
+ RefreshScrollbars();
+ InvalidateRect(MainWindow, NULL, FALSE);
+}
+
+//-----------------------------------------------------------------------------
+// Empty out our undo history entirely, as when loading a new file.
+//-----------------------------------------------------------------------------
+void UndoFlush(void)
+{
+ EmptyProgramStack(&(Undo.undo));
+ EmptyProgramStack(&(Undo.redo));
+ SetUndoEnabled(FALSE, FALSE);
+}
+
+//-----------------------------------------------------------------------------
+// Is it possible to undo some operation? The display code needs to do that,
+// due to an ugly hack for handling too-long lines; the only thing that
+// notices that easily is the display code, which will respond by undoing
+// the last operation, presumably the one that added the long line.
+//-----------------------------------------------------------------------------
+BOOL CanUndo(void)
+{
+ return (Undo.undo.count > 0);
+}
+
diff --git a/ldmicro/vc100.pdb b/ldmicro/vc100.pdb
new file mode 100644
index 0000000..99c8e3f
Binary files /dev/null and b/ldmicro/vc100.pdb differ
--
cgit