6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Apr 27, 2024 7:35 am

All times are UTC




Post new topic Reply to topic  [ 1 post ] 
Author Message
PostPosted: Thu Feb 09, 2023 1:00 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8144
Location: Midwestern USA
This post applies to all versions of the Kowalski assembler/simulator.

The Kowalski assembler has a variety of directives (pseudo-ops) that facilitate software development, especially in creating macros and conditionally assembling code blocks.  Three such pseudo-ops are of particular value: .IF, .ELSE and .ENDIF, which facilitate setting up conditional assembly.

The general usage of .IF, .ELSE and .ENDIF is:

Code:
.IF <condition>
    do something
.ELSE
    do something else
.ENDIF

In the above, <condition> is an expression that evaluates to TRUE, e.g., 3 != 2, or FALSE, e.g., 3 == 2.  The logic can be inverted by preceding <condition> with an exclamation point, i.e., !<condition>.

The keywords .DEF and .REF may be used in a <condition> expression to determine if a label or symbol exists, or has been referred to at some point in the source code.  While seemingly similar, where these keywords differ is in the way they operate.  The built-in help of the assembler doesn’t adequately explain these two, but some code examples should clarify things.

Code:
         .opt caseinsensitive
         *=$2000
         JSR ABC               ;call the ABC function

         ...

         .IF .DEF(ABC)
ABC      CLC
         LDA #12
         ADC #34
         STA OVERHERE
         RTS
         .ENDIF
         .END

In the above code fragment, we are using the .IF .DEF(ABC) statement to determine if the ABC code block should be assembled.  The expression .DEF(ABC) will evaluate TRUE if ABC has been defined—the value assigned ABC is inconsequential.  However, attempting to assemble the above code will fail with:

    ERROR E021: Undefined label 'ABC'. ROW 3, FILE <filename>

The error, which is referring to the JSR ABC instruction, is a little confusing, since you can clearly see that a code block has been labeled ABC and the only .END pseudo-op in the code is indeed at the end.  So what gives?  :?

The root cause of this error is the .IF .DEF(ABC) statement and the fact that during the first pass, it will evaluate as FALSE.  Although JSR ABC causes the assembler to insert ABC into the symbol table, the entry is initially flagged as undefined, since the assembler doesn’t yet know about the ABC code block—it hasn’t gotten that far in the source file, ergo it can’t assign a value to ABC.

Continuing along, the assembler skips over the ABC code block because .IF .DEF(ABC) evaluated as FALSE.  During the second pass, assembly goes kaput at JSR ABC because ABC is still flagged as undefined, and that is because .IF .DEF(ABC) evaluated FALSE during the first pass, preventing the assembler from setting ABC to some value.

.IF .DEF() will only work if the label or symbol referenced by .DEF() has been defined with some value.  .DEF() is best used to set up conditional assembly for things that are determined before reaching the conditional expression.  For example, a code block may assembled in one of two ways, depending on whether you are assembling for the Rockwell 65C02 (no STP and WAI instructions) or the WDC 65C02, which has those instructions, viz.:

Code:
         .opt caseinsensitive
WDC      =1                    ;assembling for WDC 65C02, actual value...
                               ;is not important
         *=$2000

         ...

ABC      ;in this code block, we halt the system:
         ;with the Rockwell C02, we spin in a loop
         ;with the WDC C02, we stop the MPU
;
         .IF .DEF(WDC)         ;if using the WDC C02...
         STP                   ;stop the internal clock
         .ELSE                 ;if using the Rockwell C02...
.forever bra .forever          ;perpetual loop
         .ENDIF
         .END

In the above fragment, only the WDC-specific code will be assembled.  This will happen because the symbol WDC was defined prior to any reference to it.  So when the .IF .DEF(WDC) is encountered, WDC will have a valid value, .IF .DEF(WDC) will evaluate TRUE and the STP instruction will be assembled—the Rockwell-specific code will be skipped.

Going back to the previous code block that wouldn’t assemble, I’ve changed one thing in it and now it will assemble:

Code:
         .opt caseinsensitive
         *=$2000
         JSR ABC               ;call the ABC function

         
         .IF .REF(ABC)
ABC      CLC
         LDA #12
         ADC #34
         STA OVERHERE
         RTS
         .ENDIF
         .END

Can you spot the change?  If not, carefully look at the conditional assembly statement before the ABC code block and compare it to what was in the earlier example.

In this case, the conditional statement uses .REF(), not .DEF().  The .IF .REF(ABC) expression will evaluate TRUE if any prior reference was made to ABC.  Such a reference was made in the earlier JSR ABC instruction, which results in ABC being inserted into the symbol table and initially flagged as undefined.  Later on, the .IF .REF(ABC) statement will evaluate TRUE and the ABC code block will be recognized by the assembler, thus assigning a value to ABC.  During the second pass, JSR ABC will assemble without error.

If the JSR ABC instruction is removed or commented out, ABC won’t be inserted into the symbol table, the .IF .REF(ABC) statement will evaluate FALSE and the ABC code block will be skipped.

As an example, this method of conditional assembly may be used to include only the functions in a large code library that are actually needed in a program, a technique I use in my projects to avoid code bloat.  For example, here’s an excerpt from my SCSI library that illustrates the technique:

Code:
         .if .ref(snsread)     ;if there's an earlier call to this function...
;===============================================================================
;
;snsread: READ SENSE KEYS FROM SCSI DEVICE
;
;   ...   ...   ...
;
snsread  clc                   ;no initial error
         php
         rep #m_setr|sr_bdm
         phy                   ;save machine state
         phx
         pha
         phb
         phd
;
   ...blah, blah, blah...
;
         .endif                ;end of conditional assembly

The above function is part of a large library of SCSI device-handling routines that runs to nearly 3000 lines of source code.  The SNSREAD function will be assembled only if a JSR SNSREAD statement is encountered earlier in the source code.

To summarize:

  • The expression .IF .DEF(ABC) will evaluate TRUE if ABC has been defined, meaning ABC has been set to any value, either by assignment or by being a label at some point in the source code.  To avoid a forward-reference error, ABC must be defined before evaluating it.

  • The expression .IF .REF(ABC) will evaluate TRUE if a prior reference was made to ABCABC doesn’t have to exist at the point at which first reference is made, but the reference should occur before any statement that will evaluate it.  In some cases, if the first reference to ABC occurs after the .IF .REF(ABC) statement, a forward reference or phase error will occur during the second pass.

* * *

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 1 post ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 29 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: