MEL PICBASIC Forum - The EXT (external) modifier.


  • The EXT (external) modifier.

    Here's a subject where RTFM does NOT apply.
    In fact, the only place the PBP manual even mentions it is in the Reserved Words (Appendix C).

    The EXT modifier for Variables and Constants can solve many problems that seem impossible otherwise. It can save TONs of code space in certain situations. And, it can also confuse the hell out of you if you're not sure how it works. Hopefully this will help with the latter part.

    EXT's only function is to tell PBP that it doesn't need to figure out where a Variable is located, or what a Constant's value is. Because it will be handled in ASM at compile time. And, since PBP doesn't "Lock it in" we can change it to anything we want later on, even in the middle of the program if need be.

    <a href="http://www.picbasic.co.uk/forum/content.php?r=233-The-EXT-(external)-modifier.#CON">Using EXT with Constants</a><br><a href="http://www.picbasic.co.uk/forum/content.php?r=233-The-EXT-(external)-modifier.#LAB">Using EXT with Labels</a><br><a href="http://www.picbasic.co.uk/forum/content.php?r=233-The-EXT-(external)-modifier.#VAR">Using EXT with Variables</a><br><a href="http://www.picbasic.co.uk/forum/content.php?r=233-The-EXT-(external)-modifier.#TYP">Typecasting Variables inside of Arrays</a><br><a href="http://www.picbasic.co.uk/forum/content.php?r=233-The-EXT-(external)-modifier.#ARRAY">Arrays within Arrays</a><br><a href="http://www.picbasic.co.uk/forum/content.php?r=233-The-EXT-(external)-modifier.#LIM">Limitations of EXT</a>


    <a name="CON"></a><hr>Using EXT with Constants

    <b><pre>@MyConstant = 1234h ;no space between @ and Name<br>MyConstant CON EXT</pre></b>&nbsp;&nbsp;... is exactly the same as ...

    <b><pre>MyConstant CON $1234</pre></b>However, by using the EXT modifier you can change it at any point in the program. And, you can use the Power of a 32-bit assembler to calculate what those values should be.

    For instance, if you wanted to select a constant to be loaded in a timer on each interrupt depending on the crystal being used, you might do something like this
    Code:
    <b>TimerConst  CON EXT</b>
    
    ASM
      IF OSC == 4                       ; Constants for 100hz interrupt from Timer1
    TimerConst = 0D8F7h                 ; Executed at compile time only
      EndIF
      If OSC == 8
    TimerConst = 0B1E7h
      EndIF
      If OSC == 10
    TimerConst = 09E5Fh
      EndIF
      If OSC == 20
    TimerConst = 03CB7h
      EndIF
    ENDASM
    Or, you could let the assembler do the math ...
    Code:
    TimerConst  CON EXT
    Freq        CON 100
    @TimerConst = 65543-(OSC*1000000/4/_Freq) ; 100hz Constant for Timer1 reload
    Either way it does not use any program space in the pic. It's all done at "Compile Time".

    <a name="LAB"></a><hr>Using EXT with Labels ...

    One of best uses of EXT is to find out the Address of ANYTHING (that's already been declared) in the program.

    To the assembler, Variables, Constants and Labels are all the same thing. Numbers! It's how those numbers get used that makes the difference.

    A Constant is just a number.
    A variable is a number that represents the Address of a Ram location.
    And a Label is a number that represents the Address of a location in Program Space.


    So any of these items can be assigned to a constant for PBP.

    In this example, a list of 14-bit WORD values can be created in a lookup "Table".
    With DataTable defined as an "External" constant, it will be assigned the Address of the Label with the same name. This allows you to easily locate items stored in Program memory.
    Code:
    DataWord  VAR  WORD
    Offset    VAR  WORD
    <b>DataTable CON  EXT</b>
    
    '-----[The DATA table]--------------------------------------------------------
    GOTO OverData             ; Make sure data doesn't try to execute
    ASM
    <b>DataTable</b>
        DW  1234h, 2178h      ; etc.
    endasm
    OverData:
    
    '-----[Retrieve from DATA table]----------------------------------------------
    Offset = 1
    ReadCODE  (<b>DataTable</b> + Offset), DataWord
    
    LCDOUT  $FE, 1, HEX4 DataWord
    Or for 16-bit WORDS on an 18F, use this ReadCODE statement.
    Code:
    ReadCODE  (DataTable + (Offset<<1)), DataWord
    Absolutely HUGE tables can be created in a small fraction of the space used by LOOKUP2.

    Note: Only PIC chips that can access their own Flash memory can use ReadCODE.
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;And, DW only works properly in MPASM, not PM.
    (thanks BigWumpus).

    <a name="VAR"></a><hr>Using EXT with Variables ...

    <pre>@MyVar = 20h<br>MyVar VAR BYTE EXT</pre>&nbsp;&nbsp;... is similar to ...

    <pre>MyVar VAR BYTE $20</pre>With the exception that PBP doesn't know about it. &nbsp; Reading or writing to the EXT MyVar will use the RAM location at $20, but PBP will assign another variable there since it doesn't know about it yet. &nbsp; So trying to use EXT to create NEW variables is pretty much useless. &nbsp;
    However, using EXT to create variables that reference other variables, or other RAM/SFR locations, is Priceless.

    This example shows how to use a 16-bit timer value as if it were just another word.
    Code:
    @Timer1 = TMR1L
    Timer1  VAR  WORD EXT
    With that, and the TimerConst example shown above. Re-Loading the timer is as simple as this ...
    Code:
    T1CON.0 = 0  ; stop timer
    Timer1 = Timer1 + TimerConst
    T1CON.0 = 1  ; turn timer back on
    You should stop the timer before reading or writing it's value, or it might overflow in-between the Low and High bytes.
    Note: Do NOT enable RD16 when using a Timer value as a WORD. (T?CON.7) (Thanks Bruce!)
    On an 18F, Timer0 is always RD16. You cannot use Timer0 as a WORD value.

    Another example along the same lines ...
    Code:
    @Capture = CCPR1L
    Capture   VAR  WORD EXT
    Then the whole capture value can be used as a Word variable.

    But here's the good stuff.

    <a name="TYP"></a><hr>Typcasting Variables inside of Arrays

    Using the EXT modifier, you can create combinations of Word and Byte variables that point to locations inside of Array variables.

    Let's say that you received a "Packet" of information from some other device using the STR function of HSERIN. But the problem is that it's not all byte sized data, even though it was stored in a byte-sized array.
    It's received 8 bytes and you need to separate them into different WORD and BYTE sized variables.

    <pre> 01 AE 7A 8B 00 00 10 2C<br> \ / \ / \ /<br> Arg0 Arg1 Arg2 Arg3 Arg4<br> WORD BYTE WORD WORD BYTE</pre>

    With a few EXTernal variables, you can Parse the entire Packet, without a single WORD of program space.
    Code:
    MyArray  VAR BYTE[8] BANK0
    ASM
    Arg0 = _MyArray        ; word
    Arg1 = _MyArray + 2    ; byte
    Arg2 = _MyArray + 3    ; word
    Arg3 = _MyArray + 5    ; word
    Arg4 = _MyArray + 7    ; byte
    ENDASM
    
    Arg0   VAR  WORD  EXT
    Arg1   VAR  BYTE  EXT
    Arg2   VAR  WORD  EXT
    Arg3   VAR  WORD  EXT
    Arg4   VAR  BYTE  EXT
    Now you simply read the Packet into the Array, and each Arg variable will have the correct value in it. This is also much quicker to run, since it doesn't need to do all the Array indexing that PBP would normally do.

    If there are multiple types of packets being received, you can just create another set of variables pointing to the same Array space, with different combinations of bytes and words. Since they don't use any program space, you can make as many as you want. &nbsp; Sure beats using 4 or 5 different HSERIN statements for the different packet types.

    And of course, it works the other way too. By setting the Arg variables first, you can easily SEND the "pre-formatted" packet with the STR function.

    If you get an "Unable to fit variable ____ in requested bank x" warning, change the BANK0 modifier to BANK1 or another bank that has room.
    The entire array must be in a single bank for this technique to work. (Thanks Charles_Leo)

    <a name="LIM"></a><hr>Limitations of EXT

    Since the value of an EXTernal constant/variable/label isn't known until the program is being Assembled. PBP cannot use the values itself.
    Code:
    @MyConstant = 1234h
    MyConstant  CON  EXT
    
    AnotherCON  CON  MyConstant + 100  ; This will fail
    
    MyArray  VAR BYTE[MyConstant]      ; This will fail
    Both of the lines above will fail since PBP needs to know what the value is for those statements when it's compiling. But they won't be known till it's assembled.

    <hr width=100>
    This also holds true throughout the program. &nbsp;The value of an EXT var/con/label must be declared prior to anything trying to use it. If a label occurs after the current point in the program, you should get an error, but it's not always evident what the problem is.
    Sometimes it will give a "Symbol not previously defined" error. Other times you might get an "Address label duplicated or different in second pass" error.

    The error will indicate the name of the Label, so it's not too hard to figure out.

    <hr width=100>
    You can't use EXT with Arrays. That would just be too convenient.
    <br>

    <a name="ARRAY"></a><hr>Arrays within Arrays

    Along with BYTEs and WORDs, you can have Arrays as part of the "Packet". It's just up to you to make sure that everything fits properly.

    Perhaps you need to send a series of A/D samples along with the previous Packet to a VB program on a PC.
    By Mapping a WORD Array inside the BYTE array, the Sample data automatically becomes part of the Packet.

    This adds a 10 WORD array to the previous Packet. And just for good measure I'll put another BYTE variable after it for a CheckSum.
    Code:
    MyArray  VAR BYTE[29] BANK0
    ASM
    Arg0 = _MyArray          ; word
    Arg1 = _MyArray + 2      ; byte
    Arg2 = _MyArray + 3      ; word
    Arg3 = _MyArray + 5      ; word
    Arg4 = _MyArray + 7      ; byte
    Samples = _MyArray + 8   ; 10 WORD array (20  bytes)
    ChkSUM  = _MyArray + 28
    ENDASM
    
    Arg0    VAR  WORD  EXT
    Arg1    VAR  BYTE  EXT
    Arg2    VAR  WORD  EXT
    Arg3    VAR  WORD  EXT
    Arg4    VAR  BYTE  EXT
    Samples VAR  WORD  EXT   ; 10 WORD array
    ChkSUM  VAR  BYTE  EXT
    Note here that you don't need to specify the array size for the Samples variable. But you do have to leave enough room for it in the array.
    And if you get an "Unable to fit variable ____ in requested bank x" warning, change the BANK0 modifier to BANK1 or another bank that has room.
    The entire array must be in a single bank for this technique to work.

    Now you can just take the A/D samples and store them in the WORD array...(which is really in the MyArray).
    Code:
    FOR X = 0 to 9
        ADCIN  0, Samples(X)
    NEXT X
    Calculate a CheckSum for the whole Packet...
    Code:
    ChkSUM = 0
    FOR X = 0 to 27
        ChkSUM = ChkSUM ^ MyArray(X)
    NEXT X
    And with very little program space used, you have a Complete Packet with 5 WORD or BYTE variables, 10 word sized A/D samples and a checksum for data integrity, ready to send with ...
    Code:
       HSEROUT ["Header", STR MyArray\29]
    And, once again, it's bi-directional. You can receive the Packet of Samples just as easily, with ...
    Code:
    HSERIN [wait("Header"), STR MyArray\29]
    This article was originally published in forum thread: The EXT (external) modifier. started by Darrel Taylor View original post