How to address main memory in PASM?

RalfStRalfSt Posts: 19
edited 2019-07-08 - 14:16:10 in Propeller 1
Hi,

I'm trying for hours to access main memory and become more and more confused.

I try to access the main memory like shown in the following code:
DAT
        org 0
test    mov     :addr,  #mainmem
        movd    :addr,  #mainmem >> 9
        rdlong  :data,  :addr
        jmp     #test

:addr   res 1
:data   res 1
        fit

This kind of code sometimes works, sometimes not. It depends on the position of the instructions. So I think I'm accessing memory, but not the one I want. I guess I overwrite some instructions.
Now I figured out that the addresses of #mainmem does not make much sense.
I declared the #mainmem as follows:
PUB main
    cognew(@blink, 0)
    ' … more cogs started …
    cognew(@test, 0)
    cogstop(cogid)

DAT mainmem long 0     ' For testing
DAT other   long 0     ' For testing

Looking at the addresses now tells me, that #mainmem is 0x00000000 and #other is 0x00000001.
Why is the distance between the two longs only 1?
And why starts the address as 0?

Is this the right way to declare "global" variables? In spin I've to use VAR, but then the symbols are not available to the assembler.

ps.: I use OpenSpin as assembler

Comments

  • I've found that the best way to address main (HUB) memory in PASM is to pass the address as a parameter from the Spin program:
    PUB main
       cognew(@test, @mainmem)
    DAT
    mainmem long 0
    
        org 0
    test
        mov addr, par  ' address is passed as parameter
        rdlong data, addr
    data res 1
    addr res 1
    
    This is because there's no way in "standard" PASM to get the HUB address of a symbol; all label references are translated to their COG (32 bit) addresses. That's why there is only "1" between the two longs in your earlier sample, and why they start at the wrong address.

    In some non-standard assemblers (bstc, fastspin, I think homespun) there is a triple @ operator that does give the absolute HUB address of a symbol. The forum messes up the at sign, so I'm going to insert spaces between them, but in the real program the 3 at signs should come immediately together:
        org 0
    test
        mov addr, mainmem_ptr
        rdlong data, addr
    mainmem_ptr long @ @ @ mainmem ' contains HUB address of mainmem
    addr res 1
    data res 1
    
  • Annoyingly, you can not directly use a Hub variable in PASM. (This is because if you include the same object multiple times, the code is only included once, only the VAR blocks are duplicated. Why DAT variables are also impacted by this is beyond me)

    There are multiple correct ways of doing it, here are some of them:
    1. Pass a pointer to one variable in through the PAR register (= the second parameter to cognew)
    PUB main
        cognew(@test, @mainmem)
    
        cogstop(cogid)
    
    VAR
    long mainmem     ' For testing
    DAT
            org 0
    test    
            rdlong  data,  par
            jmp     #test
    data                res 1
            fit
    
    Advantages:
    - No code patching => no issues when multiple cogs are being started
    Disadvantages:
    - Only one pointer - If you need more you need to either use multiple "adjacent" memory locations (= an array or multiple variables defined in a particular order) or add an additional layer of indirection

    2. Patch in the pointers before starting the cog
    PUB main
        mainmem_ptr := @mainmem
        cognew(@test, 0)
    
        cogstop(cogid)
    
    DAT mainmem long 0     ' For testing
    DAT
            org 0
    test    
            rdlong  data,  mainmem_ptr
            jmp     #test
    
    mainmem_ptr long 0
    data                res 1
            fit
    

    Advantages:
    - Multiple pointers without any mess
    Disadvantages:
    - Can cause issues when starting multiple cogs in quick succession (IIRC a cog needs roughly 8192 cycles to start up and fill its memory). When in doubt, add a waitcnt after the cognew

    3. Use manually allocated memory at the end of RAM (here: last LONG at $7FFC)
    CON
    _FREE = 1 ' 1 LONG reserved at end of RAM. I think this must be in the top level object. You can do without it, but the compiler won't warn you
    _STACK = 128 ' Also reserve some stack (otherwise _FREE isn't very useful)
    PUB main
        cognew(@test, 0)
    
        cogstop(cogid)
    DAT
            org 0
    test    
            rdlong  data,  mainmem_ptr
            jmp     #test
    
    mainmem_ptr long $7FFC
    data                res 1
            fit
    
    Advantages:
    - Smaller code
    - Nicer in an application that consists of mostly/only custom PASM
    Disadvantages:
    - Is a huge mess to keep track of
    - Not suited for "plug-and-play" reusable Spin objects
  • @Wuerfel_21,

    This is very helpful information.

    I'm writing an XMM application in C using the Catalina compiler. This is due to a bunch of menus and stuff that I can't fit into CMM mode.

    But I also need to run another cog to handle traffic management to a GPS receiver and some motors.

    Unfortunately due to the caching issue within XMM, Catalina is unable to start another cog containing C code. But it can start another cog to run Spin or Assembly code.

    So I'm thinking about writing the traffic manager code in Assembly.

    I'm using the SMALL XMM memory model, meaning that all of the variables I'm using are contained within HubRam.

    I'm now wondering if I create a structure in C that contains all of the variables the traffic manager needs, and then pass a pointer to that structure to the cog running the Assembly code, if it will work. At first glance it seems that it should.

    Thanks for giving me some ideas on how to do this...



  • Thank you very much, Wuerfel_21

    Method 2 works perfectly for my purpose.
    I know exactly how many memory I need for each buffer and I only start one instance/cog for each code "segment".
    So I can simply assign all memory addresses first and then start the cogs.
Sign In or Register to comment.