Proc and Org Address + CData cause Assembler error 118 - Overwritting address?

Started by trastikata, May 05, 2021, 11:30 AM

Previous topic - Next topic

trastikata

Hello all,

I can't find a reason as to why in some programs where Proc's are used when adding Org Address + CData will cause Assembler error and won't compile?

The following example won't compile. If I only remove the Org directive and keep the CData, it will compile! The code is short and for sure won't override the Org address at the end of the memory.  However the second simple code will compile, although Org directive is present.

So it seems that the presence of "Proc" and "Org Address" in the same program will cause Assembler error, regardless the size of the file (certainly no memory overwriting), however not always.

Device = 18F26J50

Declare Xtal = 20

Declare Reminders = OFF
Declare FSR_CONTEXT_SAVE = On
Declare LABEL_BANK_RESETS = On
Declare Optimiser_Level = 3
Declare Dead_Code_Remove = On
Declare Float_Display_Type = Large
Declare All_Digital = True

Dim Lat As Float            'Destination Point Latitude
Dim Lon As Float            'Destination Point Longitude
Dim LatOrg As Float         'Point of origin Latitude
Dim LonOrg As Float         'Point of origin Longitude

Dim Distance As Dword       'Distance between origin and destination
Dim Bearing As Word         'Bearing between origin and destination
Dim Compass As String * 3   'Compass heading

Main:
    Clear : DelayMS 300
   
    'Load some values for demonstration
    'To convert from SDWord to Decimal Degrees use procedure SIntToDeg
   
    'Destination Point Latitude NMEA: 2852.7780 or 28527780 becomes 28.87963333 degrees
    Lat = SIntToDeg(28527780)
    'Destination Point Longitude NMEA: 2759.0199 or 27590199 becomes 27.983665 degrees
    Lon = SIntToDeg(27590199)
    'Point of origin Latitude NMEA: 2952.7780 or 29527780 becomes 29.87963333 degrees
    LatOrg = SIntToDeg(29527780)
    'Point of origin Longitude NMEA: 2753.0186 or 27530186 becomes 27.88364333 degrees
    LonOrg = SIntToDeg(27530186)
   
   
    'Call GPSDistance procedure, passing the coordinates
    'to calculate the distance between the point of origin and the destination point
    'Distance = 110663 meters
    Distance = GPSDistance(Lat, Lon, LatOrg, LonOrg)
   
    'Call GPSBearing procedure, passing the coordinates
    'to calculate the bearing between the point of origin and the destination point
    'Bearing = 175 degrees
    Bearing = GPSBearing(Lat, Lon, LatOrg, LonOrg)
   
    'Call BearingToCompass procedure, passing the Bearing as degrees
    'to return the heading in compass terms
    'Compass = " S "
     Compass = BearingToCompass(Bearing)
   
End

'This proceudre will convert the NMEA coordinates from SDword To Decimal Degrees     
Proc SIntToDeg (Coordinate As SDword), Float
    Dim TempFloat0 As Float
    Dim TempFloat1 As Float
    Dim TempWord As Word
    Dim TempDWord0 As Dword
    Dim TempDWord1 As Dword
   
    TempDWord0 = Abs(Coordinate)
    TempFloat0 = TempDWord0 / 1000000
    TempWord = TempFloat0
   
    TempDWord0 = Abs(Coordinate)
    TempDWord1 = TempDWord0 // 1000000
    TempFloat0 = TempDWord1 / 600000
   
    TempFloat1 = TempWord + TempFloat0
   
    If Coordinate < 0 Then
        Result = -TempFloat1
    Else
        Result = TempFloat1
    EndIf
EndProc   

'This procedure converts degrees to radians
Proc DegToRad (Degree As Float), Float
    Dim TempFloat0 As Float
   
    TempFloat0 = Degree * 0.0174532925199
   
    Result = TempFloat0
EndProc

'This procedure calculates the distance, given the
'coordinates of the destination and the point of origin
'returning the distance as 32b integer in meters
Proc GPSDistance (Lat As Float, Lon As Float, LatOrg As Float, LonOrg As Float), Dword
    Dim TempFloat0 As Float
    Dim TempFloat1 As Float
    Dim TempFloat2 As Float
    Dim TempFloat3 As Float
   
    TempFloat0 = Lat - LatOrg               
   
    TempFloat1 = Lon - LonOrg
    TempFloat2 = DegToRad(LatOrg)
    TempFloat3 = Cos(TempFloat2)           
    TempFloat2 = TempFloat1 * TempFloat3   
    TempFloat1 = TempFloat0 * TempFloat0   
    TempFloat0 = TempFloat2 * TempFloat2   
    TempFloat2 = TempFloat1 + TempFloat0   
    TempFloat0 = Sqr(TempFloat2)
    TempFloat1 = 110250 * TempFloat0 
   
    Result = TempFloat1
EndProc

'This procedure calculates the bearing, given the
'coordinates of the destination and the point of origin
'returning the bearing as 16b integer in degrees
Proc GPSBearing (Lat As Float, Lon As Float, LatOrg As Float, LonOrg As Float), Word
    Dim TempFloat0 As Float
    Dim TempFloat1 As Float
    Dim TempFloat2 As Float
    Dim TempFloat3 As Float
   
    TempFloat0 = Lat / 2
    TempFloat1 = TempFloat0 + 45
    TempFloat0 = DegToRad(TempFloat1)       
   
    TempFloat1 = LatOrg / 2
    TempFloat2 = TempFloat1 + 45
    TempFloat1 = DegToRad(TempFloat2)       
   
    TempFloat2 = Tan(TempFloat0)           
    TempFloat0 = Tan(TempFloat1)           
    TempFloat1 = TempFloat2 / TempFloat0
    TempFloat0 = Log(TempFloat1)            'x
   
    TempFloat3 = DegToRad(Lon)
    TempFloat2 = DegToRad(LonOrg)
    TempFloat1 = TempFloat3 - TempFloat2    'y
   
    'atan2(y,x)
    If TempFloat0 > 0 Then                              'x > 0
        TempFloat3 = TempFloat1 / TempFloat0
        TempFloat2 = ATan(TempFloat3)
    ElseIf TempFloat0 < 0 And TempFloat1 >= 0  Then     'x < 0 and y >= 0
        TempFloat2 = TempFloat1 / TempFloat0
        TempFloat3 = ATan(TempFloat2)     
        TempFloat2 = TempFloat3 + 3.141592653582
    ElseIf TempFloat0 < 0 And TempFloat1 < 0  Then      'x < 0 and y < 0
        TempFloat2 = TempFloat1 / TempFloat0
        TempFloat3 = ATan(TempFloat2)     
        TempFloat2 = TempFloat3 - 3.141592653582 
    ElseIf TempFloat0 = 0 And TempFloat1 > 0  Then      'x = 0 and y > 0 
        TempFloat2 = 1.570796326791
    ElseIf TempFloat0 = 0 And TempFloat1 < 0  Then      'x = 0 and y < 0 
        TempFloat2 = -1.570796326791 
    ElseIf TempFloat0 = 0 And TempFloat1 = 0  Then      'x = 0 and y = 0 
        TempFloat2 = 0 
    EndIf

    TempFloat0 = TempFloat2 * 57.2957795130
   
    Result = TempFloat0
EndProc


'This procedure will select the heading in terms
'of compass direction, given bearing as degrees
Proc BearingToCompass (Bearing As Word), String * 3
    If Bearing >= 349 Or Bearing < 11 Then
        Result = " N "
    ElseIf Bearing >= 11 And Bearing < 34 Then
        Result = "NNE"
    ElseIf Bearing >= 34 And Bearing < 56 Then
        Result = "NE "
    ElseIf Bearing >= 56 And Bearing < 79 Then
        Result = "ENE"
    ElseIf Bearing >= 79 And Bearing < 101 Then
        Result = " E "
    ElseIf Bearing >= 101 And Bearing < 124 Then
        Result = "ESE"
    ElseIf Bearing >= 124 And Bearing < 146 Then
        Result = "SE "
    ElseIf Bearing >= 146 And Bearing < 169 Then
        Result = "SSE"
    ElseIf Bearing >= 169 And Bearing < 191 Then
        Result = " S "
    ElseIf Bearing >= 191 And Bearing < 214 Then
        Result = "SSW"
    ElseIf Bearing >= 214 And Bearing < 236 Then
        Result = "SW "
    ElseIf Bearing >= 236 And Bearing < 259 Then
        Result = "WSW"
    ElseIf Bearing >= 259 And Bearing < 281 Then
        Result = " W "
    ElseIf Bearing >= 281 And Bearing < 304 Then
        Result = "WNW"
    ElseIf Bearing >= 304 And Bearing < 326 Then
        Result = "NW "
    ElseIf Bearing >= 326 And Bearing < 349 Then
        Result = "NNW"
    EndIf
EndProc

Org 63488
    CData $CD

Device = 18F26J50

Declare Xtal = 20

Declare Reminders = OFF
Declare FSR_CONTEXT_SAVE = On
Declare LABEL_BANK_RESETS = On
Declare Optimiser_Level = 3
Declare Dead_Code_Remove = On
Declare Float_Display_Type = Large
Declare All_Digital = True

Main:
    Clear
    MyProc(5205060)
   
End
   
Proc MyProc (Temp As SDword), Float
    Result = temp
EndProc 


Org 63488
    CData $CD

top204

Org is an assembler directive that moves everything to that address.

Procedures are created and placed after the main code, so they will be placed after the Org directive, meaning they will not fit in the device because it does not have enough Flash memory.

There is nothing that can be done about this because Org has dominance when it is placed in a BASIC program because it is passed directly to the assembler, and the assembler will always follow its address.

The Org directive is something that I placed in the compiler from the start, back in approx 2002, when code was perfectly flat and the devices around then were small, with very little RAM and very little code memory, and has been removed from the Positron8 compiler's manual.

If writing a flat program, it can be used. i.e. If writing a bootloader etc, but not with procedures.

trastikata

Quote from: top204 on May 05, 2021, 12:50 PMProcedures are created and placed after the main code, so they will be placed after the Org directive, meaning they will not fit in the device because it does not have enough Flash memory.

Les, I am using the top block in some devices without EEPROM for storing user data that can be changed from time to time by the user, thus the need to erase the entire flash block.

I think of one way to avoid collision is to keep the program flat using only Subroutines. Any other suggestions of how to approach the "problem" in those cases?

Thanks.

top204

The HEF principle is a very, very, very stupid mechanism on devices, and the devices that have it should, in my opinion, be avoided at all times if non-volatile storage is required in a program's operation. :-)

I've just thought of a way of fooling the compiler into using the Edata tables as HEF tables at a specific address:

    Declare EEPROM_Address = 63488
    Declare EEPROM_Size = 128
'
' The Edata values will be placed in Flash memory at the address stated in the EEPROM_Address declare
'

    EData 1,2,3


But remember, the Eread and Ewrite commands cannot be used for the data because they are not in EEPROM. It is using one of the, unofficial and undocumented, mechanisms that I placed in the compiler when I was first writing and testing it back in the early 2000s, so I could move EEPROM around for different devices.

With the above code, open the assembler window (F2 button) and at the end of the assembler listing, you will see:

    org 0XF800
    db 1,2,3


Which is the data from the Edata table but at a Flash memory address, and it will not interfere with the compiler's code because the compiler was in charge of gathering it and placing it as if it was placing EEPROM data, after the mnemonic code of the program.

The address of the flash memory data area can be used to read and write to it, but remember, the flash data has to be erased as a block before it can be re-written as a block, and sometimes the erase and write blocks are different sizes, and each device family has different amounts of erase and write block sizes for some inexplicably stupid reason!

tumbleweed

Here's another way using ORG (although I don't know what other issues this might bring about, so beware)

Device = 18F26J50

Declare Xtal = 20

Declare Reminders = OFF
Declare FSR_CONTEXT_SAVE = On
Declare LABEL_BANK_RESETS = On
Declare Optimiser_Level = 3
Declare Dead_Code_Remove = On
Declare Float_Display_Type = Large
Declare All_Digital = True

Main:
    Clear
    MyProc(5205060)
   
End
   
Proc MyProc (Temp As SDword), Float
    Result = temp
EndProc

'-------------------------------
' locating CData in upper flash
'-------------------------------
' save current PC
Asm-
save_CData_PC Set $
EndAsm
' place CData
Org 63488
    CData $CD
' restore PC (procs will follow...)
Asm-
Org save_CData_PC
EndAsm

trastikata

Quote from: top204 on May 05, 2021, 01:31 PMI've just thought of a way of fooling the compiler into using the Edata tables as HEF tables at a specific address:

Excellent, thank you. All I need is to store few calibration parameters, which can be changed not so often if the calibration procedure is ran. I too find it impractical erasing blocks of 1024 to write few bytes but that's what it is.

I am updating the firmware for one such device and decided to use Proc's, however I stumbled on the previously mentioned issue. This will allow me to use Procedures and still keep to HEF devices, some of which offer very nice peripherals at low $$.

John Lawton

Quote from: tumbleweed on May 05, 2021, 01:53 PMHere's another way using ORG (although I don't know what other issues this might bring about, so beware)

I've just the above code in Positron V4.0.0 Build 9 and get the dreaded message:
"*** The assembler's Org directive cannot be used in a program that has procedures within it! ***"

top204


John Lawton

Thanks, but when MPASM is retired, what then?

Atomix fixed the code here: https://protoncompiler.com/index.php/topic,443.msg4741/topicseen.html#msg4741 post #53

John

Stephen Moss

Quote from: trastikata on May 05, 2021, 02:04 PMExcellent, thank you. All I need is to store few calibration parameters, which can be changed not so often if the calibration procedure is ran. I too find it impractical erasing blocks of 1024 to write few bytes but that's what it is.
I may be barking up the wrong tree here as I don't recall having done that before but it sounds like you are erasing the entire block so that can store the data at the same address every time.
If that is the case could you not keep the location of the current address in a variable, and then update to the next address on the next write, then you would only have to erase the block once it is full and reset the read/write address to base address of the block.

The issue would then be loosing the current address on a restart, but perhaps that could be found using a simple routine that reads every applicable address in sequence until it find the default (erase) value then you know the pervious address is the one containing the latest calibration values

top204

Remember, the Org directive is an assembler directive and not a compiler directive, so the compiler has absolutely no control over it.

It can be used with flat programming, but not with procedural programming, and not when using a Linker after the assembler, which the new assembler forces upon the user. :-(

If writing to a specific area of flash, why not store what is to be written in EEPROM, or a flash memory table, then write that to the area when required? What is in the area does not need to be stored there, and as long as the microcontroller initially powers up, it can be written, then jump to the main part of the actual program once the write is complete.

trastikata

Quote from: Stephen Moss on Oct 27, 2021, 07:47 AMIf that is the case could you not keep the location of the current address in a variable, and then update to the next address on the next write, then you would only have to erase the block once it is full and reset the read/write address to base address of the block.

In FLASH you write in blocks, so this complicate things. Then you have to make a complicated algorithm with redundancy to keep track of the current position and update it every time you write, but then you have the issue of writing in blocks. Further on you will get eventually to the point where you'll have to erase the 1024 block anyway ... so better to keep everything simple.