Shop OBEX P1 Docs P2 Docs Learn Events
Absolute address in assembly — Parallax Forums

Absolute address in assembly

BuddhaBuddha Posts: 25
edited 2006-07-19 01:41 in Propeller 1
Is it possible to obtain the absolute address for a location in Hub RAM in assembly?

In Spin, there is the "Object Address Plus" operator that returns the actual location as a constant, but there doesn't seem to be a method to get that same information directly in assembly.

As an example case, let's say I want to write an assembly program that will restart itself into all of the COGs:

PUB Main

   coginit(0, @Entry, 0)        ' Restart the first COG to run assembly code

DAT

Entry   MOV Startup, #@Entry    ' Load startup address
        SHL Startup, #4         ' Shift into address field for COGINIT
        OR  Startup, #8         ' Set COG # > 7 so a new COG will be started
        COGINIT Startup         ' Initialize a new COG to also run this same code

:Loop   ADD Counter, #1         ' Increment the counter      

        JMP #:Loop              ' Loop forever

Counter RES 1                   ' Counter variable

Startup RES 1                   ' New COG startup variable



The problem with this code is the MOV statement, which will only load the offset address of the assembly code, and not get the absolute address of the initial instruction. Is there a way to get the real address in assembly without using Spin code?

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-16 23:24
    Look at the description of the "@@" operator in the spin reference. It needs to be executed at run-time rather than being compiled into the assembly as a constant. One way to do it is to define a word (or a long) variable in the DAT for your assembly program and, in your Main program, do "entryAddr := @@ entry" just before you do your first coginit. In your assembly, you'd have "Entry MOV Startup,entryAddr". There are more concise ways of doing this, but this one is simple.
  • BuddhaBuddha Posts: 25
    edited 2006-07-17 01:27
    Mike, thanks for the response, but it seems like there should be a way to get the address of a piece of code in RAM, because the code shouldn't be moving around at runtime. [noparse]:)[/noparse]

    It makes sense that instanced data would have a specific address at runtime, but code loaded from the EEPROM isn't going to be moving around in Hub RAM... So it seems like there should be a compile-time method to determine absolute addresses.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-17 01:53
    You're right. I suspect it has to do with the implementation of the compiler. I think it generates SPIN code that's object/method relative, then there's a separate phase like a linker that eliminates duplicate objects and unreferenced methods, puts what's left in some kind of order, and fixes up references. For whatever reason, there was no fix up done for this kind of reference. It's not impossible to do, just wasn't a priority.
  • BuddhaBuddha Posts: 25
    edited 2006-07-18 23:27
    Is anyone from Parallax willing to comment on this? Is this a feature we can expect to see in a future release of the IDE and/or is there some reliable way to get around the limitation?
  • David BDavid B Posts: 591
    edited 2006-07-19 00:51
    I've also been experimenting, trying to learn this stuff.

    Here's a trick I found. Make an obvious value, like all A's, followed by something you're interested in.

    (spin code)

    DAT
    org

    tostart
    some assembly code...


    register0 long $AAAAAAAA
    register1 long @tostart


    Compile, then go to the Propeller tool menu "Run" -> "Compile Current" -> "View Info". You'll see your AAAAAAAA, followed by the value that the compiler assigned to your register1. If you add a NOP before your "tostart" code, you'll see the value of your register1 increase by 4, as it should.

    But I think you have to add $10 to the contents of your variable if you want the absolute hub RAM address, because it looks like there are 16 bytes ($10, 4 longs) of header info that come first in hub RAM. (The first long in the header is clkfreq)

    It's not that hard to match up those hex values with your assembly, and see exactly how your assembly is coded, using the assembly tables in the documentation, like this: (using the Windows calculator to convert between hex and binary)

    0a24 a0fc = mov start, #@entry = A0FC0A24 = 101000 0011 1111 000000101 000100100
    mov immed dest src

    With that said, I'm having all kinds of trouble getting an assembly program to start an assembly cog. Spin starting a Spin cog works well, and Spin starting an assembly cog works well, but I just can't figure out how to get assembly to reliably start an assembly cog.

    Here's some tests I've been working with. This is supposed to start assembly cogs, which then display an LED corresponding to their cog id. From Spin, the assembly works great. But not from assembly. As I play around with values, I do occasionally get working results, but nothing reliable.

    Can anyone see what's wrong here?

    David


    ' cogstart.spin
    '
    ' Propeller board with individual segments of 7-segment LED display connected to IO pins 0-6
    '
    ' works perfectly with spin starting assembly cogs
    ' only semi-working with spin starting assembly which starts assembly cog
    '

    _clkmode = xinput + pll16x ' use external clock multiplied by 16 -> 64 mHz
    _xinfreq = 4_000_000 ' external rate is 4 MHz


    PUB Main

    {
    ' working demo of spin starting asm cog. (Starting cog zero wipes out this Spin program)
    coginit(1, @tostart, 0)
    coginit(2, @tostart, 0)
    coginit(3, @tostart, 0)
    coginit(4, @tostart, 0)
    coginit(5, @tostart, 0)
    coginit(6, @tostart, 0)
    coginit(7, @tostart, 0)
    coginit(0, @tostart, 0)
    }

    ' working demo which automatically selects all available cogs (leaving out cog 0),
    ' and so will light all but the zeroeth segment of the display:

    ' repeat
    ' cognew(@tostart, 0)

    ' semi-working demo of assem starting assem cog...
    ' start assembly cog 1

    coginit(1, @starter, 0)


    repeat ' if we ever get this far. just pause.


    DAT
    org
    nop
    tostart
    cogid id ' Get the cogid: a value from 0-7
    shl bitmask, id ' build a bitmask
    or dira, bitmask ' select segment based on cog id
    or outa, bitmask ' set that bit -> turn on one segment of 7-segment display (0-6)
    :loop jmp #:loop ' them wait.

    id long $0
    bitmask long $1

    '
    org

    starter

    ' mov register1, #@tostart
    shl register1, #4
    ' andn register1, #%1111 ' try lots of variations
    ' or register1, #$0 ' on manipulating register1...
    ' or register1, #$1
    or register1, #$2
    ' or register1, #$4
    ' or register1, #$A

    coginit register1
    nop
    coginit register1
    nop
    ' coginit register1

    ' coginit register3

    :loop jmp #:loop

    ' configure coginit argument:
    ' if bit 3 is 1 then bits 0-2 specify cog.
    ' bits 4-18 specify address in Hub RAM of assembly routine to start.

    'register1 long %10_0000 + %0010
    register0 long $AAAAAAAA
    register1 long @tostart

    'register2 long %110 '%10_0000 + %0011
  • cgraceycgracey Posts: 14,133
    edited 2006-07-19 01:41
    This could be done, but the compiler is not tracking data in the way that it would need to to make this easy. All objects are their own entities with no absolute addresses within them, only relative addresses. At run-time it's easy to patch an assembly program. Just do something like 'patch := @something', where patch is a long, perhaps, that is a part of an assembly program. 'Something' could be a data segment or variable.·
    Buddha said...
    Is anyone from Parallax willing to comment on this? Is this a feature we can expect to see in a future release of the IDE and/or is there some reliable way to get around the limitation?
    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔


    Chip Gracey
    Parallax, Inc.
Sign In or Register to comment.