TMS9900 Assembler Macro Library for Controller Programs

Erland Müller, DESY -MPS- ,  19/07/2005

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.

Contents

Introduction

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]

Data Types and Access

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]

Elementary Boolean Functions

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.

Load, Store, True, and False

          +---+
     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.

Not, Or, And, AndNot

          +---+
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.

[Top]

Elementary Function Blocks

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.

Bistable Elements

           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 .

Edge Detection

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.

[Top]

Timer Function Blocks

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]

Input/Output Functions

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]

More References

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.

[Top]

Disclaimer

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]