TMS9900 family based Microcomputers still are used for control purposes. Assembler macros providing functions and function blocks akin to instruction list (IL) according to IEC 1131-3 improve the readability of assembler programs to anyone experienced with programmable logic controllers (PLC). This documentation should help writing reliable controller programs using the macro library provided, which contains elementary Boolean functions and timer function blocks. The software is copyleft under the terms of the GNU General Public License.
When occasionally programming controllers based on the dated TMS9995 microprocessor, I started writing macros in order to avoid reinventing program structures time and again. Macros considerably abbreviate the debugging process. Hopefully too, the programs are read more easily by maintenance programmers. The macro library might be useful to others now.
The macros are written for the undocumented Texas Instruments "99000 Family Macro Assembler PC2.1 84.107", a quite powerful MS-DOS cross assembler, which is run on Linux with the help of DOSEMU or on NT4/XP. The assembled and linked programs run on a TMS9995 single board microcomputer under multitasking operating system RTMT4.0, both developed at DESY.
Design objectives are readability and reliability of the programs. Only the set of timer macros, which constitute a great deal of any control code, are geared towards execution speed and efficient memory usage. Here a combination of macro and subroutine technique is applied. Macros always are less efficient on hardware resources than pure assembler code. Particularly so, if no other entry conditions than parameters are used and status registers are set according to the result of the operation, as one would expect with any ordinary op-code.
There are quite some limitations as to what degree the macros can
conform to the IEC 1131-3 standard (EN 61131-3). Basically, they don't
conform at all. Still, I think, they will work the way, you would
intuitively expect them to work. Furthermore, in order not to mask
assembler op-codes, standard IL operators have been changed (eg. S
is SET here). The odd IL-rule, that the first
parameter of any function or function block is not named on the parameter
list but passed silently through the RLO register (current result of
logical operation) is adhered to. The standard is extended by allowing
more than one parameter to a function. Finally, a sort of indirection
operator is introduced.
Included with this text is the controller macro library and the source of the timer subroutines. The latter
will have to be adapted to your operating system. The classic PLC textbook
traffic lights example
demonstrates the application of the macros. You may be interested in the
structured programming macro library
also.
Users of the excellent vi improved colour highlighting editor
vim may take advantage of the syntax files
for assembler source and
listings.
Readers are supposed to be familiar with the TMS9900 Assembler and its addressing modes in particular. A fine introduction gives the assembly language primer available at Stanford University. Also some knowledge of PLC-programming using instruction list (IL) and function block diagram (FBD) should help. A good address for introductory material on the IEC 1131-3 standard is the tutorial site of PLC-Open.
Please direct any comment and ideas to the author via email to erland.mueller at desy.de. The most recent version of this page can be found at http://adweb.desy.de/~erlandm/tms/controlmacro.html.
[Top] The functions and function blocks operate on data of Boolean type,
which is defined here as an 1 byte (8 bit) long value, 0
representing "false", 0xff representing "true". If
workspace register addressing is used, the value in the MSByte is operated
upon.
Parameters to functions can be of any TMS9900 family addressing modes but immediate (there are no constants "true" and "false", the corresponding functions are provided instead). Following the TMS manual notation, these general addressing modes are denoted Gs and Gd for source and destination addresses respectively.
In addition a general indirect addressing mode is created by
introduction of the indirection operator "*,", which is put
in front of the operand to be dereferenced. This operand can be of any
addressing mode but immediate again. As an example consider the statement
AND *,@PSTOP . This statement causes the Boolean value at
the address stored at the symbolic address PSTOP to be retrieved and
logically anded to the value of the RLO register. The shorthands eGs and
eGd denote the general addressing modes including this indirect addressing
mode.
Since I didn't find a way of implementing symbolic or direct addressing for timer function blocks, all function blocks are addressed either by immediate addressing or general indirect addressing, denoted by eAddr below.
[Top]Boolean functions store the Result of the Logical Operation in the RLO register, which is named in accordance with Siemens PLC jargon RVKE (VKE is the German equivalent to RLO and stands for Verknüpfungsergebnis). On exit the status register bits are set by comparing the result to zero. Therefore, the EQ-bit is set if the result is "false", reset otherwise. The LGT-bit is set if the result is "true", reset otherwise. Other status register bits are meaningless or not affected.
If there are one or more parameters to a function, the first
parameter is passed to the function via the register RVKE. The
LD and NOT operations are exceptions to this rule
however.
+---+
eGs--| 1 |--RVKE LD eGs
+---+
The Load operation is used to retrieve a Boolean value from memory and transfer it into the RLO register for further operation.
+---+
RVKE--| 1 |--eGd ST eGd
+---+
Store is the inverse operation of load, transferring the value from the RLO register back to memory or another workspace register.
+----+
|TRUE|--RVKE TRUE
+----+
True assigns the value "true" to the RLO register. The function False works correspondingly.
+---+
RVKE|eGs--| 1 |O--RVKE NOT [eGs]
+---+
Not without an operand logically inverts the
content of the RLO register. If an operand is given however, the value
addressed by the operand is retrieved and logically inverted. Not
with operand replaces the IEC LDN op-code.
Note: You can not have a comment on a line of a NOT-statement
without operand.
+-----+
RVKE--| >=1 |
eGs--| |
: | |--RVKE OR eGs[,eGs[,...]]
eGs--| |
+-----+
Or must have one or more parameters. The number of parameters is limited by the maximal length of the op-code/operand field of the assembler, in this case 42 characters, or by the depth of the stack, which is 12.
+-----+
RVKE--| & |
eGs--| |
: | |--RVKE AND eGs[,eGs[,...]]
eGs--| |
+-----+
And must have one or more parameters. The number must not exceed five. Otherwise a heap overflow of the assembler occurs, crashing the assembler. I don't yet understand why.
+-----+
RVKE--| & |
eGs--O| |
: | |--RVKE ANDNOT eGs[,eGs[,...]]
eGs--O| |
+-----+
Note, AndNot is not equivalent to the IEC's
ANDN . ANDNOT is much more effective than the
AND function. Therefore it should be preferred to AND
if possible.
Elementary function blocks require an 1 byte RAM memory area to store
their state. This memory location must be initialised to "false", i.e. 0.
Bistable elements and timers store the output Q, which is also copied to
RVKE at the first byte of the address. You can access that value with the
LD function. This property is denoted by Q in the function
block diagrams.
eAddr
+-----+
| SR |
RVKE--|S1 Q|--RVKE SR eAddr,Gs
Gs--|R |
+-----+
Function blocks retain their state at the address given by the first
operand eAddr . The SR function block has
a dominant set input, passed via RVKE. Reset input is the
second operand.
eAddr
+-----+
| RS |
RVKE--|S Q|--RVKE RS eAddr,Gs
Gs--|R1 |
+-----+
The RS function block has a dominant reset
input, passed as the second operand. The set input is passed via
RVKE. Note: SR and RS differ by
the dominance of the set or reset inputs only. This is IEC 1131-3
standard. Siemens PLC Step5 and Step7 statement list languages use the
reverse naming convention.
+-----+
RVKE--| SET |--eGd SET eGd
+-----+
Because this SR and RS notation is
rather confusing and difficult to remember, which of the inputs to the
SR or RS function blocks are the dominant
ones, IL programmers tend to prefer the Set and
Reset functions. The function used later in the code, is the
dominant one. However, one should place set and reset to one and the same
destination only very few lines apart. Set is S in IEC 1131-3
and reset is named R .
Triggers do not store the result which is assembled in RVKE at their address.
Addr
+-------+
| RTRIG |
RVKE--|CLK |--RVKE RTRIG Addr
+-------+
RTrig is the equivalent of R_TRIG in IEC
and triggers on a rising or leading edge.
Addr
+-------+
| FTRIG |
RVKE--|CLK |--RVKE FTRIG Addr
+-------+
FTrig is the equivalent of F_TRIG in
IEC and triggers on a falling or trailing edge.
Timer function blocks require a 6 byte (3 word) long RAM memory area starting at an even address. The first data word must be initialised to 0 before first use. You have to link timer subroutines to your objects and your operating system must provide some time counter.
A constant cTIME input determines the delay time of
timer function blocks, where cTIME is composed of one or
two constants given as symbols or numbers:
cNumberOfTimeUnits[,cMultiplier] . If cMultiplier is
omitted, a multiplier of one is assumed. The number of time units should
be large compared to the multiplier for accurate timing. On the RTMT4.0
operating system the time unit is a millisecond and the maximum number of
time units should not exceed 30,000, provided the cycle time of the
control program stays below one second. The maximum of the multiplier
always is 65,535
<cTIME>::= <cNumberOfTimeUnits>{,<cMultiplier>}
eAddr
+------+
| TON |
RVKE--| Q|--RVKE TON eAddr,cTIME
cTIME--| |
+------+
TOn implements the standard On-delay. A timing diagram is given in any PLC textbook and not repeated here, therefore.
eAddr
+------+
| TOFF |
RVKE--| Q|--RVKE TOFF eAddr,cTIME
cTIME--| |
+------+
TOff implements the standard Off-delay. According
to IEC this function block should be named TOF, what I find
hard to swallow.
eAddr
+-------+
| TONOFF|
RVKE--| Q|--RVKE TONOFF eAddr,cTIME
cTIME--| |
+-------+
TOnOff is a combination of the previous timer function blocks, which is frequently needed to debounce digital input lines. Therefore, it was coded as another subroutine, called by the corresponding macro. Actually it's faster than any of the other timer blocks each. Its function is defined by the FBD below.
TONOFF eAddr1
+-----+
| TON | eAddr3
+--------| Q|---+ +-----+
| cTIME--| | | | SR |
| +-----+ +---|S1 |
RVKE--+ | Q|--RVKE
| eAddr2 +--O|R |
| +-----+ | +-----+
| | TOFF| |
+--------| Q|---+
cTIME--| |
+-----+
[Top]
A PLC reads all inputs before any program cycle and writes the output
thereafter. On a microcomputer system you have to program input/output and
any filtering of bouncing input lines yourself. The following macros
provide input and output functions and fucnction blocks to read and write
digital i/o-lines. The addressing mode is general addressing only. These
macros require the CRU register to be set up accordingly, since they
employ the TB, SBO, and SBZ microprocessor
op-codes. "disp" denotes the displacement from CRU base, a number or
symbol with a value assigned before use, between -128 and +127.
+------+
| LINP |
disp--| |--Gd LINP Gd,disp
+------+
LInp load input line number disp from CRU base into destination Gd. Gd must address a 1 byte long memory area usually declared a Boolean type variable. If the input line is high, the variable is assigned "true", otherwise "false".
AddrT
+------+
| LINPD|
cTime--| |--Gd LINPD AddrT,cTIME,Gd,disp
disp--| |
+------+
LInpD load Gd with input line number disp from
CRU base with delay cTIME. Gd addresses the Boolean variable loaded
with the state of the input line. AddrT is the address of the
TONOFF timer function block called upon by this macro and must be
a 3 word long memory area, starting at an even address.
AddrT
+------+
|LINPDN|
cTime---| 1 Q|--Gd LINPDN AddrT,cTIME,Gd,disp
disp--O| |
+------+
LInpDN load Gd with input line negated number disp from CRU base with delay cTIME. This function block is the same as the one above but for low active input lines, i.e. low input voltage results in "true", high voltage gives "false".
+------+
| OUTP |
Gs--| |--disp OUTP Gs,disp
+------+
Outp sets the output line number disp from CRU base according to the value of the Boolean variable addressed by Gs. If "true", the output line is set to high voltage, otherwise to low voltage.
[Top]Hyde, R., 1997, Assembly Language Style Guidelines, web page
Texas Instruments, 1978, Model 990 Computer, TMS9900
Microprocessor Assembly language Programmer's Guide (943441-9701), Rev.
(ECN 446281), 366 pp.
Section VII. "Macro Language" of this manual explains on
30 pages similar macro assemblers. None of the examples work with the
MS-DOS cross assembler without modification, however.
Texas Instruments, 1978, 9900 Family Systems Design and Data Book, Chapter 6, [TMS9900] Instruction Set, 62 pp.
Texas Instruments, 1981, TMS 9995 16-Bit Microcomputer,
Preliminary Data Manual, 61 pp.
If you need the complete instruction set and information
on execution timings, this is the reference. While the instructions are
listed completely, there is no explanation, what the instructions
accomplish. The previous references do that, alas for the
TMS9900 processor.
All information contained and accompanying macros are provided with absolutely no warranty for whatsoever. Names mentioned may be trademark. It's the users own responsibility to check, whether any information given here might be covered by patents. The macro library is Free Software, copyleft under the terms of the GNU General Public License version 2 or later, as published by Free Software Foundation.
Please direct any comment and ideas to the author via email to Erland Muller (erland.mueller at desy.de).
[DESY Machine Group] [Top]