Shop OBEX P1 Docs P2 Docs Learn Events
asm functions — Parallax Forums

asm functions

Graham StablerGraham Stabler Posts: 2,510
edited 2006-10-14 14:08 in Propeller 1
If I have two pieces of related asm code that I want to act as functions, so I want an object that sits there waiting for a call, then does one or the other of these two calculations and returns the result(s).

What is the best way of implementing this? In particular how do you handle which asm segment is being called and how best do you handle the returned variables especially if there are more than one?

I'm sorry my question is not more clear, I'm completely happy with an asm loop chugging away updating some global variables with wrlong and then having functions just to read the global variables but in this instance I want to instigate the running of a code section and return more than one variable at a time, I guessed I should probably be passing pointers to global variable space defined in the main spin function but I'm not sure if that's the way to do it.

Graham

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2006-10-13 23:52
    The way to do this is to pass the address of a block of memory when you start the assembly cog. The first long is initialized to -1. The assembly routine just sits there waiting for the long to get changed to something other than -1. When that happens, it jumps to one of several routines based on the value in that long. When the assembly routine is done, it sets that first long back to -1. When that long is -1, the caller can do anything with the common data area, store new parameters, fetch result values, etc. When that long value is not -1, only the assembly routine can touch the common area where it can fetch parameters and store result values. Access to the common area is done simply from the assembly routine by moving PAR to some temporary location, adding any needed variable offset to the temporary, and doing a RDxxxx or WRxxxx to access the value.

    An example of this is the module OS_loaderInit in my Propeller OS. This is an assembly routine to do I2C I/O and takes a 2 long parameter block and returns status information in the same block. Although only one bit in the first long is used to signal busy/done, the principle is the same.

    Post Edited (Mike Green) : 10/13/2006 11:56:54 PM GMT
  • David BDavid B Posts: 592
    edited 2006-10-14 03:51
    In my Propeller SD card music player that I posted to this forum a few weeks ago, I did pretty much the same thing that Mike discussed.

    I had one asm cog read data from a WAV file stored in an SD card. It writes 4 bytes as a long into one hub variable, then sets another hub variable to "4" to indicate that four bytes are ready. The DAC player asm cog monitors the "ready" hub location, and when it contains "4", the DAC cog reads the data variable, then writes "0" to the "ready" variable.

    (The "4" was because I was thinking to maybe transfer more than 4 bytes per "ready" flag, if the player needed the extra speed, but with this single long transfer mechanism, the player can play 16 bit stereo, 44 kHz, which is good enough for now.)

    My project manages multiple variables like what you need. As well as the actual file data transfer, the SD card cog and its calling spin code share about 8 or 10 FAT filesystem variables, and the DAC cog and the calling spin share about half a dozen WAV sound parameters. There's no real trick to addressing them; like Mike says, you just add an offet of 4 to PAR for every hub variable away from the original hub address passed in when you start the asm cog.

    I'm still trying to work out a clean way to build structures of these variables that can be known to the managing spin code and both asm cog codes, sort of like a separate include file. As things are now, when I add or rearrange the hub variables, I've got to keep both the card asm and the DAC player asm correctly updated, and it's all too easy to mess things up. I've started to play around with sticking a third asm cog to be a data buffer between the SD card cog and the DAC player cog, so it's only going to get harder to manage all the variables.

    The only other way I can think to have two cogs communicate would be through doing the same sort of thing through a couple of I/O pins. I looked into that for the WAV player, but after I sketched out all the support code needed to manage the bitwise communication, the time consumed by all the code far exceeded the worst case of communicating through the hub.

    David
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2006-10-14 04:39
    Here is an Assembly Dispatch program that allows you to make Assembly calls from Spin. There are a few objects that use this technique.

    If you look at 'graphics.spin', then you should see similarities with the code below. Also, the VGA graphics Demo at the link below uses this method.

    http://forums.parallax.com/showthread.php?p=606957

    {
                                    ********************************************
                                                 Assembly Dispatch       V1.0
                                    ********************************************
                                                coded by (Parallax)
                                    ********************************************
    Version 1.0 - initial release
    }
    
    [b]CON[/b]
    '  Assembly Commands
      #1,_Command1,_Command2
    
    [b]VAR[/b]
    [b]long[/b]    ASMcog,ASMcommand
        
    [b]OBJ[/b]
    
    [b]CON[/b]'#############################################################################################################
    '                               Dispatch Entry/Exit routines
    '################################################################################################################
    [b]PUB[/b] ASMstart : okay | pa
    '' Start Assembly Dispatch program - returns false if no cog available
        ASMstop
        okay := ASMcog := [b]cognew[/b](@mainloop, @ASMcommand) + 1
    [b]PUB[/b] ASMstop
    '' Stop Assembly Dispatch program - frees a cog
        [b]if[/b] ASMcog
           [b]cogstop[/b](ASMcog~ - 1)
        ASMcommand~
    [b]PRI[/b] setcommand(cmd, argptr)
        ASMcommand := cmd << 16 + argptr                    'write command and pointer
        [b]repeat[/b] [b]while[/b] ASMcommand                             'wait for command to be cleared, signifying receipt
    [b]CON[/b]'#############################################################################################################
    '                               Spin --> Assembly Calls
    '################################################################################################################
    [b]PUB[/b] Command1(Arg0_)|Arg1_
        setcommand(_Command1, @Arg0_)
        [b]Result[/b] := Arg1_
    [b]PUB[/b] Command2(Arg0_)|Arg1_
        setcommand(_Command2, @Arg0_)
        [b]Result[/b] := Arg1_
    [b]CON[/b]'#############################################################################################################
    '                               Assembly Dispatch Program
    '################################################################################################################
    [b]DAT[/b]                     [b]org[/b]
    '
    ' main loop
    '
    mainloop                [b]rdlong[/b]  t1,[b]par[/b]          [b]wz[/b]      'wait for command
            [b]if_z[/b]            [b]jmp[/b]     #mainloop
                            [b]movd[/b]    :arg,#arg0              'get 8 arguments
                            [b]mov[/b]     t2,t1
                            [b]mov[/b]     t3,#8                           
    :arg                    [b]rdlong[/b]  arg0,t2
                            [b]add[/b]     :arg,d0
                            [b]add[/b]     t2,#4
                            [b]djnz[/b]    t3,#:arg
                            [b]mov[/b]     AddressLocation,t1      'preserve address location for passing
                                                            'variables back to spin language.
                            [b]wrlong[/b]  zero,[b]par[/b]                'zero command to signify command received
                            [b]ror[/b]     t1,#16+2                'lookup command address
                            [b]add[/b]     t1,#jumps
                            [b]movs[/b]    :table,t1
                            [b]rol[/b]     t1,#2
                            [b]shl[/b]     t1,#3
    :table                  [b]mov[/b]     t2,0
                            [b]shr[/b]     t2,t1
                            [b]and[/b]     t2,#$FF
                            [b]jmp[/b]     t2                      'jump to command
    jumps                   [b]byte[/b]    0                       '0
                            [b]byte[/b]    Command1_               '1
                            [b]byte[/b]    Command2_               '2
                            [b]byte[/b]    NotUsed_
                            [b]byte[/b]    NotUsed_                '&#61626;&#9472;&#9488;                                                               
                            [b]byte[/b]    NotUsed_                '  &#9474;                                                               
                            [b]byte[/b]    NotUsed_                '  &#9507;&#9472;&#61610; Additional functions MUST be in groups of 4-bytes (1 long)  
                            [b]byte[/b]    NotUsed_                '&#61626;&#9472;&#9496;   With this setup, there is a limit of 256 possible functions.
    NotUsed_
                            [b]jmp[/b]     #mainloop
    [b]CON[/b]'#############################################################################################################
    '                               Assembly Calls
    '################################################################################################################
    [b]DAT[/b]
    Command1_
    
            {   Do Assembly Procedure here   }
    
                  [b]jmp[/b]       #mainloop                       'Go wait for next command
    
    
    Command2_
    
            {   Do Assembly Procedure here   }
    
                  [b]jmp[/b]       #mainloop                       'Go wait for next command
    
    
    
    
    [b]CON[/b]'#############################################################################################################
    '                               Assembly Data/Variable Section
    '################################################################################################################
    [b]DAT[/b]
    {
    ########################### Defined data ###########################
    }
    zero                    [b]long[/b]    0                       'constants
    d0                      [b]long[/b]    $200
    {
    ########################### Undefined data ###########################
    }
    t1                      [b]res[/b]     1                       
    t2                      [b]res[/b]     1
    t3                      [b]res[/b]     1
    AddressLocation         [b]res[/b]     1
    
    arg0                    [b]res[/b]     1                       'arguments passed from high-level
    arg1                    [b]res[/b]     1
    arg2                    [b]res[/b]     1
    arg3                    [b]res[/b]     1
    arg4                    [b]res[/b]     1
    arg5                    [b]res[/b]     1
    arg6                    [b]res[/b]     1
    arg7                    [b]res[/b]     1
    
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 10/14/2006 4:43:23 AM GMT
  • Graham StablerGraham Stabler Posts: 2,510
    edited 2006-10-14 09:58
    Thanks all, I should have this up and running in no time!

    I was going to ask another question but I think I might have just answered it myself, I've seen programs where variables used in the asm are set directly by the spin code, I wondered why I couldn't do the same to enter the variables and get the return values. I've just realized that I only ever see that happening in the start function for that object so presumably its OK because the asm cog is begun with those values. However once started its wrlong and rdlong all the way.

    Graham
  • Jim CJim C Posts: 76
    edited 2006-10-14 11:40
    Beau:

    I was curious about one part of your parameter passing routine:

    mainloop rdlong t1,par wz 'wait for command
    if_z jmp #mainloop
    movd :arg,#arg0 'get 8 arguments
    mov t2,t1
    mov t3,#8
    :arg rdlong arg0,t2
    add :arg,d0
    add t2,#4
    djnz t3,#:arg
    mov AddressLocation,t1 'preserve address location for passing

    In this section, you add "d0" to :arg, an address, where d0 is a constant, $200. Seems like this winds up modifying the rdlong command, by adding 1 to the destination field, thus pointing to the next assembly arguement location. Is that correct?

    Thanks,

    Jim C
  • Graham StablerGraham Stabler Posts: 2,510
    edited 2006-10-14 12:01
    Jim, that's right it is a way of doing indexing of an array of data because in asm you can't just do arg etc.

    See this recent thread and also check out the definintion of movd and movs

    http://forums.parallax.com/forums/default.aspx?f=25&m=148004

    Graham
  • Mike GreenMike Green Posts: 23,101
    edited 2006-10-14 14:08
    Graham,
    The technique you mentioned, that of setting up constants in the DAT block prior to a COGNEW only works because the COGNEW copies the whole block
    of 512 longs into the cog's memory before starting it. The values set by the SPIN start routine get copied along with everything else. As you've noticed,
    once they're copied, they're not accessable except to the assembly program running in the cog.
Sign In or Register to comment.