PDA

View Full Version : asm functions



Graham Stabler
10-14-2006, 06:38 AM
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

Mike Green
10-14-2006, 06:52 AM
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 B
10-14-2006, 10:51 AM
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 Schwabe (Parallax)
10-14-2006, 11:39 AM
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
}

CON
' Assembly Commands
#1,_Command1,_Command2

VAR
long ASMcog,ASMcommand

OBJ

CON'############################################## ################################################## #############
' Dispatch Entry/Exit routines
'################################################# ################################################## #############
PUB ASMstart : okay | pa
'' Start Assembly Dispatch program - returns false if no cog available
ASMstop
okay := ASMcog := cognew(@mainloop, @ASMcommand) + 1
PUB ASMstop
'' Stop Assembly Dispatch program - frees a cog
if ASMcog
cogstop(ASMcog~ - 1)
ASMcommand~
PRI setcommand(cmd, argptr)
ASMcommand := cmd << 16 + argptr 'write command and pointer
repeat while ASMcommand 'wait for command to be cleared, signifying receipt
CON'############################################## ################################################## #############
' Spin --> Assembly Calls
'################################################# ################################################## #############
PUB Command1(Arg0_)|Arg1_
setcommand(_Command1, @Arg0_)
Result := Arg1_
PUB Command2(Arg0_)|Arg1_
setcommand(_Command2, @Arg0_)
Result := Arg1_
CON'############################################## ################################################## #############
' Assembly Dispatch Program
'################################################# ################################################## #############
DAT org
'
' main loop
'
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
'variables back to spin language.
wrlong zero,par 'zero command to signify command received
ror t1,#16+2 'lookup command address
add t1,#jumps
movs :table,t1
rol t1,#2
shl t1,#3
:table mov t2,0
shr t2,t1
and t2,#$FF
jmp t2 'jump to command
jumps byte 0 '0
byte Command1_ '1
byte Command2_ '2
byte NotUsed_
byte NotUsed_ '─┐
byte NotUsed_ ' │
byte NotUsed_ ' ┣─ Additional functions MUST be in groups of 4-bytes (1 long)
byte NotUsed_ '─┘ With this setup, there is a limit of 256 possible functions.
NotUsed_
jmp #mainloop
CON'############################################## ################################################## #############
' Assembly Calls
'################################################# ################################################## #############
DAT
Command1_

{ Do Assembly Procedure here }

jmp #mainloop 'Go wait for next command


Command2_

{ Do Assembly Procedure here }

jmp #mainloop 'Go wait for next command




CON'############################################## ################################################## #############
' Assembly Data/Variable Section
'################################################# ################################################## #############
DAT
{
########################### Defined data ###########################
}
zero long 0 'constants
d0 long $200
{
########################### Undefined data ###########################
}
t1 res 1
t2 res 1
t3 res 1
AddressLocation res 1

arg0 res 1 'arguments passed from high-level
arg1 res 1
arg2 res 1
arg3 res 1
arg4 res 1
arg5 res 1
arg6 res 1
arg7 res 1



▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe (mailto:bschwabe@parallax.com)

IC Layout Engineer
Parallax, Inc.

Post Edited (Beau Schwabe (Parallax)) : 10/14/2006 4:43:23 AM GMT

Graham Stabler
10-14-2006, 04:58 PM
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 C
10-14-2006, 06:40 PM
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 Stabler
10-14-2006, 07:01 PM
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 Green
10-14-2006, 09:08 PM
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.