[color=red]' This is a driver object, which will be used in some other SPIN, isn't it?
' So, you don't want the output_num variable to be in this file because then the other SPIN which uses the driver can not access the output
' Instead you have to do the same that you do with note and the cuelist - pass the adress.
[/color][color=red]' PS: Now I recognized that you have changed LEDdrivercall to return the output value. So, it's some kind of mixed philosophy. For note ans cuelist you
[/color][color=red]' allow the other SPIN to maintain it, whereas you don't allow it to access output directly. I'd do it either this way or that way.
[/color]VAR
[color=orange]' somewhere in this thread we had the problem of passing mixed type parameters to PASM. There we had the problem, that PropTool is reordering
' the variables and we can not simply pass one adress to PASM and rely on the fact that all variables are in a row. That's why we had the idea
' to put several adresses in a single long named note_cuelisr_addr. Now we have an additional adress we need to pass. This does no longer fit
' into a long because HUB RAM adresses need 16 bit. That's why I switched to a word array now.
' Please note: this is only needed, if note, output and cuelist have different types!
' If all have the same type we can rely on the fact that the order in memory is the same as the order of the definition. Then we only need to
' pass one adress. (To be honest I think the types are the same ... but OK, you'll learn more if we get it running this way)
word parameter_adr[noparse][[/noparse] 3 ] [s]note_cuelist_addr, output_num[/s][/color]
PUB start([color=red]note_addr,output_addr[/color], cuelist_addr)
[color=green] word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse]0] := [b][color=red]@note[/color][/b] ' Pointer for note value
[/color][color=green] [color=orange]' again, THIS IS A NONO!!!!! Don't pass a adress that points to a variable on the stack, unless you know what it means[/color]
word[noparse][[/noparse]note_cuelist_addr] [/color][color=green]:= @output_num ' Pointer for value returned
[/color][color=green] [color=red]' ^ this will directly overwrite the value stored before becauese word[noparse][[/noparse] note_cuelist_addr ] is equal to word[noparse][[/noparse] note_cuelist_addr ][noparse][[/noparse] 0 ][/color]
long[noparse][[/noparse]note_cuelist_addr][/color][color=green] := @cuelist_addr ' Put the cutelist pointer in HUB ram
[color=orange]' ^ this will overwrite everything stored in note_cuelist_addr before[/color]
[/color][color=green]
' with the change of the variable the whole block looks a bit easier, as we simply can take the adresses passed to the function
' and store these in the parameter_adr array
parameter_adr[noparse][[/noparse] 0 ]:=note_addr
[/color][color=green] parameter_adr[noparse][[/noparse] 1 ]:=output_addr
[/color][color=green] parameter_adr[noparse][[/noparse] 2 ]:=cuelist_addr
[/color]
cognew(@entry, [color=green]@parameter_adr[/color])
{ PUB LEDdrivercall ' Used to get the value back from the operation
return output_num }
[color=red]' this no longer fits to the variables. The SPIN using this object has direct access to the output variable.
[/color]
DAT
org 0
' When the cog starts par is initialized with the address passed in the coginit command
' in this case it was passed @note_cuelist_addr.
entry mov ptr1,par ' load parameter pointer into ptr (points to global variables)
' ptr is just a copy and keeps par unchanged for later.
[color=green]' up to here your PASM code looks good ;o)[/color]
mov ptr2,par ' We need a second copy of the pointer for cuelist
[color=red]' but I don't know why you need an exact copy of par again
[/color]
[color=orange]' having :start here only makes sense, if the pointer to note can change. But usually you would use the same buffer as long as the driver is running.[/color]
[s]:start[/s] rdword note_ptr,ptr1 ' Read the note pointer address from pointer1
[color=red][s]add ptr1,note_ptr ' move pointer1 to the note data[/s]
' note_ptr IS pointing to the note data. What you did here was @note + @note_cueList_adr. Unpredictable to what this will point.
[/color]
[color=green]:start[/color] rdbyte note_val,[color=green]note_[/color]ptr[s]1[/s] wz ' Read the note value
[color=purple]' it's a good idea to have a waitcnt here with an adequate waittime, because it saves energy. The waittime depends on
' the needed responsiveness. If you have time critical stuff to do, don't wait, if not wait as long as possible.[/color]
if_z jmp #:start ' If the note value is zero start again
add ptr2,#4 ' Move pointer2 to the address of cuelist
rdlong list_ptr,ptr2 ' Read cuelist address
' move getting list_ptr to where you get note_ptr
[s]add ptr2,list_ptr ' Move the pointer 2 to cuelist data[/s]
:loop rdbyte list,list_ptr[s]2[/s] ' get the first byte of the byte array (cuelist)
cmp list,note_val wz ' Compare a byte of cuelist with note_val
if_z jmp #:output_on ' if zero go to a jump
add count,#%0_0000_0001 ' Increment count
cmp count,#%0_0111_1111 wz ' compare count with 127
if_z jmp #:output_on ' if count = 127 jump
add ptr2,#1 ' Move pointer2 to the next byte (of the array cuelist)
jmp #:loop ' Jump back to the beginning of the loop
:output_on add ptr1,#2 ' Move ptr1 to the return value space
rdword count_ptr,ptr1 ' Read the count pointer address
' I would prepare a out_ptr at the same place you get the other pointers and use that. Then you don't need to calculate here.
[s]add ptr1,count_ptr ' move the pointer to the counter data space[/s]
wrlong count, ptr1 ' Output the count value
mov note_val,#0
[color=orange]wrlong note_val, note_ptr ' otherwise SPIN can not synchronize and :start will immediately run again
[/color]
jmp #:start [s]entry[/s] ' not sure about this jump, should it be to :start?
' These variables are in cog ram!
ptr1 long 0
ptr2 long 0
count long 0
count_ptr word 0
[color=red]cue_ptr long 0
[/color]' cue_ptr can not be used by PASM instructions because it does not point to a long-aligned-adress. You should have all words behind the longs
' and all bytes behind words. But still handling will be more difficult. Better to use long as long as you don't run out of COG RAM.
list_ptr long 0
list long 0
note_val long 0
note_ptr word 0
test_num long 0
fit 496
I give it up for this night ;o) Maybe I should simply write the driver for you and explain what I did for what reason.
Blah, I'm sure everyone reading this thread is tired of my posting.
another edition.
Yea I have edited about 4 times on here already.
VAR
word note_cuelist_addr
PUB start(note_addr, output_addr, cuelist_addr)
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse]0] := @note_addr ' Pointer for note value
word[noparse][[/noparse]note_cuelist_addr](1) := @output_addr ' Pointer for value returned
word[noparse][[/noparse]note_cuelist_addr](2) := @cuelist_addr ' Put the cutelist pointer in HUB ram
"(" is only for the forums. the address is actually a "[noparse][[/noparse]"
cognew(@entry, @note_cuelist_addr)
{{PUB LEDdrivercall ' Commented out, but left for clarity
return output_num}}
DAT
org 0
' When the cog starts par is initialized with the address passed in the coginit command
' in this case it was passed @note_cuelist_addr.
entry mov ptr,par ' Set register ptr to equal Par
:start rdword note_ptr,ptr ' Read the note pointer address from pointer1
rdbyte note_val,note_ptr wz ' Read the note value from the address
waitcnt ' Not sure how long to wait.
if_z jmp #:start ' If the note value is zero start again
add ptr,#2 ' Move ptr to the return value space?? Is there a space?
rdword output_ptr,ptr ' Read the output pointer address
add ptr,#2 ' Move pointer to the address of cuelist
rdword list_ptr,ptr ' Read cuelist address
:loop rdbyte list,list_ptr ' get the first byte of the byte array (cuelist)
cmp list,note_val wz ' Compare a byte of cuelist with note_val
if_z jmp #:output_on ' if zero go to a jump
add count,#%0_0000_0001 ' Increment count
cmp count,#%0_0111_1111 wz ' compare count with 127
if_z jmp #:output_on ' if count = 127 jump
add ptr,#1 ' Move pointer to the next byte (of the array cuelist)
jmp #:loop ' Jump back to the beginning of the loop
:output_on ' The following I'm not sure if I understand.
wrbyte count,output_ptr ' Output the count value
mov note_val,#0 ' Set note Value to zero
wrbyte note_val,note_ptr ' How does this work??
jmp #:start ' Back to start
' These variables are in cog ram!
ptr long 0 'not sure if any of these are right.
note_ptr word 0
output_ptr word 0
list_ptr word 0
count byte 0
note_val byte 0
list byte 0
fit 496
That's bad! The forum offered the "Post reply" button, but when I was done with my post (which took ~30min) it told me that I have no rights to post. Obviously a timeout occurred. All changes were lost.
Ok ... let's try again (with saving from time to time, so please be aware that this post might change while you read it until i write DONE at the end)
James .. the goal comes closer ;o)
VAR
word note_cuelist_addr
[color=green] ' I assume the [noparse][[/noparse] 3 ] has been swallowed by the forum software again?! You should have spaces between the square brackets and the number, otherwise[/color]
[color=green] ' forum software interprets it as 'set textsize'[/color]
PUB start(note_addr, output_addr, cuelist_addr)
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse]0] := @note_addr ' Pointer for note value
word[noparse][[/noparse]note_cuelist_addr](1) := @output_addr ' Pointer for value returned
word[noparse][[/noparse]note_cuelist_addr](2) := @cuelist_addr ' Put the cutelist pointer in HUB ram
"(" is only for the forums. the address is actually a "[noparse][[/noparse]"
cognew(@entry, @note_cuelist_addr)
{{PUB LEDdrivercall ' Commented out, but left for clarity
return output_num}}
DAT
org 0
' When the cog starts par is initialized with the address passed in the coginit command
' in this case it was passed @note_cuelist_addr.
entry mov ptr,par ' Set register ptr to equal Par
[color=purple] ' I think we have a different understanding of how the driver should work. My understanding is that you pass the pointers to[/color]
[color=purple] ' note, output and cuelist once. Your understanding seems to be that these pointers might change. For example your SPIN that uses[/color]
[color=purple] ' the driver has several notes and several cuelists and can decide which to take for decoding.[/color]
[color=purple] ' Ok, I'll adapt my understanding and we will first try to get this running like you want it. Later on we can optimize.[/color]
:start rdword note_ptr,ptr ' Read the note pointer address from pointer1
[color=red] ' first problem: this only works for the first iteration.[/color]
[color=red] ' In entry you set ptr to par, so for the first run you get the right pointer. But later on in the code you change ptr.[/color]
[color=red] ' You add 2 for getting output_ptr and again 2 for getting the list_ptr. This is OK as such. But when you come back to :start[/color]
[color=red] ' ptr still points to the parameter which gives you the list_ptr. Either you have to subtract 4 after you read the last _ptr[/color]
[color=red] ' or you do a jump to entry to get ptr reinitialized
[/color] rdbyte note_val,note_ptr wz ' Read the note value from the address
[s]waitcnt[/s] ' Not sure how long to wait.
[color=orange] ' waitcnt works a bit different than you think. You first have to calculate the cnt-value for which you want to wait.[/color]
[color=orange] ' if_z mov cnt_val, cnt[/color]
[color=orange] ' if_z add cnt_val, waittime[/color]
[color=orange] ' if_z waitcnt cnt_val, #0[/color]
[color=orange] ' as I said - first we get this running. Later on we can optimize this.[/color]
[color=orange] ' You have to create the PASM variables cnt_val and waittime at the end of the code. The if_z simply skips the wait when we[/color]
[color=orange] ' know that we want to do something.[/color]
[color=orange] ' Again: waiting here is some kind of energy optimization - if you want it to be runtime optimized, then don't wait at all.
[/color] if_z jmp #:start ' If the note value is zero start again
add ptr,#2 ' Move ptr to the return value space?? Is there a space?
rdword output_ptr,ptr ' Read the output pointer address
add ptr,#2 ' Move pointer to the address of cuelist
rdword list_ptr,ptr ' Read cuelist address
:loop rdbyte list,list_ptr ' get the first byte of the byte array (cuelist)
cmp list,note_val wz ' Compare a byte of cuelist with note_val
if_z jmp #:output_on ' if zero go to a jump
add count,#%0_0000_0001 ' Increment count
[color=red] ' Do you compile your code? I guess no, because this should produce a compile-error[/color]
[color=red] ' The label count is not pointing to a long aligned adress. For the third time: please use long as PASM variables as long as[/color]
[color=red] ' you don't have a better understanding. We can talk about that later.[/color]
[color=red] ' And again: this will only work as desired in the first iteration, because count is never set to 0 again.[/color]
[color=red] ' So, in the second conversion count will already be 127 - then you add 1. For the following compare it means it has to wait until[/color]
[color=red] ' count wrapped around and got 127 again. Result you will reach memory-adresses which are outside of the cue-list and compare your[/color]
[color=red] ' note_val with unpredictable data
[/color] cmp count,#%0_0111_1111 wz ' compare count with 127
if_z jmp #:output_on ' if count = 127 jump
add ptr,#1 ' Move pointer to the next byte (of the array cuelist)
jmp #:loop ' Jump back to the beginning of the loop
:output_on ' The following I'm not sure if I understand.
wrbyte count,output_ptr ' Output the count value
mov note_val,#0 ' Set note Value to zero
wrbyte note_val,note_ptr ' How does this work??
[color=orange] ' In note_val you have a 0. So, you write the 0 to the memory location where you find the SPIN variable note.[/color]
[color=orange] ' This has two effects. First thing is that your PASM driver will go back to :start. There you read note again. As nobody changed it[/color]
[color=orange] ' the next conversion would start immediately. That's why you have to set note to 0 - then the driver waits until SPIN put a new[/color]
[color=orange] ' value there. The other thing is that you now have the possibility to synchronize the SPIN code with the driver.[/color]
[color=orange] ' note := "c" -> this starts the lookdown[/color]
[color=orange] ' repeat while note -> this waits as long as lookdown is running
[/color] jmp #:start ' Back to start
' These variables are in cog ram!
ptr long 0 'not sure if any of these are right.
note_ptr [s]word[/s] 0
output_ptr [s]word[/s] 0
list_ptr [s]word[/s] 0
count [s]byte[/s] 0
note_val [s]byte[/s] 0
list [s]byte[/s] 0
[color=red]' Use long instead.[/color]
' cnt_val long 0
' waittime long clkfeq/1000
[color=orange]' Well, the waittime depends on how long you can wait for a decode. In this case the driver will check ~1000 times per second. This means that the[/color]
[color=orange]' lookdown could need something between runtime and runtime + 1ms until we have the result in output. So, it very much depends on which responsiveness[/color]
[color=orange]' you need in your application.
[/color] fit 496
Now some words for better understanding the problem with the words and bytes in PASM:
The propeller has two different adress-busses with different adress ranges and different organisation.
One adress-bus is the COG-RAM adress-bus. It's 9 bit wide, so it ranges from 0-512 ($000-$1FF / %0_0000_0000 - %1_1111_1111). As you can read in the propeller specs, each COG has 2kBytes of RAM. So not each byte has it's own adress! Each adress points to a LONG (4 byte) instead. 4Bytes x 512Adresses·= 2kBytes.
PASM runs in COG-RAM and only knows LONG-adresses. Now let's have a look on the latest code from you and let's assume the variables would be placed at adress $100 (256 in decimal).
$100 = ptr··········
-> so, each PASM command using ptr will use adress $100 ( mov ptr, par will be translated to mov $100, $1f0 [noparse][[/noparse]I'm not sure if PAR really is at $1f0 and I am to
lazy to search for it currently - so let's simply assume it's true]
$101 = list_ptr, count and note_val
-> as memory adress $101 is a long it can hold more data than just the list_ptr which is a word. But for PASM only the label list_ptr makes sense. Count would be at adress 256,5 and note_val would be at adress 256,75 ;o) But we don't have fractional adresses. So, when you want to access count in PASM you have to use the long which includes
count. But then count is not at bit 0-7 but on bits 16-23. Adding 1 would change list_ptr and not count. To extract count needs some extra steps which increases runtime and
memory usage. And that's why I said: You have to know what you do if you use different data types for you PASM variables.
$102 = list and 3 undefined bytes
And why does the compiler then allowes labels for words and bytes. Well ... because DAT is only data. Data can have different meanings, it could be image data ... or sound data ... whatever. In our dat section the data is a list of PASM opcodes and PASM variables. Please remember, the whole SPIN file first is placed in HUB-RAM. The HUB has it's own adress bus, which is 16 bits wide, ranging from 0 to 65535. With the first 32k-adresses you·can access RAM with the second 32k-adresses you can access the buildin ROM.
And of course in SPIN you are allowed to read/write bytes, words and longwords from HUB-memory and there you can use those labels.
Rather than posting code at the moment, I'll answer the few inaccuracies I may have created.
Cuelist is always the same byte array. This information never changes. The notes on the other hand do change, but they should be placed in the same place (address) every time (my thinking).
Your right, I will need to reset count and ptr.
I knew waitcnt worked differently, but do not have a clue how long to wait.
I now understand the long aligned problem. I greatly appreciate your explanation. For now, I'll use long values. I'm not sure if I am ready for any other alignment. It appear very difficult to deal with.
When I get a chance I'll rewrite, and post the code.
I just omitted waitcnt for now, but left a place for it.
VAR
word note_cuelist_addr[noparse][[/noparse] 3 ]
PUB start(note_addr, output_addr, cuelist_addr)
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 0 ] := @note_addr ' Pointer for note value
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 1 ] := @output_addr ' Pointer for value returned
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 2 ] := @cuelist_addr ' Put the cutelist pointer in HUB ram
cognew(@entry, @note_cuelist_addr)
DAT
org 0
' When the cog starts par is initialized with the address passed in the coginit command
' in this case it was passed @note_cuelist_addr.
entry mov ptr,par ' Set register ptr to equal Par
:start rdword note_ptr,ptr ' Read the note pointer address from pointer
rdbyte note_val,note_ptr wz ' Read the note value from note pointer address
' Waitcnt goes here
if_z jmp #:start ' If the note value is zero start again
add ptr,#2 ' Move ptr to the output address
rdword output_ptr,ptr ' Read the output pointer address
add ptr,#2 ' Move pointer to the address of cuelist
rdword list_ptr,ptr ' Read cuelist address
:loop rdbyte list,list_ptr ' Read the first byte of the byte array from list_ptr address
cmp list,note_val wz ' Compare byte with note_val
if_z jmp #:output_on ' if zero go to a jump
add count,#%0_0000_0001 ' Increment count
cmp count,#%0_0111_1111 wz ' compare count with 127
if_z jmp #:output_on ' if count = 127 jump
add list_ptr,#1 ' Move pointer to the next byte ( array cuelist)
jmp #:loop ' Jump back to the beginning of the loop
:output_on wrlong count,output_ptr ' Output the count value to the output address
mov note_val,#0 ' Set note Value to zero
wrlong note_val,note_ptr ' Setting note_ptr to 0 (synchronizes Spin)
mov count,#0 ' Reset Count to 0
sub ptr,#4 ' Back up the ptr to origin.
jmp #:start ' Back to start
' These variables are in cog ram!
ptr long 0
note_ptr long 0
output_ptr long 0
list_ptr long 0
count long 0
note_val long 0
list long 0
fit 496
Bingo!
This seems to me like it could work. (I did not try it) Now we can start with some optimizations:
Let's think about that: "... but they should be placed in the same place (address) every time" you talked about note and cuelist.
So, why do you read the adresses with each iteration? When the adresses of note and cuelist never change, there is no need to read the adresses with each
loop. Just do it in the initialisation phase of your PASM.
Your version: org 0
entry [s]mov ptr,par[/s] ' Set register ptr to equal Par
[s]:start[/s] list_act: rdword note_ptr,par[s]ptr[/s] ' Read the note pointer address from pointer
[s]rdbyte note_val,note_ptr wz ' Read the note value from note pointer address
[/s] ' Waitcnt goes here
[s] if_z jmp #:start ' If the note value is zero start again[/s]
add par[s]ptr[/s],#2 ' Move ptr to the output address
rdword output_ptr,par[s]ptr[/s] ' Read the output pointer address
add par[s]ptr[/s],#2 ' Move pointer to the address of cuelist
rdword list_ptr,par[s]ptr[/s] ' Read cuelist address
[color=red] ' now you have the pointers to note, output and list in COG-RAM. No need to read it again and again, if these[/color]
[color=red] ' don't change[/color]
:start rdbyte note_val, note_ptr wz
if_z jmp #:start
[color=orange] ' but now we should not change the list pointer, so we copy that and work with the copy in the :loop[/color]
mov list_act, list_ptr
[color=red] ' we never need the code above :start again, so we can overwrite with runtime variables. The time will come, when you need[/color]
[color=red] ' each long for your PASM code to fit into the COG-RAM ;o) Note: you could even move the _ptr variables to the top. Then[/color]
[color=red] ' you won't need a single long.[/color]
:loop rdbyte list,list_act[s]ptr[/s] ' Read the first byte of the byte array from list_ptr address
cmp list,note_val wz ' Compare byte with note_val
if_z jmp #:output_on ' if zero go to a jump
add count,#%0_0000_0001 ' Increment count
cmp count,#%0_0111_1111 wz ' compare count with 127
[color=orange] ' a little exercise: if you count from 127 down (instead of up), you could have 2 instructions less in the loop[/color]
[color=orange] ' so, in worst case you have 127 * 2 instructions less. Tip: DJNZ is your friend here. But you have to reorder stuff[/color]
[color=orange] ' a little bit. And when you found the note in the list, you have to calculate the right count (=127-count)
[/color] if_z jmp #:output_on ' if count = 127 jump
add list_act[s]ptr[/s],#1 ' Move pointer to the next byte ( array cuelist)
jmp #:loop ' Jump back to the beginning of the loop
:output_on wrlong count,output_ptr ' Output the count value to the output address
[s]mov note_val,#0[/s] ' Set note Value to zero
[color=red] ' there is no need to set note_val to zero, as we read a fresh value everytime[/color]
[color=red] ' so, for clearing note_ptr you could use a different zero, after some reordering of the code
[/color] wrlong note_val,note_ptr ' Setting note_ptr to 0 (synchronizes Spin)
mov count,#0 ' Reset Count to 0
[s]sub ptr,#4[/s] ' Back up the ptr to origin.
jmp #:start ' Back to start
[s]ptr long 0[/s]
note_ptr long 0
output_ptr long 0
list_ptr long 0
PAR is nothing else than·a COG-RAM adress with the purpose of passing one parameter to the PASM. This is called a special register. As it's simply COG RAM you can
change PAR and there is no need to copy PAR to PTR.
Nice, isn't it? We reduced size by 6 longs, with potential of saving another 5 longs (if you follow my hints). And we reduced runtime of the loop.
Happy digesting ... then the next optimizations will follow ;o) ·
As for moving them to the top, I'm not sure what you mean. Also, I'm not sure where to get another zero from the code. The count will not always come to zero.
You may be thinking of an arrangement I can't not see at this point. Using spin for so long, I guess habits are hard to break.
I've omitted everything above the DAT to save space.
DAT
org 0
' When the cog starts par is initialized with the address passed in the coginit command
' in this case it was passed @note_cuelist_addr.
entry
: pointers rdword note_ptr,par ' Read the note pointer address from pointer (had to add a space between : and p)
add par,#2 ' Move ptr to the output address
rdword output_ptr,par ' Read the output pointer address
add par,#2 ' Move pointer to the address of cuelist
rdword list_ptr,par ' Read cuelist address
:start rdbyte note_val,note_ptr wz ' Read the note value from note pointer address
' Waitcnt goes here
if_z jmp #:start ' If the note value is zero start again
mov list_loc,list_ptr ' Copy the list pointer to list location
' copied to prevent needing to reset list_ptr on
' each iteration
mov count,#127 ' set Count to 127
:loop rdbyte list,list_loc ' Read the byte (moves with loop) of the byte array
' from list_loc address
cmp list,note_val wz ' Compare byte with note_val
if_z jmp #:output_on ' If the values match jump to output on
add list_loc,#1 ' Move pointer to the next byte(array cuelist)
cmp count,#0 wz ' compare Count with 0
djnz count,#:loop ' If the values do not match decrement count and jump to loop
:output_on wrlong count,output_ptr ' Output the count value to the output address
mov note_val,#0 ' not sure where to get another 0??
wrlong note_val,note_ptr ' Setting note_ptr to 0 (synchronizes Spin)
jmp #:start ' Back to start
' These variables are in cog ram!
list_loc long 0
note_ptr long 0
output_ptr long 0
list_ptr long 0
count long 0
note_val long 0
list long 0
fit 496
I really hope some other soul out there is also getting something from all of this. I put off learning Pasm for a long time, and the threshold had been reached.
If you are out there, and have hit that point, some public airing of the laundry may be needed. I hate to show how hard it is for me to learn something like Pasm, but I feel the end justifies the means.
If you are following, I hope your light went on before mine did.
If I count the lines which have been striked out I count 6 lines. This means we saved 6 longs in comparision to the version before.
You are right with what you say about the zero. I forgot about the other tip to use DJNZ. That of course means that counter is no longer initialized with zero.
······················· ' we never need the code above :start again, so we can overwrite with runtime variables. The time will come, when you need ························' each long for your PASM code to fit into the COG-RAM ;o) Note: you could even move the _ptr variables to the top. Then ······················· ' you won't need a single long.
What I mean is: From label entry up to label :start we have some code which is only needed on the first call of our driver. When calling cognew( @entry, @note_cuelist_addr ). All the jump we have in the code don't go back to the very beginning. So, here we have 5 longs that we can use in our loops. By doing so, we can save some memory at the end.
entry note_ptr rdword note_ptr,par ' Read the note pointer address from pointer (had to add a space between : and p)
output_ptr add par,#2 ' Move ptr to the output address
list_ptr rdword output_ptr,par ' Read the output pointer address
list_loc add par,#2 ' Move pointer to the address of cuelist
count rdword list_ptr,par ' Read cuelist address
...
[s]list_loc long 0[/s]
[s]note_ptr long 0[/s]
[s]output_ptr long 0[/s]
[s]list_ptr long 0[/s]
[s]count long 0[/s]
note_val res 1
list res 1
See what I mean? We reuse the memory. First it is filled with our code, but when the code ran we can use it as any other memory adress. So we saved another 5 longs. I know we don't need to save for this little driver, but if you learn to save memory you get familiar with it and you don't have to twist your brain when you really need it.
Please be aware this not only saves memory in COG-RAM, it also saves memory in HUB-RAM as each long defined has a real memory adress and data in HUB-RAM.
Of course you can only use this trick for variables which need not to be initialized.
What does RES mean? Well, it's doing the same with the difference that it uses memory behind our code and long, word, byte definitions. That's very important! The RES·has to be used after all code AND variable declarations of the PASM snipped for one COG! Otherwise you have different labels point to the same memory adress. In opposite to long, RES does not put data to HUB-RAM, but you need some COG-RAM for it. So, if you used all 496 longs for code and data it's not possible to add a RES. Overwriting no longer needed memory is then the only way to have some extra room for variables.
Hope this was understandable for you! Again in short form:
long (and of course word and byte) needs memory in HUB-RAM and in COG-RAM and·they are·initialized it with a value.
res points to an adress in COG-RAM and needs no memory in HUB-RAM - that's why you can only use it for variables which need not to be initialized by default.
IMPORTANT: RES has to be defined AFTER code and long/word/byte of the PASM for one COG.
In your code you still have one little obstacle:
:loop rdbyte list,list_loc ' Read the byte (moves with loop) of the byte array
' from list_loc address
cmp list,note_val wz ' Compare byte with note_val
if_z jmp #:output_on ' If the values match jump to output on
add list_loc,#1 ' Move pointer to the next byte(array cuelist)
[s]cmp count,#0 wz ' compare Count with 0[/s]
[color=red]' you don't need to compare. DJNZ is doing the whole job ([color=green][b]D[/b][/color]ecrement and [color=green][b]J[/b][/color]ump if [b][color=green]N[/color][/b]ot [b][color=green]Z[/color][/b]ero). The cmp can not see if the decrement will[/color]
[color=#ff0000] ' be zero or not.[/color]
djnz count,#:loop ' If the values do not match decrement count and jump to loop
Ok ... now the code looks pretty good. Maybe I'll give it a try this evening/night. But we're not done with optimizing. Guess this thread will be a nice tutorial when we're done.
What's to come next? I suggest that you now write the code which uses our driver. Based on that we can do more improvements.
Hmmmm.....I didn't even know that was legal. I do not think I have ever seen what you did here.
entry
note_ptr rdword note_ptr,par ' Read the note pointer address from pointer (had to add a space between : and p)
output_ptr add par,#2 ' Move ptr to the output address
list_ptr rdword output_ptr,par ' Read the output pointer address
list_loc add par,#2 ' Move pointer to the address of cuelist
count rdword list_ptr,par ' Read cuelist address
That is interesting. I didn't know that could be done. It is a totally new concept to me. I haven't paid a lot of attention to PASM in the past, so I may have missed this done before.
As for the djnz, I missed that concept.
The nice thing about this driver we built as it could be tested with nothing but hyperterminal. Doesn't really need any additional hardware. I am using a SD card to read data into the byte array, but there are ways around that.
I actually have code I was writing this for, but it is a mess, and huge. I'm not sure that would be good to test with.
PUB start(note_addr, output_addr, cuelist_addr)
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 0 ] := [s][color=red]@[/color][/s]note_addr ' Pointer for note value
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 1 ] := [s][color=orange]@[/color][/s]output_addr ' Pointer for value returned
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 2 ] := [s][color=red]@[/color][/s]cuelist_addr ' Put the cutelist pointer in HUB ram
This is the bug in the SPIN-part. We don't want to pass the adresses of the start function parameters. We want the variable adresses which are passed to start when we call it:
start( @note, @output, @cue_list )
Already talked about that (never pass an adress of a function parameter) and still I did not see it this time ;o)
[color=orange]' THIS was missing! I'm baffled a bit by myself ... have to think about that![/color]
[color=orange]ptr rdlong ptr,par[/color]
note_ptr rdword note_ptr, ptr
output_ptr add ptr, #2
list_ptr rdword output_ptr, ptr
list_loc add ptr, #2
count rdword list_ptr, ptr
[color=red]add list_ptr, #127[/color]
[color=#ff0000] ' that's not a bug, it only makes things easier. Count starts from 127 and counts down to 0. So, to look at the[/color]
[color=#ff0000] ' place which matches with this count, we simply start at the end of the list[/color]
:start rdbyte note_val,note_ptr wz ' Read the note value from note pointer address
' Waitcnt goes here
if_z jmp #:start ' If the note value is zero start again
mov list_loc,list_ptr ' Copy the list pointer to list location
' copied to prevent needing to reset list_ptr on
' each iteration
mov count,#127 ' set Count to 127
:loop rdbyte list,list_loc ' Read the byte (moves with loop) of the byte array
' from list_loc address
cmp list,note_val wz ' Compare byte with note_val
if_z jmp #:output_on ' If the values match jump to output on
[color=orange]sub list_loc,#1 ' Move pointer to the next byte(array cuelist)[/color]
[color=orange] ' list location has to decrease then
[/color] djnz count,#:loop ' If the values do not match decrement count and jump to loop
:output_on
[color=red]wrbyte count,output_ptr ' Output the count value to the output address[/color]
[color=red] ' here we had a wrlong. That's wrong when we deal with byte variables
[/color] mov note_val,#0 ' not sure where to get another 0??
[color=orange]wrbyte note_val,note_ptr ' Setting note_ptr to 0 (synchronizes Spin)[/color]
jmp #:start ' Back to start
' These variables are in cog ram!
note_val res 1
list res 1
fit 496
As you might know some bugs don't show up immediately! So, what was wrong with the part that baffled me?
PUB start(note_addr, output_addr, cuelist_addr)
[s]word[noparse][[/noparse][/s]note_cuelist_addr[s]][/s][noparse][[/noparse] 0 ] := note_addr ' Pointer for note value
[s]word[noparse][[/noparse][/s]note_cuelist_addr[s]][/s][noparse][[/noparse] 1 ] := output_addr ' Pointer for value returned
[s]word[noparse][[/noparse][/s]note_cuelist_addr[s]][/s][noparse][[/noparse] 2 ] := cuelist_addr ' Put the cutelist pointer in HUB ram
See the difference? Maybe you learned enough, so that you can explain it to me? And what makes this bug dangerous.
Of course the code which reads the adresses has to be changed back now that we corrected this bug:
[color=orange][s]ptr rdlong ptr,par[/s][/color]
note_ptr rdword note_ptr, par [s]ptr[/s]
output_ptr add par [s]ptr[/s], #2
list_ptr rdword output_ptr, par [s]ptr[/s]
list_loc add par [s]ptr[/s], #2
count rdword list_ptr, par [s]ptr[/s]
MagIO2 said...
Ah ... the dark clouds of dumbness dissappear.
As you might know some bugs don't show up immediately! So, what was wrong with the part that baffled me?
PUB start(note_addr, output_addr, cuelist_addr)
[s]word[noparse][[/noparse][/s]note_cuelist_addr[s]][/s][noparse][[/noparse] 0 ] := note_addr ' Pointer for note value
[s]word[noparse][[/noparse][/s]note_cuelist_addr[s]][/s][noparse][[/noparse] 1 ] := output_addr ' Pointer for value returned
[s]word[noparse][[/noparse][/s]note_cuelist_addr[s]][/s][noparse][[/noparse] 2 ] := cuelist_addr ' Put the cutelist pointer in HUB ram
See the difference? Maybe you learned enough, so that you can explain it to me? And what makes this bug dangerous.
Of course the code which reads the adresses has to be changed back now that we corrected this bug:
[color=orange]ptr ][/color]
note_ptr rdword note_ptr, par <STRIKE>ptr
output_ptr add par [s]ptr[/s], #2
list_ptr rdword output_ptr, par [s]ptr[/s]
list_loc add par [s]ptr[/s], #2
count rdword list_ptr, par [s]ptr[/s]
Yep, you are right, I didn't see that either. Using the address of the address would be of little use.
I would assume, because the way the items are declared, all of those are longs.
But if that in fact the case, wouldn't the "add par, #2" need to be "add par, #4".
Just my take on the subject.
I know it didn't work with the last iteration with all of the variables declared at the bottom.
James Long said...
But if that in fact the case, wouldn't the "add par, #2" need to be "add par, #4".
Further up you declare note_cuelist_addr as
word note_cuelist_addr[noparse][[/noparse] 3 ]
So +2 should be fine. What I have trouble with is the actual instruction
add par, #2
PAR is a read-only register and can't be used as a destination (without unwanted side effects). Therefore you'll usually find stuff like this at the beginning if more than one parameter is to be passed into the PASM section.
If you scroll back up, you will see Mag suggest to initialize some of the data at the beginning of the PASM. Using the following:
note_ptr rdword note_ptr, par
output_ptr add ptr, #2
list_ptr rdword output_ptr, par
list_loc add ptr, #2
count rdword list_ptr, par
If you notice par is not copied at all.
What I don't understand is why the following will not work:
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 0 ] := note_addr ' Pointer for note value
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 1 ] := output_addr ' Pointer for value returned
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 2 ] := cuelist_addr ' Put the cutelist pointer in HUB ram
I'm not sure at this point why.
I haven't done any deep thinking about it, so I may be missing the target.
I have found what I believe to be the issue, but I'm not sure why it is an issue.
Both are legal in Spin:
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 0 ] := note_addr ' Pointer for note value
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 1 ] := output_addr ' Pointer for value returned
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 2 ] := cuelist_addr ' Put the cutelist pointer in HUB ram
This is also legal, with the declarations in the previous post:
note_cuelist_addr[noparse][[/noparse] 0 ] := note_addr ' Pointer for note value
note_cuelist_addr[noparse][[/noparse] 1 ] := output_addr ' Pointer for value returned
note_cuelist_addr[noparse][[/noparse] 2 ] := cuelist_addr ' Put the cutelist pointer in HUB ram
I have no idea why, but spin will let you do this.
I can't say one way or another. I believe Mag has written a top object to test with, and I have not.
He may be doing something illegal with PAR, but I can not say. I'm just trying to learn.
I know you can write to PAR, for Mike Green does it. I'm not sure what he is writing to PAR, but I have his exact code which does. I know it works. I'm using it.
As for adding or subtracting, I have no idea.
I have to take what I am taught at face value and assume the instructor has done these procedures before. Mag appears to know what can and can not be done, so I believe what he says is true.
when you write "word[noparse][[/noparse] note_cuelist_addr ]" you mean "the word whose address is contained in the value note_cuelist_addr". Does that mean what you think it means?
Mike Green said...
when you write "word[noparse][[/noparse] note_cuelist_addr ]" you mean "the word whose address is contained in the value note_cuelist_addr". Does that mean what you think it means?
Mike,
Just for comparison, for my use:
When you do not use "word", what does it mean?
I'm trying to learn this, and it is important for me to understand the significance of each.
James Long said...
I have to take what I am taught at face value and assume the instructor has done these procedures before. Mag appears to know what can and can not be done, so I believe what he says is true.
Yes, you can write to PAR but that write goes to the shadow copy. Using it as the source register (afterwards) will give you (again) the value passed into coginit/cognew. Just try it, pass a two value array (initialised with two different values) to cognew and try to get to the 2nd value by adding the relevant offset to PAR.
Example for Hydra (adjust LED(6) pin for your setup):
VAR
long array[noparse][[/noparse] 2]
PUB init
array[noparse][[/noparse] 0] := 42
array[noparse][[/noparse] 1] := 24
cognew(@entry, @array)
DAT
entry mov dira, #1 << 6
rdlong one, par
add par, #4
rdlong two, par
cmp two, #24 wz
if_z or outa, #1 << 6
loop waitcnt loop, #0
jmp #loop
one long 0
two long 0
If the LED stays dark, change the cmp instruction to cmp two, #42 wz.
As for arrays, note_cuelist_addr[noparse][[/noparse]idx] is equivalent to word[noparse][[/noparse]@note_cuelist_addr][noparse][[/noparse]idx]. Just writing word[noparse][[/noparse]note_cuelist_addr] will give you a word array which starts at address note_cuelist_addr[noparse][[/noparse]0] (i.e. with whatever is in the first element).
James Long said...
I have to take what I am taught at face value and assume the instructor has done these procedures before. Mag appears to know what can and can not be done, so I believe what he says is true.
Yes, you can write to PAR but that write goes to the shadow copy. Using it as the source register (afterwards) will give you (again) the value passed into coginit/cognew. Just try it, pass a two value array (initialised with two different values) to cognew and try to get to the 2nd value by adding the relevant offset to PAR.
Example for Hydra (adjust LED(6) pin for your setup):
VAR
long array[noparse][[/noparse] 2]
PUB init
array[noparse][[/noparse] 0] := 42
array[noparse][[/noparse] 1] := 24
cognew(@entry, @array)
DAT
entry mov dira, #1 << 6
rdlong one, par
add par, #4
rdlong two, par
cmp two, #24 wz
if_z or outa, #1 << 6
loop waitcnt loop, #0
jmp #loop
one long 0
two long 0
If the LED stays dark, change the cmp instruction to cmp two, #42 wz.
I'm not doubting what you are saying, but you are not initializing the variables like Mag did.
At no time did he do like you have done above.
He is just using variables to simulate a situation. This is what I understand to be happening.
I understand what he is doing to be the following:
note_ptr rdword note_ptr, par <---- this is a variable equal to a word read from par
output_ptr add par, #2 <---- this is a variable equal to par +2
list_ptr rdword output_ptr, par <-----this is a variable equal to word read from par + 2
list_loc add par, #2 <-----this is a variable equal to par + 4
count rdword list_ptr, par <-----this is a variable equal to word read from par + 4
Like I said. I have not tested the code. We were using a copy of par before he changed the variables to the top of the PASM. Then he implemented the above code.
To get into a deep discussion, you would really need to discuss it with Mag.
as a side note, the # of list_loc may be wrong, but I believe this would work.
Mag has spent a lot of time working with me to understand all of this, and we both have made a few errors. Well I have made more than a "few". I do not dare look back. I skip the whole lot, and get to the end. This is to prevent learning back bad information.
thinking back on the whole topic, he may have meant the following:
note_ptr rdword note_ptr, par <---- this is a variable equal to a word read from par
count add par, #2 <---- this is a variable equal to par +2
output_ptr rdword output_ptr, count <-----this is a variable equal to word read from par + 2
list_loc add par, #4 <-----this is a variable equal to par + 4
list_ptr rdword list_ptr, list_loc <-----this is a variable equal to word read from par + 4
This would make more sense if using the variables for multi-purpose. I know he has to cope with a large amount of frustration dealing with me, which is bound to cause errors.
Their use like I have above makes much more sense to me, but then again, it all doesn't make much sense at all.
It's already been mentioned, but as I asked I want to explain the thing in my own words now.
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 0 ] := note_addr ' Pointer for note value
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 1 ] := output_addr ' Pointer for value returned
word[noparse][[/noparse]note_cuelist_addr][noparse][[/noparse] 2 ] := cuelist_addr ' Put the cutelist pointer in HUB ram
note_cuelist_addr is our word array. Variables are initialized with zero. SPIN is not typesafe. This means that you can use not_cuelist_addr as normal variable instead of as an array. My guess here is, that note_cuelist_addr is the same as note_cuelist_addr[noparse][[/noparse]0]. Maybe I'll check that out somewhen.
So, what word[noparse][[/noparse] note_cuelist_addr ][noparse][[/noparse]0] will do is use content of note_cuelist_addr in the first dimension.
word[noparse][[/noparse] 0 ][noparse][[/noparse] 0 ] simply points to $0000 in HUB-RAM.
word[noparse][[/noparse] 0 ][noparse][[/noparse] 1 ] points to $0002 in HUB-RAM.
word[noparse][[/noparse] 0 ][noparse][[/noparse] 2 ] points to $0004
That means we overwrite a part in HUB-RAM with some special meaning. The first long in RAM for example is reserved for the SPIN internal variable clkfreq. And that's the dangerous part of this bug. If we later on have some code which reads clkfreq for calculating some timing it gets a wrong result and the timing will not be as expected. Now find that bug when your driver has been written·a half year ago·;o)
As the note_cuelist_ptr is defined in the drivers var section we can simply write
note_cuelist[noparse][[/noparse] 0 ] :=
...
Why did it work with rdlong ptr, par? Because this added another level of indirection. First we got the content of note_cuelist_addr, which is zero. Then we read the content of $0000 into note_par which really contained the adress of note. So, I used a workaround for the bug, but did not fix it.
Ok, the par is used in a wrong way. I really thought I saw my code working like that. I leaned my lesson!
So, this will work definitely.
I attached my test-code to the post, so you can see that par as well. Guess this thread is long enough, so I don't put it in the post directly. We have to discuss enough other topics now ;o)
The test needs the FullDuplexSerial and pleas adjust the clock-settings as I use a 10MHz crystal. Open the terminal and press any key and it will start after a second.
@james:
I'm not frustrated, I'm learning by explaining things. I just started with the prop a few weeks ago (or maybe one or two months - time is passing so fast when you do interesting things).
Well if nothing else, you are getting a chance to learn how to explain things in different ways. This is the secret of teaching someone.
Now the main question, is this faster than a Spin lookdown statement?
The reason I ask, the byte array I'm working with may be up to 128 words long. Yea, right now I'm working with byte, but the array may need to change to accommodate words.
Putting this into lookdown is a problem. Because of the syntax of Spin, that line gets really long, and I haven't experienced a "wrap around" yet. I do not know of a way to cause a line to wrap around, so using a PASM lookdown seems to be the way to go.
As for my inaccuracies with the word[noparse][[/noparse] 0 ][noparse][[/noparse] 0 ], that explains the problem I've been having. Before when i tried the code, my propeller would lock up. I was killing the reserved variables the prop needed to run.
Now that the driver is written, I'm going to try it with my existing code that will do away with the regular lookdown.
Comments
I give it up for this night ;o) Maybe I should simply write the driver for you and explain what I did for what reason.
The code does have these in it, but it doesn't show for some reason:
word[noparse][[/noparse]note_cuelist_addr](1) := @output_num
long[noparse][[/noparse]note_cuelist_addr](1) := @cuelist_addr
Had to use "(" to get them to show this time.
James L
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
I'm still working on it.......have to digest what you wrote the last time.
James L
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
another edition.
Yea I have edited about 4 times on here already.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
Post Edited (James Long) : 4/23/2009 1:35:42 AM GMT
Ok ... let's try again (with saving from time to time, so please be aware that this post might change while you read it until i write DONE at the end)
James .. the goal comes closer ;o)
Now some words for better understanding the problem with the words and bytes in PASM:
The propeller has two different adress-busses with different adress ranges and different organisation.
One adress-bus is the COG-RAM adress-bus. It's 9 bit wide, so it ranges from 0-512 ($000-$1FF / %0_0000_0000 - %1_1111_1111). As you can read in the propeller specs, each COG has 2kBytes of RAM. So not each byte has it's own adress! Each adress points to a LONG (4 byte) instead. 4Bytes x 512Adresses·= 2kBytes.
PASM runs in COG-RAM and only knows LONG-adresses. Now let's have a look on the latest code from you and let's assume the variables would be placed at adress $100 (256 in decimal).
$100 = ptr··········
-> so, each PASM command using ptr will use adress $100 ( mov ptr, par will be translated to mov $100, $1f0 [noparse][[/noparse]I'm not sure if PAR really is at $1f0 and I am to
lazy to search for it currently - so let's simply assume it's true]
$101 = list_ptr, count and note_val
-> as memory adress $101 is a long it can hold more data than just the list_ptr which is a word. But for PASM only the label list_ptr makes sense. Count would be at adress 256,5 and note_val would be at adress 256,75 ;o) But we don't have fractional adresses. So, when you want to access count in PASM you have to use the long which includes
count. But then count is not at bit 0-7 but on bits 16-23. Adding 1 would change list_ptr and not count. To extract count needs some extra steps which increases runtime and
memory usage. And that's why I said: You have to know what you do if you use different data types for you PASM variables.
$102 = list and 3 undefined bytes
And why does the compiler then allowes labels for words and bytes. Well ... because DAT is only data. Data can have different meanings, it could be image data ... or sound data ... whatever. In our dat section the data is a list of PASM opcodes and PASM variables. Please remember, the whole SPIN file first is placed in HUB-RAM. The HUB has it's own adress bus, which is 16 bits wide, ranging from 0 to 65535. With the first 32k-adresses you·can access RAM with the second 32k-adresses you can access the buildin ROM.
And of course in SPIN you are allowed to read/write bytes, words and longwords from HUB-memory and there you can use those labels.
DONE
Post Edited (MagIO2) : 4/23/2009 10:45:45 AM GMT
Rather than posting code at the moment, I'll answer the few inaccuracies I may have created.
Cuelist is always the same byte array. This information never changes. The notes on the other hand do change, but they should be placed in the same place (address) every time (my thinking).
Your right, I will need to reset count and ptr.
I knew waitcnt worked differently, but do not have a clue how long to wait.
I now understand the long aligned problem. I greatly appreciate your explanation. For now, I'll use long values. I'm not sure if I am ready for any other alignment. It appear very difficult to deal with.
When I get a chance I'll rewrite, and post the code.
James
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
Post Edited (James Long) : 4/23/2009 7:27:56 PM GMT
This seems to me like it could work. (I did not try it) Now we can start with some optimizations:
Let's think about that: "... but they should be placed in the same place (address) every time" you talked about note and cuelist.
So, why do you read the adresses with each iteration? When the adresses of note and cuelist never change, there is no need to read the adresses with each
loop. Just do it in the initialisation phase of your PASM.
PAR is nothing else than·a COG-RAM adress with the purpose of passing one parameter to the PASM. This is called a special register. As it's simply COG RAM you can
change PAR and there is no need to copy PAR to PTR.
Nice, isn't it? We reduced size by 6 longs, with potential of saving another 5 longs (if you follow my hints). And we reduced runtime of the loop.
Happy digesting ... then the next optimizations will follow ;o)
·
I have reordered the code some to use djnz.
As for moving them to the top, I'm not sure what you mean. Also, I'm not sure where to get another zero from the code. The count will not always come to zero.
You may be thinking of an arrangement I can't not see at this point. Using spin for so long, I guess habits are hard to break.
I've omitted everything above the DAT to save space.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
Post Edited (James Long) : 4/24/2009 2:17:07 AM GMT
If you are out there, and have hit that point, some public airing of the laundry may be needed. I hate to show how hard it is for me to learn something like Pasm, but I feel the end justifies the means.
If you are following, I hope your light went on before mine did.
James L
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
It seems like we're alone in this thread.
If I count the lines which have been striked out I count 6 lines. This means we saved 6 longs in comparision to the version before.
You are right with what you say about the zero. I forgot about the other tip to use DJNZ. That of course means that counter is no longer initialized with zero.
······················· ' we never need the code above :start again, so we can overwrite with runtime variables. The time will come, when you need
························' each long for your PASM code to fit into the COG-RAM ;o) Note: you could even move the _ptr variables to the top. Then
······················· ' you won't need a single long.
What I mean is: From label entry up to label :start we have some code which is only needed on the first call of our driver. When calling cognew( @entry, @note_cuelist_addr ). All the jump we have in the code don't go back to the very beginning. So, here we have 5 longs that we can use in our loops. By doing so, we can save some memory at the end.
See what I mean? We reuse the memory. First it is filled with our code, but when the code ran we can use it as any other memory adress. So we saved another 5 longs. I know we don't need to save for this little driver, but if you learn to save memory you get familiar with it and you don't have to twist your brain when you really need it.
Please be aware this not only saves memory in COG-RAM, it also saves memory in HUB-RAM as each long defined has a real memory adress and data in HUB-RAM.
Of course you can only use this trick for variables which need not to be initialized.
What does RES mean? Well, it's doing the same with the difference that it uses memory behind our code and long, word, byte definitions. That's very important! The RES·has to be used after all code AND variable declarations of the PASM snipped for one COG! Otherwise you have different labels point to the same memory adress. In opposite to long, RES does not put data to HUB-RAM, but you need some COG-RAM for it. So, if you used all 496 longs for code and data it's not possible to add a RES. Overwriting no longer needed memory is then the only way to have some extra room for variables.
Hope this was understandable for you! Again in short form:
long (and of course word and byte) needs memory in HUB-RAM and in COG-RAM and·they are·initialized it with a value.
res points to an adress in COG-RAM and needs no memory in HUB-RAM - that's why you can only use it for variables which need not to be initialized by default.
IMPORTANT: RES has to be defined AFTER code and long/word/byte of the PASM for one COG.
In your code you still have one little obstacle:
Ok ... now the code looks pretty good. Maybe I'll give it a try this evening/night. But we're not done with optimizing. Guess this thread will be a nice tutorial when we're done.
What's to come next? I suggest that you now write the code which uses our driver. Based on that we can do more improvements.
Post Edited (MagIO2) : 4/24/2009 11:07:14 AM GMT
Hmmmm.....I didn't even know that was legal. I do not think I have ever seen what you did here.
That is interesting. I didn't know that could be done. It is a totally new concept to me. I haven't paid a lot of attention to PASM in the past, so I may have missed this done before.
As for the djnz, I missed that concept.
The nice thing about this driver we built as it could be tested with nothing but hyperterminal. Doesn't really need any additional hardware. I am using a SD card to read data into the byte array, but there are ways around that.
I actually have code I was writing this for, but it is a mess, and huge. I'm not sure that would be good to test with.
I'll think about writing something to test with.
James L
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
This is the bug in the SPIN-part. We don't want to pass the adresses of the start function parameters. We want the variable adresses which are passed to start when we call it:
start( @note, @output, @cue_list )
Already talked about that (never pass an adress of a function parameter) and still I did not see it this time ;o)
But now it works.
As you might know some bugs don't show up immediately! So, what was wrong with the part that baffled me?
See the difference? Maybe you learned enough, so that you can explain it to me? And what makes this bug dangerous.
Of course the code which reads the adresses has to be changed back now that we corrected this bug:
·
Yep, you are right, I didn't see that either. Using the address of the address would be of little use.
I would assume, because the way the items are declared, all of those are longs.
But if that in fact the case, wouldn't the "add par, #2" need to be "add par, #4".
Just my take on the subject.
I know it didn't work with the last iteration with all of the variables declared at the bottom.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
So +2 should be fine. What I have trouble with is the actual instruction
PAR is a read-only register and can't be used as a destination (without unwanted side effects). Therefore you'll usually find stuff like this at the beginning if more than one parameter is to be passed into the PASM section.
HTH
If you scroll back up, you will see Mag suggest to initialize some of the data at the beginning of the PASM. Using the following:
If you notice par is not copied at all.
What I don't understand is why the following will not work:
I'm not sure at this point why.
I haven't done any deep thinking about it, so I may be missing the target.
James L
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
Anyway I was referring to MagIOs last posting (starting with Ah ... the dark clouds of dumbness dissappear.) which I thought was up-to-date.
You are right, it is a typo.
the code should be:
Might be a typo on my part.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
Both are legal in Spin:
This is also legal, with the declarations in the previous post:
I have no idea why, but spin will let you do this.
James L
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
He may be doing something illegal with PAR, but I can not say. I'm just trying to learn.
I know you can write to PAR, for Mike Green does it. I'm not sure what he is writing to PAR, but I have his exact code which does. I know it works. I'm using it.
As for adding or subtracting, I have no idea.
I have to take what I am taught at face value and assume the instructor has done these procedures before. Mag appears to know what can and can not be done, so I believe what he says is true.
James L
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
Mike,
Just for comparison, for my use:
When you do not use "word", what does it mean?
I'm trying to learn this, and it is important for me to understand the significance of each.
James L
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
Example for Hydra (adjust LED(6) pin for your setup):
If the LED stays dark, change the cmp instruction to cmp two, #42 wz.
I'm not doubting what you are saying, but you are not initializing the variables like Mag did.
At no time did he do like you have done above.
He is just using variables to simulate a situation. This is what I understand to be happening.
I understand what he is doing to be the following:
Like I said. I have not tested the code. We were using a copy of par before he changed the variables to the top of the PASM. Then he implemented the above code.
To get into a deep discussion, you would really need to discuss it with Mag.
as a side note, the # of list_loc may be wrong, but I believe this would work.
James L
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
Post Edited (James Long) : 4/25/2009 2:32:14 AM GMT
thinking back on the whole topic, he may have meant the following:
This would make more sense if using the variables for multi-purpose. I know he has to cope with a large amount of frustration dealing with me, which is bound to cause errors.
Their use like I have above makes much more sense to me, but then again, it all doesn't make much sense at all.
I'm still trying to get the "@" symbol right.
James L
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services
Post Edited (James Long) : 4/25/2009 2:54:28 AM GMT
note_cuelist_addr is our word array. Variables are initialized with zero. SPIN is not typesafe. This means that you can use not_cuelist_addr as normal variable instead of as an array. My guess here is, that note_cuelist_addr is the same as note_cuelist_addr[noparse][[/noparse]0]. Maybe I'll check that out somewhen.
So, what word[noparse][[/noparse] note_cuelist_addr ][noparse][[/noparse]0] will do is use content of note_cuelist_addr in the first dimension.
word[noparse][[/noparse] 0 ][noparse][[/noparse] 0 ] simply points to $0000 in HUB-RAM.
word[noparse][[/noparse] 0 ][noparse][[/noparse] 1 ] points to $0002 in HUB-RAM.
word[noparse][[/noparse] 0 ][noparse][[/noparse] 2 ] points to $0004
That means we overwrite a part in HUB-RAM with some special meaning. The first long in RAM for example is reserved for the SPIN internal variable clkfreq. And that's the dangerous part of this bug. If we later on have some code which reads clkfreq for calculating some timing it gets a wrong result and the timing will not be as expected. Now find that bug when your driver has been written·a half year ago·;o)
As the note_cuelist_ptr is defined in the drivers var section we can simply write
note_cuelist[noparse][[/noparse] 0 ] :=
...
or alternativels
word[noparse][[/noparse] @note_cuelist_addr ][noparse][[/noparse] 0 ] :=
...
Why did it work with rdlong ptr, par? Because this added another level of indirection. First we got the content of note_cuelist_addr, which is zero. Then we read the content of $0000 into note_par which really contained the adress of note. So, I used a workaround for the bug, but did not fix it.
Ok, the par is used in a wrong way. I really thought I saw my code working like that. I leaned my lesson!
So, this will work definitely.
I attached my test-code to the post, so you can see that par as well. Guess this thread is long enough, so I don't put it in the post directly. We have to discuss enough other topics now ;o)
The test needs the FullDuplexSerial and pleas adjust the clock-settings as I use a 10MHz crystal. Open the terminal and press any key and it will start after a second.
@james:
I'm not frustrated, I'm learning by explaining things. I just started with the prop a few weeks ago (or maybe one or two months - time is passing so fast when you do interesting things).
Well if nothing else, you are getting a chance to learn how to explain things in different ways. This is the secret of teaching someone.
Now the main question, is this faster than a Spin lookdown statement?
The reason I ask, the byte array I'm working with may be up to 128 words long. Yea, right now I'm working with byte, but the array may need to change to accommodate words.
Putting this into lookdown is a problem. Because of the syntax of Spin, that line gets really long, and I haven't experienced a "wrap around" yet. I do not know of a way to cause a line to wrap around, so using a PASM lookdown seems to be the way to go.
As for my inaccuracies with the word[noparse][[/noparse] 0 ][noparse][[/noparse] 0 ], that explains the problem I've been having. Before when i tried the code, my propeller would lock up. I was killing the reserved variables the prop needed to run.
Now that the driver is written, I'm going to try it with my existing code that will do away with the regular lookdown.
James L
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
James L
Partner/Designer
Lil Brother SMT Assembly Services