Is it true that when I want to play an audio 16[url=mailto:16bit@22.05khz]bit@22.05khz[/url]·signal, I only have (20000000/(16*22050))=56.56clock cycles thus I only may use about 14 instructions(most instructions cost 4 cycles)?
The thing that I want to make a synthesizer and·would love to use 16bit 44.1khz to playback my calculations.
But that would really limit me in assembly instructions.
Edit:
I seem to have made a mistake about the calc.
It would have been 20000000/22050=907cycles about 226cycles which would be enough I gess, and then shift the 16bits in the audio dac.
maybe even at 44.1khz...
But for now...
I want to use one cog for wave generation and then put it trought a filter algoritm.
I then have to see if I could sync the 2 cogs.
first cog will generate the first 16bit of the wave, the second will have to wait till the first cog calculated the waveform.
I thought of writing·a bit in the hub mem, "0" would be wait for waveform calc to finish which will be '1" if finished, second cog will make it '0' once it is read.
The first cog sees a '0' and know it has to calc·the next 16bit value of the wave form.
Is this a good approach or not?
Post Edited (darkxceed) : 10/8/2008 5:24:40 PM GMT
For t=1/f, 44.1kHz equates to just over 22675ns, so that's how many nanoseconds you have between each update of the output value. At 80MHz, cycle time is 12.5nS, instruction time 50nS, so 22675/50 = 453 instructions between updates.
PS : It's much better if you start a new topic for questions not related to an existing thread.
darkxceed, the cog operates at 20mips with 80Mhz ( 5Mhz clock * 16PLL ) which is 80Million / 4 clocks per instruction ( with some exceptions hub-ops etc.) = 20Mips [noparse]:)[/noparse]
INA is a register / memory location just like any other. You need to use INA as a source field. Typically, you'd have a bit mask in some location which corresponds to the pin(s) you want, then use a test instruction to set one of the conditional flags (zero or carry) or you'd copy INA to a location and shift and mask the bits you want there. For example:
test bit2mask,INA wc ' At this point, the carry flag is set to the state of pin 2
bit2mask long |< 2
mov temp,INA
shr temp,#16 ' Shift bit 16 to the position of bit 0
and temp,#$FF ' Mask off least significant 8 bits
temp long 0
yllawwally, making sure you haven't got the bits set in DIRA as output, ie leave them 0's
.
.
.
waitpne PIN2,PIN2 'waits for INA anded with PIN2 to not equal PIN2 ( assuming only one bit is set in PIN2 otherwise a low on any of the input pins in PIN2 would pass this check )
mov datain,INA ' get the data from the port.
shr datain,#16 ' I'm assuming you want the byte, so shift the bits right 16 times
and datain,#255
.
.
.
PIN2 long 1<<2
Hello , i would like to ask if someone changed the code in the library Synth.Spin (which is in Spin language) , to propeller assembly language .
Is it possible or it's very complicated ?
Thank you in advance
DAT org'
' SPI Engine - main loop
'
loop rdlong t1,par wz 'wait for command
if_z jmp #loop
movd :arg,#arg0 'get 5 arguments ; arg0 to arg4
mov t2,t1 ' │
mov t3,#5 '───┘
:arg rdlong arg0,t2
add :arg,d0
add t2,#4
djnz t3,#:arg
mov address,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 SHIFTOUT_ '1
byte SHIFTIN_ '2
byte NotUsed_ '3
NotUsed_ jmp #loop
what is the "movd" command doing?
I read the command explanation out of the manual, and I understand movd copies the value in the source location to the destination field (bits [17..9]) of the destination location; but I don't understand what it's doing here. Can you fill the destination field of PASM instructions with a movd operation? and if that is what it's being used for here, why wouldn't you just use arg0, why would you use movd and #arg0?
Also, I understand when you use the # symbol in front of a value, you are indicating that rather than a memory location, you are using a 9 bit literal. But what does it mean when there is a # symbol in front of a memory address?
I've spent a good while trying to figure this out myself, and I don't think I'm going to.
So I really would appreciate an explanation. Thanks.
I read the command explanation out of the manual, and I understand movd copies the value in the source location to the destination field (bits [17..9]) of the destination location; but I don't understand what it's doing here. Can you fill the destination field of PASM instructions with a movd operation? and if that is what it's being used for here, why wouldn't you just use arg0, why would you use movd and #arg0?
In the example above the insn at :arg is modified from two different places and the whole parameter fetch sequence happens to be repeated (new/next command). Hope that's enough to get you down the right path here ...
Also, I understand when you use the # symbol in front of a value, you are indicating that rather than a memory location, you are using a 9 bit literal. But what does it mean when there is a # symbol in front of a memory address?
What are the values ending up in a and b? Then think about what the effect would be on an insn when used with movd in the context of parameter setup.
DAT org 0
entry mov a, location ' $000, a = ?
mov b, #location ' $001, b = ?
waitpeq $, #0 ' $002
location long 42 ' $003
a res 1 ' $004
b res 1 ' $005
I had a discussion with a friend that I think cleared some of this up for me.
The short answers to my short questions:
Q: Can you modify a later instruction using the "movd" command?
A: Yes. It makes sense, but the only applications for the "movd" command described in the manual are both configuring special registers (either the video or counter modules), and I was having a hard time testing the theory.
Q: What does it mean when you put a literal symbol in front of a label rather than a value?
A: If you put a # symbol in front of a a label for either an initialized or un-initialized variable, you are telling the propeller to use the memory location that label refers to, as a value; rather than the value contained there-in. In other words, if you have a label "Number" at memory location 4, and the value in "Number" is 18, then "#Number" equals 4, rather than 18.
If any of that is wrong please show no mercy.
So to test my understanding I wrote a small block of assembly code, but am not getting the results I expected.
CON
_clkmode= xtal1+pll16x
_xinfreq= 5_000_000
PUB main
cognew(@start, 4)
DAT
ORG 0
start mov n1, #0 wz 'set Z to 1
:read rdlong n1, par 'read par into n1
add :read, #4 'add the literal decimal value 4 into :read
n1Lights muxz dira, n1 'set bits in dira corresponding with the high bits in n1 to Z
muxz outa, n1 'set bits in outa corresponding with the high bits in n1 to Z
Ldelay mov time, cnt 'time:=cnt
add time, Ldelay 'add delay into time
waitcnt time, #0 'wait for count to equal time
_Ldelay long 500_000
n1 res 1
time res 1
I was hoping to see P3 light up (using P0-P31 as a binary display for the value in n1).
I'm using the Gear: Parallax Propeller Emulator (which can be found here) and it's built in dir display,
and this is what I'm getting: (shows both dira and dirb with dira on top)
which I'm pretty sure reads P0, P1, P2, P3, P5, P6, P9, P10, P11, P13, P20 as high, and everything else as low.
I was hoping to see P3 light up (using P0-P31 as a binary display for the value in n1).
At least dira looks correct ($00102E6F). Why is that? You set par to 4. In the PASM section you do a rdlong from said address (loading a long from hub RAM address #4) which gives you - among other things - the clkmode ($6F) and the binary checksum ($2E). Where did you think you'd get the mask from which would light up P3 (%1000)?
Edit: Just realised that you mentioned dira/dirb rather than dira/outa (still too early here).
For non-special-register movd usage check [post=1108886]this example[/post].
In the propeller manual, it clearly says you can use nine bit literal values in place of a memory location.
Here's an example from the manual:
add X, #25 'Add 25 to X
add X, Y 'Add Y to X
X long 50
Y long 10
and the description:
"the result of the first ADD instruction is 75 (i.e.: X + 25 → 50 + 25 = 75) and that value, 75, is stored back in the X register. Similarly, the result of the second ADD instruction is 85
(i.e.: X + Y → 75 + 10 = 85) and so X is set to 85. "
So to answer Kuroneko, I thought I was adding 4+4=8, ie 000000_00000000_00000000_00001000
Really I'm just trying to understand what's going on in this block of code (from Beau Schwabe's SPI engine)
So to answer Kuroneko, I thought I was adding 4+4=8, ie 000000_00000000_00000000_00001000
I assume you mean this bit of code:
start mov n1, #0 wz 'set Z to 1
:read [COLOR="#FFA500"]rdlong[/COLOR] n1, par 'read par into n1
add :read, #4 'add the literal decimal value 4 into :read
What happens is that - after setting the Z flag - you read from hub RAM location #4 (par == 4) which gives you $00102E6F in n1 (basically the wrong insn here). After that you increase the content of :read by 4 (not n1 or par). Location :read contains an insn (rdlong n1, par) which is encoded as $08BC13F0. Adding 4 to it results in $08BC13F4 (rdlong n1, outa). One way of arriving at 8 would have been:
start mov n1, #0 wz 'set Z to 1
:read mov n1, par 'move par into n1
add n1, #4 'add the literal decimal value 4 into n1
Doing it like this is OK for a small number of parameters (or [thread=135075]if you want it fast[/thread]). To keep the code size down it's sometimes done in a loop.
read the command (par is the hub address of the command)
if said command is zero try again (until non-zero)
reset parameter fetch insn to rdlong arg0, t2
take copy of command (hub address in lower bits)
initialise loop counter (5 arguments)
read first argument into arg0
modify parameter fetch insn to point to next cog address (arg1)
advance hub address
read next argument (insn at :arg has now been modified/updated)
A note re: point 7, what happens here is that the destination field (insn[17..9]) is incremented by one, IOW starting with rdlong arg0, t2 you'll effectively end up with rdlong arg0+1, t2 which is the same as rdlong arg1, t2 in our case. After 5 loop cycles said insn has been incremented by 5*d0. Because of this we need point 3 which resets the insn's destination field to point to arg0 when the next command is handled.
Thank you very much for your excellent response. I'm going to have to study it as soon as I get some time.
Glancing through it, I basically didn't understand how to do a single thing correctly there. PASM is weird.
I need to re-examine how "values" are passed to PASM sub-routines.
At any rate, again thanks for the response. I've got some work to do.
I thought I'd add to this thread rather than start a new one...
I'm still grappling with some fundamental concepts of ASM. I'm trying to imagine the framework of an ASM routine that shares data with a Spin routine. Here's what I don't get:
I have two bytes and 12 words in hub memory that need to be manipulated by a cog running ASM. How do I get the addresses of said bytes and words into the ASM routine with just one long parameter variable? Or tell Spin where they are if they have to be defined and put in the hub with ASM?
You can only pass one address via par so what most of us do is pass the starting address of a block of longs; within this block you can store the addresses (use @ to populate) of your byte and word arrays.
Note: It is not a good idea to count on placement of variables in a list when you want multiple addresses -- better to manually stuff and pass the addresses as part of a parameters list.
Here's a partial listing:
var
long cog
long param1
long param2
word warray[12]
byte barray[2]
pub start
stop
param1 := @warray ' get hub address of arrays
param2 := @barray
cog := cognew(@entry, @param1) + 1
return cog
pub stop
if (cog)
cogstop(cog - 1)
cog := 0
dat
org 0
entry mov t1, par ' get address of params
rdlong wpntr, t1 ' get hub addr of w's
add t1, #4 ' next long
rdlong bpntr, t1 ' get hub addr of b's
This sets the cog variables wpntr and bpntr to the addresses of the respective arrays. You can use these values (updated with an index) with wrxxxx and rdxxxx to access the arrays.
Here's how you might create cog subroutines to read/write those arrays:
readw mov t1, wpntr ' point to w array
shl idx, #1 ' x2 for words
add t1, idx ' add index
rdlong wval, t1 ' read warray[idx] into wval
readw_ret ret
writew mov t1, wpntr ' point to w array
shl idx, #1 ' x2 for words
add t1, idx ' add index
wrlong wval, t1 ' write wval to warray[idx]
writew_ret ret
readb mov t1, bpntr ' point to b array
add t1, idx ' add index
rdlong bval, t1 ' read barray[idx] into bval
readb_ret ret
writeb mov t1, bpntr ' point to b array
add t1, idx ' add index
wrlong bval, t1 ' write bval to barray[idx]
writeb_ret ret
Remember that the cog sees the hub as a giant array of bytes. What this means, then, is that when you're reading/writing a word, you must multiply the index value by 2 (2 bytes per long word). This is added into the base address of the array which gives you the correct hub address for the word you want to access.
Now I'm trying to replicate the process in PASM.
This is what I have so far:
{PASM 1000th prime number generator ***Time to execute:???}CON
_clkmode= xtal1+pll16x
_xinfreq= 5_000_000
OBJ
pst: "Parallax Serial Terminal"
VAR
long _Prime
PUB main
pst.Start(57600)
waitcnt(clkfreq*2+cnt)
cognew(@start, @_Prime)
repeat while _Prime==0
pst.str(string(16, "Calculating..."))
pst.str(string(13, "1000th Prime Number == "))
pst.dec(_Prime)
DAT
org 0
start add candidate, #2 'generate "next" candidate (all odd numbers)
mov divisor, candidate 'copy candidate to divisor
dd sub divisor, #2 'generate "next" divisor (all odd numbers < candidate)
sub divisor, #1 nr, wz 'check if divisor has reached one
if_z jmp #prime 'if divisor==1 (Z==1) then jump to #prime
divide mov checked, check wz 'check if (candidate//divisor)==0
if_z jmp #start 'if check==0 (Z==1) then move on to next candidate
jmp #dd 'if check<>0 (Z==0) then move on to next divisor
prime add prime_cnt, #1 'count candidate as prime
sub prime_cnt, grand nr, wz 'check if prime_cnt==1000
if_z jmp #end 'if prime_cnt==1000 (Z==1) then jump to #end
jmp #start 'if prime_cnt<>1000 (Z==0) then move on to next candidate
end wrlong candidate, PAR 'write candidate (1000th prime number) to address at PAR (_Prime)
'************************************************************
candidate long 7
prime_cnt long 4
check long (candidate//divisor)
grand long 1000
divisor res 1
quotient res 1
checked res 1
Unfortunately, instead of the expected 7919, It returns 1999.
My first suspicion is that I don't quite understand how to use the flags properly.
Also, I'm not certain I can have a variable ("check") equal an operation between
two other variables (candidate//divisor) without doing more than just putting candidate//divisor in the value field.
Remember that the cog sees the hub as a giant array of bytes. What this means, then, is that when you're reading/writing a word, you must multiply the index value by 2 (2 bytes per long).
Little slip there - 2 bytes per word is what was meant! And for longs the factor is 4 (or a shift-left of 2 bits).
I guess I'm confused as to how to do that.
In my code I have one variable named check initialized to (candidate//divisor)
and at instruction "divide" I copy the value from check into checked and "wz"
where Z==1 would mean I had evenly divided divisor into candidate, thus candidate is not prime.
In my code I have one variable named check initialized to (candidate//divisor)
This is a compile time value which is calculated as the remainder from dividing the cog address of candidate ($00D) by the cog address of divisor ($011). IOW it will stay $00D for the duration of your run and you will always get a result of 7+(1000-4)*2 = 1999.
Division & Co have to be done the hard way in PASM if you want runtime results. Check appendix B of the manual. You should also terminate your program properly after the wrlong (with an endless loop/wait or cogstop). Otherwise the cog continues to execute what's there.
Bummer. I thought the value was determined when the value was read (not that I can think of how that would happen).
Thanks for the help.
Update:
Integrated the division code from appendix b of the manual (thanks Kuroneko) and it works.
16.5ish seconds to execute; and that's with a 2 second handshake period for the serial terminal. So 14.5ish seconds to generate the prime number. 01_PASM_PrimeNumberGenerator.spin
Update2:
So now I'm trying to pass the starting address of an array of longs like JonnyMac described earlier in the thread.
I think I understand the general goal: to set a variable equal to the starting address of an array of "longs" and then in assembly, read each long into it's own variable by starting with the address in PAR then adding an Index value to that address such that you arrive at the next long (which I think is idx:=4). I have had a hell of a time wrapping my head around the idea of memory addresses, but I feel like I'm close.
{PASM 1000th prime number generator ***Time to execute:???}CON
_clkmode= xtal1+pll16x
_xinfreq= 5_000_000
OBJ
pst: "Parallax Serial Terminal"
VAR
long array_start
long _PASM_PARS[2]
PUB main
pst.Start(57600)
waitcnt(clkfreq*2+cnt)
array_start:=@_PASM_PARS
_PASM_PARS[1]:=(cognew(@Pre, @array_start)+1)
pst.str(string(16, "Calculating..."))
repeat while _PASM_PARS[0]==0
waitcnt(clkfreq/100+cnt)
pst.str(string(13, "1000th Prime Number == "))
pst.dec(_PASM_PARS[0])
DAT
org 0
Pre mov Larray,PAR 'get address of array_start
mov array_addr,Larray 'copy address of array_start
mov idx,#1 'set index to 1
shl idx,#2 'set index to 4
add array_addr,idx 'add index to array start to get address of next location in array
rdlong cog_id,array_addr 'read from main memory
'*************************************************************************************************************************************
start add candidate,#2 'generate "next" candidate (all odd numbers)
mov divisor,candidate 'copy candidate to divisor
dd sub divisor,#2 'generate "next" divisor (all odd numbers < candidate)
sub divisor,#1 nr,wz 'check if divisor has reached one
if_z jmp #prime 'if divisor==1 (Z==1) then jump to #prime
check jmp #divide 'jmp to #divide
checked mov dend,rem nr,wz 'flag z if rem==0
if_z jmp #start 'if check==0 (Z==1) then move on to next candidate
jmp #dd 'if check<>0 (Z==0) then move on to next divisor
prime add prime_cnt,#1 'count candidate as prime
sub prime_cnt,grand nr, wz 'check if prime_cnt==1000
if_z jmp #end 'if prime_cnt==1000 (Z==1) then jump to #end
jmp #start 'if prime_cnt<>1000 (Z==0) then move on to next candidate
end wrlong candidate,Larray 'write candidate (1000th prime number) to address at PAR (_Prime)
'cogstop cog_id 'stop cog(cog_id)
dead nop
jmp #dead
'*************************************************************************************************************************************
divide mov dor, divisor 'copy divisor into dor
mov dend, candidate 'copy den into candidate
shl dor,#15 'get dor into dor[30..15]
cmpsub dend,dor wc 'dend =< dor? Subtract it, quotient bit in c
rcl dend,#1 'rotate c into quotient, shift dividend
cmpsub dend,dor wc '
rcl dend,#1 'repeat x16
cmpsub dend,dor wc '
rcl dend,#1 '
cmpsub dend,dor wc '
rcl dend,#1 '
cmpsub dend,dor wc '
rcl dend,#1 '
cmpsub dend,dor wc '
rcl dend,#1 '
cmpsub dend,dor wc '
rcl dend,#1 '
cmpsub dend,dor wc '
rcl dend,#1 '
cmpsub dend,dor wc '
rcl dend,#1 '
cmpsub dend,dor wc '
rcl dend,#1 '
cmpsub dend,dor wc '
rcl dend,#1 '
cmpsub dend,dor wc '
rcl dend,#1 '
cmpsub dend,dor wc '
rcl dend,#1 '
cmpsub dend,dor wc '
rcl dend,#1 '
cmpsub dend,dor wc '
rcl dend,#1 '
cmpsub dend,dor wc '
rcl dend,#1 '
'quotient in dend[15..0], remainder in dend[31..16]
mov rem,dend 'copy dend to rem
shr rem,#15 'shift out quotient
ret_checked jmp #checked 'return to checked
'*************************************************************************************************************************************
candidate long 7
prime_cnt long 4
grand long 1000
divisor res 1
dor res 1
dend res 1
rem res 1
Larray res 1
array_addr res 1
idx res 1
cog_id res 1
Other than adding the Pre routine, all I changed was the wrlong from "wrlong candidate,PAR" to "wrlong candidate,Larray" where "Larray" theoretically == @_PASM_PARS[0]
The other variable in the array is the cog_id of the PASM cog so I can use a cogstop command at end; rather than an endless loop. But I'm not using it right now.
My point being I'm probably missing something in the Pre routine.
We were all learning the language at the time and that was my personal preferred method.... now I think OR to set the pin and ANDN to clear the pin is the more accepted method, but it probably depends on the circumstance.
It seems to me that a potential advantage to the MUX method is that it deals with the current pin/s in the mind of the programmer and wont interfere should there be some other pins that other part of the code has set purposely. Why AND or XOR the whole INA or DIRA unless you really want to? It makes no difference on these simple and short code examples, but on more sophisticated pin sets it is better to have code applying only what ought to be applied... n'est pas?
Comments
Is it true that when I want to play an audio 16[url=mailto:16bit@22.05khz]bit@22.05khz[/url]·signal, I only have (20000000/(16*22050))=56.56clock cycles thus I only may use about 14 instructions(most instructions cost 4 cycles)?
The thing that I want to make a synthesizer and·would love to use 16bit 44.1khz to playback my calculations.
But that would really limit me in assembly instructions.
Edit:
I seem to have made a mistake about the calc.
It would have been 20000000/22050=907cycles about 226cycles which would be enough I gess, and then shift the 16bits in the audio dac.
maybe even at 44.1khz...
But for now...
I want to use one cog for wave generation and then put it trought a filter algoritm.
I then have to see if I could sync the 2 cogs.
first cog will generate the first 16bit of the wave, the second will have to wait till the first cog calculated the waveform.
I thought of writing·a bit in the hub mem, "0" would be wait for waveform calc to finish which will be '1" if finished, second cog will make it '0' once it is read.
The first cog sees a '0' and know it has to calc·the next 16bit value of the wave form.
Is this a good approach or not?
Post Edited (darkxceed) : 10/8/2008 5:24:40 PM GMT
PS : It's much better if you start a new topic for questions not related to an existing thread.
But isn't it true that a cog operates at 20Mhz and not 80Mhz?
your calculations is almost the same as me btw 80000000/44100/4=453, so it take one cycle of the 20Mhz clock to execute 1 instruction.
Post Edited (darkxceed) : 10/8/2008 5:57:52 PM GMT
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
http://www.propgfx.co.uk/forum/·home of the PropGFX Lite
·
Well this adds up all well!
thanx
.
.
.
waitpne PIN2,PIN2 'waits for INA anded with PIN2 to not equal PIN2 ( assuming only one bit is set in PIN2 otherwise a low on any of the input pins in PIN2 would pass this check )
mov datain,INA ' get the data from the port.
shr datain,#16 ' I'm assuming you want the byte, so shift the bits right 16 times
and datain,#255
.
.
.
PIN2 long 1<<2
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
http://www.propgfx.co.uk/forum/·home of the PropGFX Lite
·
Is it possible or it's very complicated ?
Thank you in advance
Assembly Code Examples for the Beginner: SPI Engine Demo
and I have a really simple question.
what is the "movd" command doing?
I read the command explanation out of the manual, and I understand movd copies the value in the source location to the destination field (bits [17..9]) of the destination location; but I don't understand what it's doing here. Can you fill the destination field of PASM instructions with a movd operation? and if that is what it's being used for here, why wouldn't you just use arg0, why would you use movd and #arg0?
Also, I understand when you use the # symbol in front of a value, you are indicating that rather than a memory location, you are using a 9 bit literal. But what does it mean when there is a # symbol in front of a memory address?
I've spent a good while trying to figure this out myself, and I don't think I'm going to.
So I really would appreciate an explanation. Thanks.
What are the values ending up in a and b? Then think about what the effect would be on an insn when used with movd in the context of parameter setup.
I had a discussion with a friend that I think cleared some of this up for me.
The short answers to my short questions:
Q: Can you modify a later instruction using the "movd" command?
A: Yes. It makes sense, but the only applications for the "movd" command described in the manual are both configuring special registers (either the video or counter modules), and I was having a hard time testing the theory.
Q: What does it mean when you put a literal symbol in front of a label rather than a value?
A: If you put a # symbol in front of a a label for either an initialized or un-initialized variable, you are telling the propeller to use the memory location that label refers to, as a value; rather than the value contained there-in. In other words, if you have a label "Number" at memory location 4, and the value in "Number" is 18, then "#Number" equals 4, rather than 18.
If any of that is wrong please show no mercy.
So to test my understanding I wrote a small block of assembly code, but am not getting the results I expected.
I was hoping to see P3 light up (using P0-P31 as a binary display for the value in n1).
I'm using the Gear: Parallax Propeller Emulator (which can be found here) and it's built in dir display,
and this is what I'm getting: (shows both dira and dirb with dira on top)
which I'm pretty sure reads P0, P1, P2, P3, P5, P6, P9, P10, P11, P13, P20 as high, and everything else as low.
So what am I missing here?
Thanks for your help.
Edit: Just realised that you mentioned dira/dirb rather than dira/outa (still too early here).
For non-special-register movd usage check [post=1108886]this example[/post].
In the propeller manual, it clearly says you can use nine bit literal values in place of a memory location.
Here's an example from the manual: and the description:
"the result of the first ADD instruction is 75 (i.e.: X + 25 → 50 + 25 = 75) and that value, 75, is stored back in the X register. Similarly, the result of the second ADD instruction is 85
(i.e.: X + Y → 75 + 10 = 85) and so X is set to 85. "
So to answer Kuroneko, I thought I was adding 4+4=8, ie 000000_00000000_00000000_00001000
Really I'm just trying to understand what's going on in this block of code (from Beau Schwabe's SPI engine)
The objective here is to read 5 parameters from hub RAM. Unrolled it would look like this: Doing it like this is OK for a small number of parameters (or [thread=135075]if you want it fast[/thread]). To keep the code size down it's sometimes done in a loop.
- read the command (par is the hub address of the command)
- if said command is zero try again (until non-zero)
- reset parameter fetch insn to rdlong arg0, t2
- take copy of command (hub address in lower bits)
- initialise loop counter (5 arguments)
- read first argument into arg0
- modify parameter fetch insn to point to next cog address (arg1)
- advance hub address
- read next argument (insn at :arg has now been modified/updated)
A note re: point 7, what happens here is that the destination field (insn[17..9]) is incremented by one, IOW starting with rdlong arg0, t2 you'll effectively end up with rdlong arg0+1, t2 which is the same as rdlong arg1, t2 in our case. After 5 loop cycles said insn has been incremented by 5*d0. Because of this we need point 3 which resets the insn's destination field to point to arg0 when the next command is handled.Glancing through it, I basically didn't understand how to do a single thing correctly there. PASM is weird.
I need to re-examine how "values" are passed to PASM sub-routines.
At any rate, again thanks for the response. I've got some work to do.
I'm still grappling with some fundamental concepts of ASM. I'm trying to imagine the framework of an ASM routine that shares data with a Spin routine. Here's what I don't get:
I have two bytes and 12 words in hub memory that need to be manipulated by a cog running ASM. How do I get the addresses of said bytes and words into the ASM routine with just one long parameter variable? Or tell Spin where they are if they have to be defined and put in the hub with ASM?
Help me, Obi Wans...
Note: It is not a good idea to count on placement of variables in a list when you want multiple addresses -- better to manually stuff and pass the addresses as part of a parameters list.
Here's a partial listing:
This sets the cog variables wpntr and bpntr to the addresses of the respective arrays. You can use these values (updated with an index) with wrxxxx and rdxxxx to access the arrays.
Here's how you might create cog subroutines to read/write those arrays:
Remember that the cog sees the hub as a giant array of bytes. What this means, then, is that when you're reading/writing a word, you must multiply the index value by 2 (2 bytes per long word). This is added into the base address of the array which gives you the correct hub address for the word you want to access.
First I wrote one in SPIN, (01_SPIN_PrimeNumberGenerator.spin)
It takes 4 minutes and 44 seconds to run.
Now I'm trying to replicate the process in PASM.
This is what I have so far:
Unfortunately, instead of the expected 7919, It returns 1999.
My first suspicion is that I don't quite understand how to use the flags properly.
Also, I'm not certain I can have a variable ("check") equal an operation between
two other variables (candidate//divisor) without doing more than just putting candidate//divisor in the value field.
As always I would appreciate any advice.
Thanks.
Little slip there - 2 bytes per word is what was meant! And for longs the factor is 4 (or a shift-left of 2 bits).
You haven't implemented the divisibility test so every odd number is believed to succeed, so you stop on the 1000'th odd number.
In my code I have one variable named check initialized to (candidate//divisor)
and at instruction "divide" I copy the value from check into checked and "wz"
where Z==1 would mean I had evenly divided divisor into candidate, thus candidate is not prime.
Division & Co have to be done the hard way in PASM if you want runtime results. Check appendix B of the manual. You should also terminate your program properly after the wrlong (with an endless loop/wait or cogstop). Otherwise the cog continues to execute what's there.
Thanks for the help.
Update:
Integrated the division code from appendix b of the manual (thanks Kuroneko) and it works.
16.5ish seconds to execute; and that's with a 2 second handshake period for the serial terminal. So 14.5ish seconds to generate the prime number.
01_PASM_PrimeNumberGenerator.spin
Update2:
So now I'm trying to pass the starting address of an array of longs like JonnyMac described earlier in the thread.
I think I understand the general goal: to set a variable equal to the starting address of an array of "longs" and then in assembly, read each long into it's own variable by starting with the address in PAR then adding an Index value to that address such that you arrive at the next long (which I think is idx:=4). I have had a hell of a time wrapping my head around the idea of memory addresses, but I feel like I'm close.
Other than adding the Pre routine, all I changed was the wrlong from "wrlong candidate,PAR" to "wrlong candidate,Larray" where "Larray" theoretically == @_PASM_PARS[0]
The other variable in the array is the cog_id of the PASM cog so I can use a cogstop command at end; rather than an endless loop. But I'm not using it right now.
My point being I'm probably missing something in the Pre routine.
Thanks for your help.
for example:
mov pin_mask, #1
shl pin_mask, pin
or outa, pin_mask 'preset pin high
or dira, pin_mask 'set pin to output