Next Previous Contents

6. Symbols and labels

A symbol or label is an identifier that starts with a letter and is followed by letters and digits. Depending on some features enabled (see at_in_identifiers, dollar_in_identifiers and leading_dot_in_identifiers) other characters may be present. Use of identifiers consisting of a single character will not work in all cases, because some of these identifiers are reserved keywords (for example "A" is not a valid identifier for a label, because it is the keyword for the accumulator).

The assembler allows you to use symbols instead of naked values to make the source more readable. There are a lot of different ways to define and use symbols and labels, giving a lot of flexibility.

6.1 Numeric constants

Numeric constants are defined using the equal sign or the label assignment operator. After doing

        two = 2

may use the symbol "two" in every place where a number is expected, and it is evaluated to the value 2 in this context. The label assignment operator is almost identical, but causes the symbol to be marked as a label, so it may be handled differently in a debugger:

        io := $d000

The right side can of course be an expression:

        four = two * two

6.2 Numeric variables

Within macros and other control structures ( .REPEAT, ...) it is sometimes useful to have some sort of variable. This can be achieved by the .SET operator. It creates a symbol that may get assigned a different value later:

        four .set 4
        lda     #four           ; Loads 4 into A
        four .set 3
        lda     #four           ; Loads 3 into A

Since the value of the symbol can change later, it must be possible to evaluate it when used (no delayed evaluation as with normal symbols). So the expression used as the value must be constant.

Following is an example for a macro that generates a different label each time it is used. It uses the .SPRINTF function and a numeric variable named lcount.

        .lcount .set 0          ; Initialize the counter

        .macro  genlab
                .ident (.sprintf ("L%04X", lcount)):
                lcount .set lcount + 1

6.3 Standard labels

A label is defined by writing the name of the label at the start of the line (before any instruction mnemonic, macro or pseudo directive), followed by a colon. This will declare a symbol with the given name and the value of the current program counter.

6.4 Local labels and symbols

Using the .PROC directive, it is possible to create regions of code where the names of labels and symbols are local to this region. They are not known outside of this region and cannot be accessed from there. Such regions may be nested like PROCEDUREs in Pascal.

See the description of the .PROC directive for more information.

6.5 Cheap local labels

Cheap local labels are defined like standard labels, but the name of the label must begin with a special symbol (usually '@', but this can be changed by the .LOCALCHAR directive).

Cheap local labels are visible only between two non cheap labels. As soon as a standard symbol is encountered (this may also be a local symbol if inside a region defined with the .PROC directive), the cheap local symbol goes out of scope.

You may use cheap local labels as an easy way to reuse common label names like "Loop". Here is an example:

        Clear:  lda    #$00             ; Global label
                ldy    #$20
        @Loop:  sta    Mem,y            ; Local label
                bne    @Loop            ; Ok
        Sub:    ...                     ; New global label
                bne    @Loop            ; ERROR: Unknown identifier!

6.6 Unnamed labels

If you really want to write messy code, there are also unnamed labels. These labels do not have a name (you guessed that already, didn't you?). A colon is used to mark the absence of the name.

Unnamed labels may be accessed by using the colon plus several minus or plus characters as a label designator. Using the '-' characters will create a back reference (use the n'th label backwards), using '+' will create a forward reference (use the n'th label in forward direction). An example will help to understand this:

        :       lda     (ptr1),y        ; #1
                cmp     (ptr2),y
                bne     :+              ; -> #2
                beq     :+++            ; -> #4
                bne     :-              ; -> #1
                inc     ptr1+1
                inc     ptr2+1
                bne     :-              ; -> #1

        :       bcs     :+              ; #2 -> #3
                ldx     #$FF

        :       ldx     #$01            ; #3
        :       rts                     ; #4

As you can see from the example, unnamed labels will make even short sections of code hard to understand, because you have to count labels to find branch targets (this is the reason why I for my part do prefer the "cheap" local labels). Nevertheless, unnamed labels are convenient in some situations, so it's your decision.

Note: Scopes organize named symbols, not unnamed ones, so scopes don't have an effect on unnamed labels.

6.7 Using macros to define labels and constants

While there are drawbacks with this approach, it may be handy in a few rare situations. Using .DEFINE, it is possible to define symbols or constants that may be used elsewhere. One of the advantages is that you can use it to define string constants (this is not possible with the other symbol types).

Please note: .DEFINE style macros do token replacements on a low level, so the names do not adhere to scoping, diagnostics may be misleading, there are no symbols to look up in the map file, and there is no debug info. Especially the first problem in the list can lead to very nasty programming errors. Because of these problems, the general advice is, NOT do use .DEFINE if you don't have to.


        .DEFINE two     2
        .DEFINE version "SOS V2.3"

        four = two * two        ; Ok
        .byte   version         ; Ok

        .PROC                   ; Start local scope
        two = 3                 ; Will give "2 = 3" - invalid!

6.8 Symbols and .DEBUGINFO

If .DEBUGINFO is enabled (or -g is given on the command line), global, local and cheap local labels are written to the object file and will be available in the symbol file via the linker. Unnamed labels are not written to the object file, because they don't have a name which would allow to access them.

Next Previous Contents