Shop OBEX P1 Docs P2 Docs Learn Events
What does @ do in PASM ? — Parallax Forums

What does @ do in PASM ?

heaterheater Posts: 3,370
edited 2008-04-18 13:01 in Propeller 1
What does @ do in PASM ?

I've got a program that uses a simple Large Memory Model Virtual Machine. So I have to set the LMM's program counter to the start of the LMM program. Obvious thing for me is to just have a LONG that holds the address of the LMM program and then move that into the program counter "mov lmm_pc, target". So now the adress long (target) needs initializing so I write "target long @lmm_prog".
This does not work. The @ does not seem to give the correct address.

If I initialize target from spin prior to launching the PASM into the cog all is well "target := @lmm_prog". Here the @ works as expected.

This is OK if there is only one LMM program but a pain if there are many small LMM functions in a table driven system. A table of targets.

Below is an out line of what I'm trying to do (Notice how posting this screws up the indentation which is why I dislike the use of indenting for code structuring)



DAT
                        org     0
enter
                        .
                        . some code here
                        .
                        mov     lmm_pc, target           'Set LMM's program counter to some program address
                        jmp     #lmm_start                 'Start the LMM virtual machine
                        .
                        .more code here
                        .

' This is a reversed execution order LMM virtual machine (Which, of course, starts near the bottom:)
lmm_ins_0               nop               
                              rdlong  lmm_ins_1, lmm_pc
                              sub     lmm_pc, #7
lmm_ins_1               nop
lmm_start                rdlong  lmm_ins_0, lmm_pc
                              djnz    lmm_pc, #lmm_ins_0


lmm_pc                   long    0
target                      long    @lmm_prog    '<--------- THIS DOES NOT WORK AS I EXPECT

this                         long    0
that                         long    0

                              fit

lmm_prog               mov     this, that            'Lage memory model program
                             . 
                             . more LMM code here
                             .
                             jmp #some_instruction_in_native_code



▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
For me, the past is not over yet.

Comments

  • hippyhippy Posts: 1,981
    edited 2008-04-18 00:27
    ptr long @somewhere

    When used in PASM the address put in the long is the hub address of 'somewhere'
    minus the base address of the object the PASM code is included with. For a top-level
    object the base address is $0010, for others ... it has to be calculated ( brain too
    warped at present to even start describing that ).

    Changing ...

    mov lmm_pc, target
    jmp #lmm_start

    To ...

    mov lmm_pc, target
    add lmm_pc,#$10
    jmp #lmm_start

    should fix this one. I find the easiest way is to pass the address of the LMM code
    to execute in via the PAR in the CogNew because that way PAR points to the
    genuine start address.

    If you have "long @somewhere" and "CogNew(@enter,@somewhere)", the difference
    should give the base address, but normally I wouldn't have the "long @somewhere"
    within the LMM.

    And a big hint : Don't try reverse LMM operations until you've got the traditional LMM
    working as it's a nightmare to debug -- trust me, I speak with experience !
  • heaterheater Posts: 3,370
    edited 2008-04-18 05:17
    As you may have guessed I'm using LMM in my 8080/5 emulator. At the moment 17 of the opcodes are handled by 11 LMM code, err, handlers.
    My opcode dispatch table entries contain either the COG address of the native PASM handlers or the HUB address of the LMM handlers, these are indicated by having the top bit of the address set. The main instruction dispatch loop checks that top bit and jumps to a native handler or sets the LMM PC and jumps to the VMM accordingly.

    So there are 17 addresses in the dispatch table that need initializing from SPIN (would not like to do it from PASM as it eats COG space) something like this:

    do_daa_addr := @do_daa | $8000
    do_xchg_addr := @do_xchg | $8000
    etc
    etc

    This is a pain. Given that there is only one DAT section per object the compiler knows the address at compile time so @ should work fine or perhaps @@.

    Anyway way from what you say I'm stuck with this run time setting. I guess I could calculate the difference as you show but then I would still have to add it to all entries at start up - no gain.

    Atually I'm using a reverse LMM and its no more problem than the forward one BUT only because my handlers are short simple straight line code, no jumps or calls within LMM code, only calls to native functions. I just write the handler after the fit statement just like normal code.
    The code revesal is done in spin at start up and the dispatch table set up goes like this:

    do_daa_addr := (@lmm_end + @lmm_begin - @do_daa) | $8000

    It's easy to move handlers from PASM to LMM and vice-versa.

    Thanx.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    For me, the past is not over yet.
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-04-18 07:35
    The compiler doesn't actually know the address when it compiles the machine code. The compile sequence is something like this
    1. Get constants in CON blocks
    2. Compile sub objects (could be first)
    3. Make the method table with blank entries? (this may be done later, not sure, but needs to be before 6)
    4. Assemble code in DAT block
    5. Allocate VAR space? (this could be done before this, not sure, but needs to be before 6)
    6. Compile PUB and PRI blocks including constant expressions in these
    7. Fixup method table
    8. Put sub objects in binary file in order

    So, the compiler can only know the address of a dat variable if it is in the first object and the method table is done before assembling the DAT block.

    hippy, how are DAT variables accessed, as an offset from the object base?
  • hippyhippy Posts: 1,981
    edited 2008-04-18 07:39
    There's no real reason not to add the offset at run time; you've already accepted that
    the opcodes requiring LMM will run slower so an extra instruction probably won't make
    a lot of difference, none at all if you're lucky and it doesn't delay the subsequent rdlong
    which gets the LMM opcode. I was quite surprised how little effect adding instructions
    into my VM's had.

    Another thought on patching up the offset; if there is a "long @somewhere" in the PASM,
    Spin can read that, subtract its own "@something" and write the offset into Cog before
    launching. That gets it down to just one update to handle.
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-04-18 07:48
    On the offset, you can use @@0 to give the base address of the current object. Got it from the tricks and traps document here http://forums.parallax.com/attachment.php?attachmentid=49618. See page 10.
  • hippyhippy Posts: 1,981
    edited 2008-04-18 09:11
    Excellent find; I've never got to grips with @@. Presumably then, if one didn't want to
    pass @@0 via PAR one could write it to the Cog image before loading using something
    like this ( untested ) ....

    PUB Main
      offset := @@0
      CogNew( @Lmm, 0 )
    
    DAT
    
    Lmm    mov    Lmm_pc,start
           add    Lmm_pc,offset
    Loop   rdlong :Opc,Lmm_pc
           add    Lmm_pc,#4
    :Opc   long   0-0
           jmp    #Loop
    
    start  long    @LmmCode
    offset long   0-0
    
    Lmm_pc res    1
    
    DAT     ' Fast P0 toggling run as LMM interpreted code
    
    LmmCode mov   DIRA,#1
            xor   OUTA,#1
            sub   Lmm_pc,#8 ' equiv to jmp #$-1
    
    
    
  • heaterheater Posts: 3,370
    edited 2008-04-18 11:55
    stevenmess, I have to disagree, the compiler must know exactly what the address is at compile time. The way I see it is that for VARiables this must be a run time calculation as because at compile time it is unknown how many instances of the variable there will be and where they might be. But there is only ever one DAT section shared amoung all instances so surely it has an address fixed at run time. After all in spin we use
    "hub_address_something := @something" or "cognew(@enter, param)" and it works just fine. I see no reason why it should not work the same in ASM statements and DAT definitions.

    hippy, these adress fixups whether done in spin or asm (using the @00 trick) whatever will never slow down the LMM or 8080 emulator as they are only done once prior to launching the cog. Currently I have a bunch spin statements like below to fix up the dispatch table addreses prior to coginit:

    do_daa_addr := (@lmm_end + @lmm_begin - @do_daa) | $8000

    N.B. This caters for the reverse execution order of the LMM.

    I'd rather just write "do_daa_addr word (@lmm_end + @lmm_begin - @do_daa) | $8000" into my dispatch table.

    I'm sure your "offset := @@0" would work just fine as I do similar already with other things.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    For me, the past is not over yet.
  • hippyhippy Posts: 1,981
    edited 2008-04-18 12:03
    I think the compiler does know the addresses, but only sort of. I think through the nature
    of compilation it only knows the address offset to its start, and every module is compiled
    individually and only at linking ( done invisibly ) is the real address known. The "@" and
    "@@" for Spin provide the magic in the background for that.

    As to why Spin and PASM get different results, that's AIUI because both store this offset,
    then once linked and at runtime the values differ; in Spin "@" is a unary operator, in PASM
    it's a compile time directive.

    It's annoying, but that's the way it is. And probably too late to change it if it could be as
    doing so would break a lot of existing code.
  • stevenmess2004stevenmess2004 Posts: 1,102
    edited 2008-04-18 12:27
    Like hippy says I think that the compiler knows the offset from the start of the object, not the actual address. Must do or otherwise DOL wouldn't be able to launch the tv driver. The "@" operator determines the actual address at runtime.

    When used in a DAT section the @ only knows the offset from the start of the object so that is all it can provide.
  • heaterheater Posts: 3,370
    edited 2008-04-18 13:01
    It's crazy that in the same file and the same object and referring to the same thing the "@" gives different results.
    It is what it is so let it rest there. I'm sure Parallax had a good reason for this.

    Anyway I'm very happy thanks to hippy and stevenmess I now have an opcode dispatch table that initializes it's own entries, all be it to an offset address, and a LMM virtual machine that adds the required offset as it runs.

    Turns out, hippy, as you said the extra instruction in starting the LMM does not show up but wow I saved 50 longs by not initializing things in spin !!

    Current execution rate of 8080/5 opcodes is 323KIPS running FIBO and 352KIPS running the EX8080 test prog.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    For me, the past is not over yet.
Sign In or Register to comment.