Relocating_Macro_Assembler

Introduction

The OS-9 Level Two Relocatable Macro Assembler (RMA) is a full-featured relocatable macro assembler and linkage editor designed to be used by advanced programmers or with compiler systems.

RMA lets you assemble sections of assembly-language programs independently to create relocatable object files. The linkage editor, RLINK, takes any number of program sections and/or library sections, and combines them into a single OS-9 memory module RMA's features include:

  • OS-9 modular, multi-tasking environment support
  • Built-in functions for calling OS-9 system routines
  • Position-independent, re-entrant code support
  • Creating of standard subroutine libraries by allowing programs to be written and assembled separately and then linked together.
  • Macro capabilities
  • OS-9 Level Two compatibility
  • Automatic resolution of global data and program references
  • Conditional assembly and library source file support

This manual describes how to use RMA and basic programming techniques for the OS-9 environment. However, this manual does not attempt to teach 6809 assembly language programming. If you are not familiar with 6809 programming, consult the Motorola 6809 programming manuals or an assembly-language programming book available at most bookstores and libraries.

Installation

The RMA distribution diskette contains a number of files that you will want to copy to a working system disk. After copying the files, store the original diskette in a safe place.

RMA
Relocatable Macro Assembler program. Copy this file to the system's execution directory (CMDS).

RLINK
Linkage Editor program. Copy this file to the system's execution directory.

ROOT.A
Assemble-language source code file used as a front-end section for programs that use initialized data. Copy this file to an RMA working directory.

Using RMA

RMA is a command program that you can run from the OS-9 Shell, from a Shell procedure file, or from another program. The basic format used to run RMA is:

    RMA filename options >listing

The filename argument represents the source text file. it is the only required argument.

The options argument lets you specify certain RMA features, such as the ability to generate a listing or object file. The list of available options is given in the next section.

The listing option tells RMA to generate a program listing. The redirection symbol (>) lets you redirect the listing to a printer or a disk file, or even pipe the listing to another program. If you omit the redirection symbol, OS-9 prints the listing on your terminal screen.

Available Options

You specify options on the command line by using the prefix - or --. Use - to turn on an option and -- to turn off an option. The available RMA options are:

-o=path
Writes the relocatable output to the specified mass storage file. (Default=off)

-l
Writes the formatted assembler listing to standard output. When this option is off, OS-9 prints error messages only. (Default=off)

-c
Suppresses conditional assembly lines in assembler listings. (Default=on)

-f
Sends a top-of-form signal to the printer. (Default=off)

-g
Lists all code bytes generated. (Default=off)

-x
Suppresses macro expansions in assembler listings. (Default=on)

-e
Suppresses error messages in assembler listings. (Default=on)

-s
Prints the symbol table at the end of the assembly listing. (Default=off)

-d n
Sets the number of lines per page for the listing to n. (Default=66)

> Note: You can override command line options by using the OPT statement with a source program. See the OPT statement for more information.

Examples:

    RMA prog5 -l -s -c >/p [ENTER]

This command line tells RMA to assemble the source program, Prog5. The -l and >/p options cause RMA to write the formatted assembler listing to the printer. The -s option tells RMA to print the symbol table. The -c option tells RMA not to print any conditional assembly lines in the listing.

    RMA sample -l -x --c >/h0/programs/sample.lst [ENTER]

This command line assembles the source program sample and sends the listing to the file sample.lst on the hard disk. The -x option tells RMA to suppress macro expansion in the listing. The --c option tells RMA to print conditional assembly lines.

General Information

RMA is a two-pass assembler. During the first pass through the source file, it creates the symbol table. During the second pass, RMA places the machine-language instructions and data values into the relocatable object file.

Writing and testing an assembly-language program using RMA involves a basic edit, assemble, link, and test cycle. RMA simplifies this process by letting you write programs in sections that you can assemble separately then link to form the entire program. With this method, if you must change one program section, you do not have to reassemble the entire program.

When using RMA to develop assembly-language programs, following these steps:

  1. Create a source program file using a test editor, such as the OS-9 Level Two screen editor, Scred.
  2. Run RMA to translate the source file(s) to relocatable object module(s).
  3. If the assembler reports any errors, correct the source file and reassemble.
  4. Run RLINK to combine the relocatable object module(s).
  5. If RLINK reports any errors, correct the source files, reassemble, and relink.
  6. Run and test your program. You can use the Interactive Debugger to help you with this step. Correct errors, if any.

You now have an executable assembly-language program.

Source File Format

The assembler reads its input from the specified source file. This source file contains variable-length lines of ASCII characters. You can create the source file using any text editor, such as Scred.

Each line of the source file is a text string terminated by an end-of-line character (carriage return). The maximum length for a line is 256 characters. Each line can have from one to four fields, which are:

  • Label field (optional)
  • Operation field
  • Operand field (for some operations)
  • Comment field (optional)

You can specify an entire line as a comment by placing an asterisk (*) as the first character of the line.

> Note: The assembler ignores any blank lines in the source file.

The Label Field

The label field begins at the first character position of the line. Some statements require labels (for example, EQU and SET); others must not have them (for example SPC and TTL).

If a label is present, the assembler usually defines the label as the program address of the first object byte generated for the line. Exceptions occur in the SET, EQU, and RMB statements. In the SET and EQU statements, the assembler gives the label the value of the result of the operand field. In the RMB statement, it gives the current value of the data address counter.

The label must be a legal symbolic name consisting of from one to eight upper- or lowercase characters. Letters, numbers, dollar signs ($), dots (.), and underline characters (_) are allowed. The first character must be a letter. You must not define a label more than once in a program (except when using it with the SET directive).

If you follow the symbolic name in a label field with a colon (:), RMA makes the name globally known to all modules that are linked together. In this way, you can execute a branch or jump to a label in another module. If you do not place a colon after the label, that label is known only in its own PSECT.

If a line does not contain a label, the first character must be a space.

The Operation Field

The operation field specifies the machine-language instruction or assembler directive statement mnemonic name. Use one or more spaces between it and the label field.

Some instructions include a register name (such as LDA, LDD, or LDU) in the operation field. In these cases, you cannot separate the register name from the rest of the field with a space. RMA accepts instruction mnemonic names in either upper- or lowercase characters.

Instructions generate from one to five bytes of object code depending on the specific instruction and address mode. Some assembler directives (such as FCB and FCC) also cause the assembler to generate object code).

The Operand Field

The operand field follows the operation field. You must separate the two fields by at least one space. Some instructions do not use the operand fields; other instructions and assembler directives require an operand to specify an addressing mode, operand address, parameters, and so on.

The Comment Field

The comment field is the last field of a source statement. It is an optional field you can use to include a comment about the instruction. RMA does not process field but copies it to the program listing.

The Assembly Listing Format

If you specify the -l option with RMA, the assembler generates a formatted assembly listing. The output listing uses the following format:

0098  0032 59    +       ROLB
00117 0045=17ffb8 copyit LBSR dmove  copy result


* * *

 ---- ------ ------ ---- -----  -----------
  |     | |  |   |  |     |     |        |
  |     | |  |   |  |     |     |        comment area
  |     | |  |   |  |     |     operand
  |     | |  |   |  |     mnemonic
  |     | |  |   |  label
  |     | |  |   macro expansion indicator
  |     | |  object code bytes
  |     | external reference indicator
  |     location counter value
  listing line number

Evaluation of Expressions

Operands of many instructions and assembler directives can include numeric expressions in one or more places. The assembler can evaluate expressions of almost any complexity using a form similar to the algebraic notation used in programming languages such as BASIC and FORTRAN.

An expression consists of an operand and an operator. An operand is a symbolic name and an operator specifies an arithmetic or logical function. All assembler arithmetic uses 2-byte (16-bit binary internally) signed or unsigned integers in the range of 0 to 65,535 for unsigned numbers, or -32,768 to +32,767 for signed numbers.

In some cases, the assembler expects an expression to produce a value that fits in one byte, such as 8-bit register instructions. Such values must be in the range 0 to 255 for unsigned values and -128 to +127 for signed values.

If the result of an expression is outside its range, OS-9 returns an error message.

OS-9 evaluates expressions from left to right using the algebraic order of operations. That is, it performs multiplication and division before addition and subtraction. You can use parentheses to alter the natural order of evaluation.

Expression Operands

You can use the following items as operands within an expression:

decimal numbers
A positive or negative value containing one to five digits (values are in the range -32,768 through +32,767).

hexadecimal numbers
The dollar sign ($) followed by one to four hexadecimal characters (0-9, A-F, or a-f).

binary numbers
Percent sign (%) followed by one to 16 binary digits (0 or 1)

character constants
Single quotation mark (') followed by any printable ASCII character.

symbolic names
One to nine characters, the first character must be a letter. Legal characters are upper- and lowercase letters (A-Z, a-z), digits (0-9), and the special characters underscore (_), period (.), dollar sign ($), and at (@).

instruction counter
Placed at the beginning of the expression, the asterisk (*) represents the program instruction counter value.

Expression Operators

The following list shows the available operators in the order in which OS-9 evaluates them. Operators listed on the same line have identical precedence. OS-9 processes them left to right when they occur in the same expression.

-
negative
^
logical NOT

&
logical AND
 !
logical OR

  • multiplication
    \
    division

+
addition
-
subtraction

Logical operators are performed bit-by-bit for each bit of the operands.

Division and multiplication functions expect unsigned operands, but subtraction and addition accept signed (2's complement) or unsigned numbers. OS-9 returns an error if you attempt to divide by zero or multiply by a factor that results in a product larger than 65,535.

Symbolic Names

A symbolic name consists of one to nine lower- or uppercase characters, decimal digits, or the special characters dollar sign ($), underscore (_), period (.), or the at (@). The first character in a symbolic name must be a letter. Some examples of legal symbolic names are:

HERE
there
SPL030
PGM_A

Q1020.1
t$integer
L.123.X
a002@

> Note: RMA does not convert lowercase characters to uppercase. The names file_A and FILE_A are unique names.

The following are examples of illegal symbol names:

2move
The first character is not a letter

main.backup
There are more than nine characters

lbl#123
The number sign (#) is not a legal character.

Symbolic Names for System Calls

A system-wide assembly language equate file called OS9defs.a defines the RMA symbolic names for all system calls. You can include this file when RMA assembles hand-written or compiler-generated code by using the USE assembler directive (see Chapter 6). RMA has a built-in macro that generates the system calls from the symbolic names.

Symbolic System names can also be resolved by using sys.l in the LIB directory with RLINK. This chapter contains additional information on the LIB directory. Chapter 9 discusses RLINK.

The DEFS Directory

The OS9defs.a file contains the following groups of defined symbols:

 System Service Request Code definitions 
 I/O Service Request Code definitions 
 File access modes 
 Signal codes 
 Status codes for GetStat/PutStat structure formats 
 Module definitions

 Universal module offsets 
 Type-dependent module offsets

 System module 
 File manager module 
 Device driver module 
 Program module 
 Device descriptor module 
 Machine characteristics definitions 
 Error code definitions 
 System dependent error codes 
 Standard OS-9 error codes

To view the contents of the OS9defs.a file, which includes a brief description of each symbol name, use the OS-9 LIST command. For example, if your OS-9 Level Two Development Pack Diskette 1 is in the current drive, type:

    list /d0/defs/os9defs.a

Or send the file to your printer by typing:

    list /d0/defs/os9defs.a >/p

To include the OS9defs.a file with your source code when assembling a file, you can use the following statements:

    ifp1
    use os9defs.a
    endc

However, OS9defs.a provides the assembly source from which Sys.l is created (see the following section "The LIB Directory"). In many cases, using Sys.l requires less memory and processes faster.

For programmers who prefer to use the OS-9 Level One ASM program for writing code, the DEFS directory contains four other files: Defsfile, Defsfile.dd, OS9defs, and Systype. These four files contain Level Two information but are in the format required by ASM.

Also included in the DEFS directory are Wind.h, Stdmenu.h, Mouse.h, and Buffs.h. These four files contain Level 2 data structures for window, menu, mouse, and buffer manipulation using the C language.

The LIB Directory

The OS-9 library files are also included in the LIB directory on Diskette 1 of your Development Pack. The files are:

cgfx.l
that provides Level Two graphics routines for the C language

sys.l
the system library--defines the standard symbolic references (error messages, I$ and F$ system calls, and so on). Use with RLINK to resolve references rather than the USE instruction in your source code.

For instance, to link a program called Updn (see Chapter 11, "Examples"), you could type:

rlink rels/updn.r -l=/d0/lib/sys.l -o=/d0/cmds/updn

Macros

At times, you might need to use an identical sequence of instructions more than once in a program, such as a routine to display messages to the screen. Instead of repeating the routine in your program, you can create a macro that you can call just like any other assembly-language instruction.

A macro defines a set of instructions with a name you assign. Using this name, you can call the macro as many times as you want. In addition, you can use macros to create complex constant tables and data structures. To define a macro, use the MACRO and ENDM directives. For example, the following macro performs a 16-bit left shift on Register D:

dasl MACRO
     aslb
     rola
     ENDM

The MACRO directive marks the beginning of the macro definition. The name assigned to the macro is dasl. To use this new macro, specify dasl as an instruction as shown here:

    ldd 12,s    get operand
    dasl        double it
    std 12,s    save operand

If RMA encounters a macro name in the instruction field during the assembly process, it replaces the macro name with the machine instructions given in the macro definition. So, when RMA encounters the dasl macro name in the instruction field, it outputs the codes for aslb and rola.

Normally, RMA does not expand macros on listing. However, you can use the -x option to cause it to do so.

> Note: Macros are similar to subroutines, but do not confuse the two. A macro duplicates the routine within your program every time you call it. It also allows some alteration of the instruction operands. A subroutine, however, appears only once within a program and cannot be changed. Also, you call a subroutine using the special instructions (BSR and JSR). Generally, a macro instead of a subroutine produces longer but slightly faster programs.

Macro Structure

A macro definition consists of three sections: header, body, and terminator. The macro header marks the beginning of the macro and assigns the macro's name. the body of the macro contains the statements. The terminator indicates the end of the macro. The general format is as shown here:

name    MACRO    /* macro header */
          .
          .
        body     /* macro body */
          .
          .
        ENDM     /* macro terminator */

The name is required by the MACRO directive. It can be any legal assembler label. You can, if you wish, even redefine a 6809 directive,such as LDA or CLR, by defining a macro with the same name. This lets you use RMA as a cross-assembler for non-6809 (8- or 16-bit) processors by either defining (or redefining) instructions for the target CPU.

> Note: Redefinition of assembler directives, such as RMB, can cause unpredictable consequences. Redefine with care.

The body of the macro contains any number of legal RMA instruction or directive statements. You can even include references to previously defined macros. Calling another macro from whtin a macro is called nesting. For example:

times4  MACRO
        dasl
        dasl
        ENDM

This example shows the times4 macro calling the dasl macro twice. You can nest macros up to eight deep.

> Note: You cannot define one new macro within another.

Macro Arguments

By using arguments with your macros, you can vary a macro each time you call it. You can use arguments to pass operands, register names, constants, variables, and so on, to the macro. A macro can have as many as nine arguments in the operand field. An argument consists of a backslash and an argument number (\1, \2, ...\9).

When RMA expands the macro, the assembler replaces each argument with the corresponding text string argument specified in the macro call. When using arguments within the macro, you can only use them in the operand field. You can use arguments in any order and any number of times.

The following example macro performs the typical instruction sequence to create an OS-9 file:

create  MACRO
        leax    \1,pcr    get addr of filename string
        lda     #\2       set path number
        lda     #\3       set file access mode
        os9     I$Create
        ENDM

The first argument, \1, supplies the filename string address. The second argument, \2, specifies the path number, and the third, \3, gives the file access-mode code. The follow instruction shows how to call the create macro with its arguments:

        create  outname,2,$1E

RMA expands the create macro like this:

        leax    outname,pcr
        lda     #2
        ldb     #$1E
        os9     I$Create

Note that if an argument string includes special characters such as backslashes or commas, you must enclose the string in double quotation marks. For example, the following instruction calls a macro called double and passes two arguments:

        double  count,"2,s"

To declare a null argument, omit the argument and use a comma to hold its place in the sequence (if necessary). RMA creates an empty string. For example:

        double  count

or

        double  ,"2,s"

Special Arguments

RMA has two special argument operators that you might find useful when constructing complex macros. They are:

\L_n_
Returns the length of argument n in bytes

\#
Returns the number of arguments passed in a given macro call

Generally, you use these special operators with RMA's conditional assembly statements to test the validity of arguments used in a macro call, or to customize a macro according to the actual arguments passed. You can use the FAIL directive if you want a macro to report errors that occur during execution. The following example is an expanded version of the create macro:

        double  ,"2,s"
create  MACRO
        ifne    \#-3       must have exactly 3 arguments
        FAIL    create: must have 3 arguments
        endc
        ifgt    \L1-29     filename can be 1 - 29 characters long
        FAIL    create: filename too long
        endc
        leax    \1,pcr     get addr of filename string
        lda     #\2        set path number
        ldb     #\3        set file access mode
        os9     I$Create
        ENDM

Automatic Internal Labels

At times, it might be necessary to use labels within a macro. You can specify macro-internal labels with \@. If there is more than one label, you can add an extra character or characters for uniqueness. For example, if you need two labels with a macro, you might use the names \@A and \@B. You can add the extra character(s) before backslash or after the \@ symbol.

When RMA expands the code, internal labels (\@) take the form \@xxx where xxx is a decimal number between 000 and 999. For example, the expansion of the labels \@A and \@B would be \001A and \001B. If the macro is called again, the expansion would be \002A and \002B, and so on.

The following example shows a macro using internal labels:

testovr MACRO
        cmpd    #\1        compare to arg
        bls     @A         branch if in range
        orcc    #1         set carry bit
        bra     \@B        and skip next instruction
@A      andcc   #$FE       clear carry
@B      equ     1          continue with routine
         .
         .
         .
        ENDM

If you call the testovr macro with the instruction

testovr $80

RMA expands the labels in the following way:

        cmpd    #$80       compare to arg
        bls     @001A      branch if in range
        orcc    #1         set carry bit
        bra     @001B      skip next instruction
@001A   andcc   #$FE       clear carry
@001B   equ     1          continue with routine...
         .
         .
         .

If you call the testovr macro a second time with

testovr 240

RMA expands the labels in the following way:

        cmpd    #240       compare to arg
        bls     @002A      branch if in range
        orcc    #1         set carry bit
        bra     @002B      skip next instruction
@002A   andcc   #$FE       clear carry
@002B   equ     1          continue with routine...
         .
         .
         .

Documenting Macros

Although macros are a useful programming tool, you should use them with care. Indiscriminate use can impair the readability of a program and make it difficult for other programmers to understand the program logic. Be sure to document your macros thoroughly.

Program Sections

One of the most useful function of RMA is that it lets you write programs in segments that you can assemble separately. You can then use RLINK to combine the segments into one OS-9 memory module with a coordinated data storage area.

When writing a program in segments, you must divide it into sections for variable storage definitions (VSECTs) and sections for program statements (PSECTs). By using external names, the code in one segment can reference variables declared in another segment, or can transfer program control to labels in another segment. The assembler outputs a relocatable object file (ROF) for each program section. This object file contains the object code output plus information about the variable storage declarations for the linker to use.

RLINK reads relocatable object files, and assigns space in the data storage area. It also combines all the object code into a single executable memory module. To do this, RLINK must alter the operands of instructions to refer to the final variable assignments and must adjust program transfer control instructions that refer to labels in other segments.

The following shows a simplified memory map after the linker has processed three program segments (A, B, and C):

process data area

Segment A Variables

Segment B Variables

Segment C Variables

Executable Memory Module

Module Header

Segment A Object Code

Segment B Object Code

Segment C Object Code

CRC Check Value

Each section in the process data area corresponds to each program segment's VSECT. RLINK generates the module header and CRC check values. The Segment A Object Code is the mainline, or beginning, segment. Each object code segment corresponds to each program segment's PSECT.

Program Section Declarations

RMA uses three section directives (PSECT, VSECT, and CSECT) to control the placement of object code and allocation of variable space in the program. The ENDSECT directive indicates the end of a section.

PSECT indicates the beginning of a relocatable object file. PSECT causes RMA to initialize the instruction and data location counters, and assemble subsequent instructions into the ROF object code area.

VSECT causes RMA to change the variable (data) location counters and to place information about subsequently declared variables in the appropriate ROF data description area. You declare VSECTs within PSECTs.

CSECT initializes a base value for assigning sequential numeric values to symbolic names. CSECTs are provided for convenience only. Their use is optional.

RMA maintains the following counters within each section:

Directive
Counter

PSECT
instruction location counter

VSECT
initialized direct page counter

non-initialized direct page counter

initialized data location counter

non-initialized data location counter

Because the source statements within a certain program section cause the linker to perform a function appropriate for the statement, the type of mnemonic allowed within a section is sometimes restricted. However, the following mnemonics can appear inside or outside any section: nam, opt, ttl, pag, spc, use, fail, rept, endr, ifeq, ifne, iflt, ifle, ifge, ifgt, ifp1, endc, else, equ, set, macro, endm, and endsect.

Program Section Directives

PSECT Directive

The PSECT directive specifies the beginning of a program code section. You can specify only one PSECT for each assembly-language file. The PSECT directive initializes all assembler location counters and marks the start of the program segment. You must declare all instruction statements and VSECT data reservations (RMB) within the PSECT/ENDSECT block.

The syntax for the PSECT directive is:

    PSECT name,typelang,attrrev,edition,stacksize,entry

If the program section is to be a mainline segment, you can specify the name and five expressions as an operand list to PSECT. RMA stores the values of the operand list in the relocatable object file for later use by the linker. If you omit the operand list, PSECT defaults to the name Program and all expressions default to zero. The following list describes the available expressions:

name
Used by the linker to identify the PSECT. The name can be up to 20 bytes long and can consist of any printable characters, except the space and comma. The name does not need to be unique; however, it is often easier to identify PSECTs when their names are distinct.

typelang
Used by the linker as the executable module type/language byte. If the PSECT is not a mainline segment, typelang must be zero.

attrrev
Used by the linker as the executable module attribute/revision byte.

edition
Used by the linker as the executable module edition byte.

stacksize
Used by the linker as the amount of stack storage required by the PSECT. Specify stacksize as a word expression. The linker adds the value in all PSECTs that make up the executable module and adds the total to any data storage requirement for the entire program.

entry
Used by the linker as the program entry point offset for the PSECT. Specify entry as a word expression. If the PSECT is not a mainline segment, this value must be zero.

Statements that you can use in a PSECT are: any 6809 mnemonic, fcc, fcs, fdb, rzb, vsect, endsect, os9, and end. Note that you cannot use RMB in a PSECT.

> Note: If you are familiar with the OS-9 Level I Interactive Assembler, note the following differences between RMA's PSECT directive and the Interactive Assembler's MOD statement. The MOD statement directly outputs an OS-9 module header, but PSECT only sets up information for the linker. The linker creates the module header.

Example

* this program starts a basic09 process

        ifp1
        use     ..../defs/os9defs.a
        endc

PRGRM   equ     $10
OBJCT   equ     $1
stk     equ     200

        psect   rmatest,$11,$81,0,stk,entry

name    fcs     /basic09/
prm     fcb     $d
prmsize equ     *-prm

entry   leax    name,pcr
        leau    prm,pcr
        ldy     #prmsize
        lda     #PRGRM+OBJCT
        clrb
        os9     F$Fork
        os9     F$Wait
        os9     F$Exit
        endsect

VSECT Directive

The VSECT directive indicates the variable storage section, which can contain either initialized or non-initialized variable storage definitions. The VSECT directive causes RMA to change the data location counters. RMA offers two sets of counters for each VSECT: one set for direct page variables and another for variables that are normally index-register offsets into a process's data storage area.

The syntax for a VSECT directive is:

    VSECT [DP]

If you specify the DP operand, RMA uses the direct page counters. If you omit DP, RMA uses the index register counters.

You can specify any number of VSECT blocks within a PSECT. Note, however, that the data location counters maintain their values from one VSECT to the next. Because the linker handles the actual data allocation, there is no facility to adjust the data location counters.

Statements that you can use within a VSECT are: rmb, fcc, fdb, fcs, fcb, rzb, and endsect. The fcc, fdb, fcb, fcs, and rzb directives place data into the initialized data area. Programs move initialized constants which appear iinside a VSECT from the data section to the program section for accessing by the 6809 program counter relative addressing mode. Initialized constants can appear outside of a VSECT; however, if they do, the program cannot change them.

Example

        ifp1
        use     ..../defs/os9defs.a
        endc

PRGRM   EQU     $10
OBJCT   EQU     $1
stk     EQU     200

        PSECT    pgmlen,$11,$81,0,stk,start

* data storage declarations
        VSECT
temp    RMB     1
addr    RMB     2
buffer  RMB     500
        ENDSECT

start   leaxy   buffer,u   get address of buffer
        clr     temp
        inc     temp
        ldd     #500       loop count
loop    clr     ,x+
        subd    #1
        bne     loop
        os9     F$Exit     return to OS9
        ENDSECT

CSECT Directive

The CSECT directive provides a method for assigning consecutive offsets to labels without restoring to EQUs.

The syntax for the CSECT directive is:

    CSECT expression

If you specify an expression, RMA sets the CSECT base counter to the specified value. If you do not include an expression, RMA uses a base counter value of zero.

Example

* This CSECT assignes offset of 0, 1, and 2 respectively

        CSECT   0
R$CC    RMB     1          Condition code register
R$A     RMB     1          A accumulator
R$B     RMB     1          B accumulator
        ENDSECT

See the Defs file that is included in the OS9 Development diskette for more CSECT examples

Assembler Directive Statements

Directive statements give the assembler information that affects the assembly process, but they do not generate code. REad the descriptions in this chapter carefully. Some directives require labels, some allow optional labels, and a few cannot have labels.

End Statement

The END statement indicates the end of the program. The syntax for END is:

    END

You cannot use a label with the END statement.

Because RMA assumes the end of file when it encounters an end-of-file condition on the source, the END statement is optional.

EQU and SET Statements

The EQU and SET statements let you assign a value to a symbolic name (label). The syntax for these statements are:

    label EQU expression
    label SET expression

The label is required. You can specify expression' as an expression, a name, or a constant.

EQU lets you define symbols only once in the program. Usually, you use EQU to define program symbolic constants, especially those used with instructions. It is a standard programming practice to place all EQUs at the beginning of the program.

When using EQU, the label must be unique, and you must define the expression if you specify a name.

SET lets you redefine a symbol as many times as you want. Usually, you use SET to define symbols used to control the assembler operations, such as conditional assembly and listing control.

Example

FIVE    EQU     5
OFFSET  EQU     address+base
TRUE    EQU     $FF
FALSE   EQU     0
SUBSET  SET     TRUE

        ifne    SUBSET
        use     subsets.defs
        else
        use     full.defs
        endc

SUBSET  set     FALSE

FAIL Statements

The FAIL statement forces RMA to report an assembler error. Generally, you use FAIL with conditional assembly directives that test for various illegal conditions. The syntax for the FAIL statement is:

    FAIL textstring

RMA displays the textstring operand in the same manner as normal RMA-generated error messages. Because RMA assumes the entire line after the FAIL keyword to be the error message, you cannot specify a comment field.

Example

        ifeq    maxval
        FAIL    maxval cannot be zero
        endc

IF, ELSE, and ENDC Statements

The IF, ELSE, and ENDC statements let you selectively assemble (or not assemble) one or more parts of a program, depending on the value of a variable or computed value. The syntax for these statements are:

        IFxx    expression
        statements
        ELSE
        statements
        ENDC

WHEN RMA processes an IF statement, it makes the desired comparison. If the comparison result is true, RMA processes the statements following the IF statement until it finds an ENDC or ELSE.

The ELSE statement is optional. If RMA encounters an ELSE statement, it processes the statements following the ELSE if the result of the comparison is false.

The ENDC statement marks the end of a conditional program section.

There are several available IF statements:

IFEQ
True if operand equals zero

IFNE
True if operand does not equal zero

IFLT
True if operand is less than zero

IFLE
True if operand is less than or equal to zero

IFGT
True if operand is greater than zero

IFGE
True if operand is greater than or equal to zero

IFP1
True only during the assembler's first pass (no operand)

Examples

In the following example, IFEQ tests if the operand is equal to zero:

        IFEQ    SWITCH
        ldd     #0         assembled only if SWITCH=0
        leax    1,x
        ENDC

The following example adds the ELSE condition to the preceding program.

        IFEQ    SWITCH
        ldd     #0         assembled only if SWITCH=0
        leax    1,x
        ELSE
        ldd     #1         assembled only if SWITCH does not equal 0
        leax    -1,x

You can use IF statements to test the result of an arithmetic evaluation as an operand. This example tests to see if the result of the subtraction of MIN from MAX is less than or equal to zero:

        IFLE    MAX-MIN

The IFP1 statement tells RMA to process subsequent statements during the first pass only. You can use this for program sections that contain only symbolic definitions to be processed only once during the assembly. Because they do not generate actual object code output, the symbolic definitions are processed during Pass 1 only. The OS9Defs file is an example of a large section of such definitions. For example, you can use the following statements at the beginning of many source files:

        IFP1
        use     /dd/defs/OS9Defs
        ENDC

NAM and TTL Statements

The NAM and TTL statements let you define or redefine a program name or listing title line, respectively. RMA prints this information on each listing page header.

The syntax for NAM and TTL are:

    NAM string
    TTL string

You cannot specify a label with these statements.

RMA prints the program name, set by NAM, on the left side of the second line of each listing page. RMA then prints a dash, and the title list, set by TTL. You can change the program name and listing title as often as you like.

Example

    NAM Datac
    TTL Data Acquisition System

This example prints the following information in the listing header:

    Datac - Data Acquisition System

OPT Statement

The OPT statement lets you set or reset any of several assembler control options. The syntax is:

    OPT option

The operand option can be any of the assembler options described in Chapter 1 of this manual. It consists of one character, except for the d and w options, which require a number. Do not specify - or -- in the OPT statement.

You cannot use the label or comment fields with the OPT statement.

Examples

The following statements suppresses the listing generation:

    OPT l

The next example sets the line width to 72 characters:

    OPT w72

PAG and SPC Statements

The PAG and SPC statements let you improve the readability of a program listing by starting a new page or inserting blank lines. The syntax for the statements are:

    PAG
    SPC expression

The PAG and SPC statements cannot have a label field.

The PAG statement causes RMA to begin a new page in the listing. For Motorola compatibility you can also use the alternate form, PAGE.

The SPC statement inserts blank lines in the listing. The operand expression specifies the number of blank lines to be inserted. The expression can be an expression, constant, or name. If you omit the expression', RMA inserts one blank line.

REPT and ENDR Statements

REPT and ENDR let you repeat the assembly of a sequence of instructions a specified number of times. The syntax is:

    REPT expression
    statements
    ENDR

The operand expression specifies the number of times the assembly is to be repeated. The expression cannot include EXTERNAL or undefined symbols. You cannot nest REPT loops.

Example

* make module size exactly 2048 bytes
        REPT    2048-*-3   compute fill size w/crc space
        fcb     0
        ENDR
        emod

* 20-cycle delay
        REPT    5
        nop
        nop
        ENDR

RMB Statement

The RMB statement has two uses. When used within a VSECT, RMB declares storage for non-initialized variables in the data area. When used within a CSECT, RMB assigns a sequential value to the symbolic name given as its label. The syntax for RMB is:

    label RMB expression

When using RMB in a VSECT, specify a label that is assigned the relative address of the variable. In OS-9, the address must not be absolute and you should usually use indexed or direct page addressing modules to access variables. The linker assigns the actual relative address when processing the relocatable object file. It adds the operand, expression to the address counter to update them.

When using RMB in a CSECT, specify a label to which to assign the value of the current CSECT location counter. Doing this, then updates the counter by causing the program to add the result of the expression given.

USE Statement

The USE statement causes RMA to temporarily stop reading the current input file. USE requests that OS-9 open and read input from the specified file/device until an end-of-file occurs. OS-9 then closes the new input file, and RMA resumes processing at the statement following the USE statement. The syntax is:

    USE pathlist

The pathlist specifies the new input file or device. You cannot specify a label with the USE statement.

You can nest as many USE statements as you can have open files at one time (usually 13, not including the standard I/O paths).

Example

To accept interactive input from the keyboard during the assembly of a disk file, use the following statement:

    USE /term

Pseudo-Instructions

Pseudo-instructions are special assembler statements that generate object code, but do not correspond to actual 6809 machine instructions. Their primary purpose is to create special sequences of data to be included in the program. Labels are optional on pseudo-instructions.

FCB and FDB Statements

The FCB and FDB pseudo-instructions generate sequences of constants within the program. The syntax for these pseudo-instructions are:

    FCB expression,[expression,...]
    FDB expression,[expression,...]

Expression' can be any legal expression. You can specify more than one expression by separating them with commas.

FCB generates a sequence of single constants in the program. It reports an error if an expression has a value that is greater than 255 or less than -128.

FDB generates a sequence of double constants in the program. If FDB evaluates an expression with an absolute value of less than 256, the high-order byte is zero.

If FCB or FDB appears within a VSECT, RMA assigns the data to the appropriate initialized data area (DP or non-DP). Otherwise, RMA places the constant in the code area. If the constant contains an EXTERNAL reference, the program, using Root.a, must copy out and adjust the references.

Examples

    FCB  1,20,'A
    FCB  Index/2+1,0,0,1

    FDB  1,10,100,1000,10000
    FDB  $F900,$FA00,$FB00,$FC00

FCC and FCS Statements

The FCC and FCS pseudo-instructions generate a series of bytes corresponding to the specified character string. The syntax are:

    FCC  string
    FCS  string

FCS is the same as FCC except that the most significant bit (the sign bit) of the last character in the string is set. This is a common OS-9 programming technique to indicate the end of a text string without using additional storage.

String must be enclosed in delimiters. You can use the following characters as delimiters:

    ! " # $ % & ( ) * + , - . /

The beginning and ending delimiters must be the same character. The delimiting character cannot appear in the character string.

FCC and FCS output bytes that are the literal numeric representation of each ASCII character in the character string.

If FCC or FCS appear in a VSECT, RMA assigns the data to the appropriate initialized data area (DP or non-DP). Otherwise, RMA places the constant in the code area.

Examples

    FCC  /this is the character string/
    FCS  ,0123456789,
    FCS  AA     null string
    FCC  $z$
    FCS  ""     null string

RZB Statement

The RZB pseudo-instruction fills memory with a sequence of bytes, each of which has a value of zero. The syntax is:

    RZB expression

The expression is a 16-bit expression. RMA evaluates the expression and places that number of zero bytes in the appropriate code or data section.

OS9 Statement

The OS9 pseudo-instruction is a convenient way to generate OS-9 system calls. The syntax is:

    OS9 expression

RMA uses the expression value as the request code. The following instruction sequence is the equivalent to the OS9 pseudo-instruction:

    SWI2
    FCB  operand

The OS9Defs file contains the standard definitions of the symbolic names of all the OS-9 service requests. You can use these names with the OS9 pseudo-instruction to improve the readability and portability of assembly-language software.

Examples

    OS9  I$Read    call OS-9 READ service request
    OS9  F$Exit    call OS-9 EXIT service request

Accessing the Data Area

In general, RMA assumes that the program will access data using indexed or direct page addressing modes. By convention, one index register contains the starting address of the data area, and the direct page register contains the page number of the lowest-address page of the data area. The RMA/RLINK system automatically adjusts operands of instructions, using indexed and direct page addressing modes.

RMA accesses the data area differently depending on whether or not your program uses initialized data. Initialized data is data that has an initial value that is modified by the program. You create initialized data with the FCB, FDB, FCC, and similar directives used in a VSECT.

If you do not use initialized data, RMA accesses data using index registers--this is the method used by the OS-9 Level I Interactive Assembler.

Using Non-Initialized Data

Programs that do not use initialized data declare all data storage in VSECTs using RMBs. The following diagram shows how RMA sets up the data memory area and registers for a new process:

[-img src=Figure 8-1.png: missing =-]

When OS-9 executes a process, the MPU registers contain the bounds of the data area. Register U contains the beginning address and Register Y contains the ending address. OS-9 sets the SP register to the ending address + 1, unless you use a parameter. The direct page register contains the page number of the beginning page. If you used no parameters, Y, X, and SP are the same value. The OS-9 shell always passes at least an end-of-line character in the parameter area.

If Register U is maintained throughout the program, you can use constant-offset-indexed addressing.

You can write part of the program's initialization routine to compute the actual addresses of the data structure and store these addresses in pointer locations in the direct page. Then, obtain the addresses later using direct-page addresses mode instructions.

> Note: Because the memory addresses assigned to the program section and the address section are not a fixed distance apart, you cannot use program-counter relative addressing to obtain the address of objects in the data section.

Using Initialized Data

If you plan to use initialized data, you need to copy the data from the initialized data section in the object module to the data storage area pointed to by Register U. Do so by using the Root.a mainline module (object code that is directly executable by using OS-9 F$Fork). The function of the Root.a mainline module is to use the initializing values and offsets of the initialized data location, stored in the object code module, to actually initialize variables. The linker automatically generates the initialization information passed by RMA in the relocatable object file.

Root.a sets Register Y to point to the same location to which Register U pointed. Register X points to the parameter area, and Register U points to the top of data allocated by the linker. The data-index register choice is arbitrary, but use your choice consistently. To maintain compatibility with code produced by the C compiler, register Y is used as the data pointer. For more information on Root.a, study the commented source code supplied on the distribution diskette. The following diagram shows how RMA sets up the data area:

[-img src=Figure 8-3.png: missing =-]

Using the Linker

The Relocating Macro Assembler lets you write and assemble programs separately and then link them to form a single object code OS-9 module. The linker, RLINK, combines relocatable object files (ROF) into a single OS-9 format memory module. It also resolves external data and program references. Because RLINK allows references to occur between modules, you can write one program that references a symbol in another program.

If RMA encounters an external reference during the assembly process, it sets up information denoting the existence of an internal reference. RMA does not know the location of the external reference.

Because RMA is a relocatable assembler, it produces relocatable object files that do not have absolute addresses assigned. RMA assembles each section with the absolute address 0.

RLINK reads in all the relocatable object files and assigns each an absolute memory address for data locations and instruction locations for branching. OS-9 resolves any other addresses at execution time.

By using RMA and RLINK, you can write programs in smaller sections that are easier to read and debug. In this way, if an error occurs, you need edit and reassemble only the module in which the error occurred. Then, you can relink the fixed module with the rest of the program.

Running the Linker

You call the linker, RLINK, with the following command line:

    RLINK [options] mainline [sub1 ... subn] [options]

All input files must be in relocatable object format (ROF).

Mainline specifies the pathlist of the mainline (first) segment from which RLINK resolves external references and generates a module header. It is the object of the mainline file to perform the initialization of data and the relocation of any initialized data references within the initialized data, using the information in the object module supplied by RLINK (See Chapter 7). You indicate that a program is the mainline module by setting the type/lang value in the PSECT directive to a non-zero value.

The sub1 and subn options represent any additional modules to be linked to the mainline module. The additional ROFs cannot contain a mainline PSECT notation (type/lang>0).

RLINK includes the mainline file and all sub-modules in the final linked object module, even if you did not reference the subroutine.

Available Options

You can use any of the following options on the RLINK command line:

-o=path
Writes the linker object (memory module) output to the file specified by the pathlist. RLINK assumes the last element in the pathlist to be the module name unless you use the -n option.

-n=name
Specifies name as the object file.

-l=path
Specifies path as the library. A library file consists of one or more merged assembly ROFs. The assembler checks each PSECT in the file to see if it resolves any unresolved references. If so, RLINK includes the module in the final output module. Otherwise, RLINK skips the file. RLINK searches library files in the order in which you specify them on the command line. A library file cannot contain a mainline PSECT.

-E=n
Sets the edition number in the final output module to n. You can also use -e (lowercase).

-M=size
Sets the number of pages of additional emmory for RLINK to allocate to the data area of the final object module. If you omit this option, RLINK adds up the total data stack requirements found in the PSECT of the input modules, and uses that value.

-m
Prints the linkage map that indicates base addresses of the PSECTs in the final object module.

-s
Prints the final addresses that RLINK assigned to symbols in the final object module.

-b=ept
Links C-language functions so that they can be called from BASIC09. The argument ept specifies the name of the function to which control is transferred when BASIC09 executes a RUN command.

-t
Allows tatic data to appear in a BASIC09 callable module. RLINK assumes that the C function being called and the calling BASIC09 program provide a sufficiently large static storage data area pointed to by Register Y.

Error Messages

When RMA detects an error during assembly, it prints an error message in the listing just before the source line containing the error. In some cases, RMA might report more than one error for a source line. If you do not use the -l option to produce the listing, RMA still prints the error messages and the problem source line. At the end of the listing, RMA reports the total number of errors and warnings in the assembly summary statistics.

RMA prints all error messages, the associated source line, and the assembly summary to the assembler's error path. You can redirect this output using the shell redirection symbol. For example:

    RMA sourcefile -o=sourcefile >>src.error

During the initial stages of assembly, you might find it useful to suppress generation of both the listing and object code (by omitting the -l and -o options). Doing this lets RMA perform a quick assembly just to check for errors. In this way, you can find and correct many errors before printing a lengthy listing.

Some errors stop execution on a line. In these cases, RMA might not detect all errors that occur on one line; so, make changes carefully.

The following list shows the RMA error messages and a description for each message.

Bad label

The label field contains an incorrectly formed label.

Bad Mnemonic

The mnemonic field contains a mnemonic that RMA does not recognize or a mnemonic that is not allowed in the current program section.

Bad number

The numeric constant definition contains a character that is not allowed in the current radix.

Bad operand

The operand field is missing an expression or contains an incorrectly formed operand expression.

Bad operator

The operator contains an incorrectly formed arithmetic expression.

Bad option

An option is not recognized or is incorrectly specified.

Bracket missing

A bracket is missing from an expression.

Can't open file

RMA encountered a problem when opening an input file.

Can't open macro work file

RMA cannot open a macro work file.

Comma expected

RMA cannot find an expected comma.

Conditional nesting error

Program contains mismatched If and ELSE/ENDC conditional assembly directives.

Constant definition

The statement contains an incorrectly formed constant definition.

DP section???

Direct Page assignments have exceeded 256 bytes.

ENDM without MACRO

RMA encountered an ENDM statement without a matching MACRO statement.

ENDR without REPT

RMA encountered an ENDR statement without a matching REPT statement.

Fail message

RMA encountered a FAIL directive.

File close error

An error occurred closing a file.

Illegal addressing mode

The specified addressing mode cannot be used with the instruction.

Illegal external reference

You cannot use external names with assembler directives. If an operand expression contains an externam name, RMA can only perform binary plus and minus operations.

Illegal index register

You cannot use the specified register as an index register.

Label missing

The statement is missing a required label.

Macro arg too long

More than 60 characters (total) were pass to the macro.

Macro file error

RMA experienced problems when trying to access the macro work file.

Macro nesting too deep

You can nest macros up to eight levels deep.

Nested MACRO definition

You cannot define a macro within another macro definition.

Nested REPT

You cannot nest repeat blocks.

New symbol in pass two

This indicates an assembler symbol lookup error. This error can be caused by a symbol table overflow or bad memory.

No input files

You must specify an input file.

No param for arg

A macro expansion is attempting to access an argument that was not passed by the macro call.

Phasing error

A label has a different value during Pass 2 than it did during Pass 1.

Redefined name

The name appears more than once in the label field (other than on a SET directive).

Register list error

The legal register names allowed in trf, exg, and pul are: A, B, CC, DP, X, Y, U, S, and PC.

Register size mismatch

The registers specified in the tfr and exg instruction must be the same size.

Symbol lost?

This indicates an assembler symbol lookup error. This error can be caused by a symbol table overflow or bad memory.

Too many args

You can pass up to nine arguments to a macro.

Too many object files

You can specify the -o option to RMA only once on the command line.

Too many input files

You can specify a maximum of 32 input files.

Undefined org

The * (program counter org) cannot be accessed within a VSECT.

Unmatched quotes

A quotation mark is missing.

Value out of range

A byte expression value is less than -128 or greater than 255.

Examples

This chapter contains two assembly language programming examples:

  • LIST, to list files
  • UpDn, to convert input case to either upper or lower


    • LSIT UTILITY COMMAND
    • A "LIST" Command for poor typists
    • Syntax: lsit <path>
    • Lsit copies input from path to standard output
    • NOTE: This command is similar to the
    • LIST command. Its name was changed
    • to allow easy assembly and testing
    • since LIST normally is already in memory.

    PRGRM equ $10
    OBJCT equ $01
    STK equ 200

         csect
    

    IPATH rmb 1 input path number
    PRMPTR rmb 2 parameter pointer
    BUFSIZ rmb 200 size of input buffer
    endsect

         psect   list,PRGRM+OBJCT,$81,0,STK,LSTENT
    

    BUFFER equ 200 allocate line buffer
    READ equ 1 file access mode

    LSTENT stx PRMPTR save parameter ptr
    lda #READ select read access mode
    os9 I$Open open input file
    bcs LSIT50 exit if error
    sta IPATH save input path number
    stx PRMPTR save updated param ptr

    LSIT20 lda IPATH load input path number
    leax BUFFER,u load buffer pointer
    ldy #BUFSIZ max bytes to read
    os9 I$ReadLn read line of input
    bcs LSIT30 exit if error
    lda #1 load st. out path #
    os9 I$WritLn output line
    bcc LSIT20 repeat if no error
    bra LSIT50 exit if error

    LSIT30 cmpb #E$EOF at end of file?
    bne LSIT50 branch if not
    lda IPATH load input path number
    os9 I$Close close input path
    bcs LSIT50 ...exit if error
    ldx PRMPTR restore param ptr
    lda 0,x
    cmpa #$0D end of param line?
    bne LSTENT ...no; list next file
    clrb
    LSIT50 os9 F$Exit ...terminate
    endsect

    • This is a program to convert characters to lower to
    • upper case (by using the u option), and upper to lower
    • (by using no option). To use type:
    • updn u (for lower to upper)

6809 Instructions and Addressing Modes

  Direct Extended Indexed Immediate Accumulator Inherent Relative Register

ABX
 
 
 
 
 
X
 
 

ADCA
X
X
X
X
 
 
 
 

ADCB
X
X
X
X
 
 
 
 

ADDA
X
X
X
X
 
 
 
 

ADDB
X
X
X
X
 
 
 
 

ADDD
X
X
X
X
 
 
 
 

ANDA
X
X
X
X
 
 
 
 

ANDB
X
X
X
X
 
 
 
 

ANDCC
 
 
 
X
 
 
 
 

ASL
X
X
X
 
 
 
 
 

ASLA
 
 
 
 
 
X
 
 

ASLB
 
 
 
 
 
X
 
 

ASR
X
X
X
 
 
 
 
 

ASRA
 
 
 
 
 
X
 
 

ASRB
 
 
 
 
 
X
 
 

(L)BCC
 
 
 
 
 
 
X
 

(L)BCS
 
 
 
 
 
 
X
 

(L)BEQ
 
 
 
 
 
 
X
 

(L)BGE
 
 
 
 
 
 
X
 

(L)BGT
 
 
 
 
 
 
X
 

(L)BHI
 
 
 
 
 
 
X
 

(L)BHS
 
 
 
 
 
 
X
 

BITA
X
X
X
X
 
 
 
 

BITB
X
X
X
X
 
 
 
 


Related

Old Wiki: NitrOS-9_Level_2_Development_System