Stuck on ASM problem :RESOLVED THANKS TO ALL
Wurlitzer
Posts: 237
The following code snippit works fine if I have 2 (or more)·instructions inserted where shown. The instruction type does not seem to matter as even NOPs work.
This routine should use data read·out of a circular queue (it does) and eventually jump to some code to write a long to the hub ram and does if I have these 2 (or more) instructions added. If I remark them out, nothing is written to the hub ram.
The "CALL #TestWrite" does work as I can see the another HUB ram location change to the expected value.
My guess is that somehow the JMP to "StatusJumpTbl" gets screwed up without the 2 added instructions.
The address in the cog of the StatusJumpTbl is $1F (the address is $1D without the added instructions).
For testing,·the value of tmp_z is $0B (before it is added to the StatusJumpTbl address) and should force a jump to "ControllerCmd" which it does if
I have the··extra instuctions where shown.
If I just place 2 extra NOPs in the same location, then do a call #testwrite from the routine at "ControllerCmd" I get a value of $20 which is exactly what it should be. $0B-$0A = 1 plus the StatusJumpTbl address of $1F = $20. If the extra code is not present then it seems like "ControllerCmd" is
never jumped to.
Post Edited (Wurlitzer) : 2/24/2008 3:25:27 PM GMT
This routine should use data read·out of a circular queue (it does) and eventually jump to some code to write a long to the hub ram and does if I have these 2 (or more) instructions added. If I remark them out, nothing is written to the hub ram.
The "CALL #TestWrite" does work as I can see the another HUB ram location change to the expected value.
My guess is that somehow the JMP to "StatusJumpTbl" gets screwed up without the 2 added instructions.
The address in the cog of the StatusJumpTbl is $1F (the address is $1D without the added instructions).
For testing,·the value of tmp_z is $0B (before it is added to the StatusJumpTbl address) and should force a jump to "ControllerCmd" which it does if
I have the··extra instuctions where shown.
If I just place 2 extra NOPs in the same location, then do a call #testwrite from the routine at "ControllerCmd" I get a value of $20 which is exactly what it should be. $0B-$0A = 1 plus the StatusJumpTbl address of $1F = $20. If the extra code is not present then it seems like "ControllerCmd" is
never jumped to.
ParseQueData mov DataByte2_z, QueReadData_Lng and DataByte2_z, #$FF 'strip off everything except LSbyte mov StatusByte_z,QueReadData_Lng shr StatusByte_z, #16 mov tmp_z, StatusByte_z shr tmp_z, #4 'removes Channel Nibble and tmp_z, #$0F 'FOR SOME REASON I NEED 2 (OR MORE) INSTRUCTIONS BELOW. EVEN 2 NOPs WORK. WHY? mov loopcounter, tmp_z 'REMOVE call #testwrite'REMOVE cmp tmp_z, #$0A wc,wz,nr if_b jmp #MainLoop_z 'if < $0A then jump to main loop cmp tmp_z, #$0F wc,wz,nr if_a jmp #MainLoop_z 'if > $0F then jump to main loop sub tmp_z, #$0A wc,wz'$0A points to the first entry in table. add tmp_z, #StatusJumpTbl jmp tmp_z 'jump to statusJumpTbl based upon value of StatusByte '=================================================================================== StatusJumpTbl jmp #MainLoop_z '$Ax NOT USED Aftertouch jmp #ControllerCmd '$Bx Controller jmp #ChannelMessage '$Cx Program change jmp #mainLoop_z' '$Dx NOT USED Channel pressure jmp #mainLoop_z 'NOT USED'$Ex Pitch wheel Transposer values jmp #sysCommand_z '$Fx System command
Post Edited (Wurlitzer) : 2/24/2008 3:25:27 PM GMT
Comments
know if I need to but it's something worth trying. I cannot see any reason why it wouldn't work or why
adding extra 'nop' instructions make it work.
Thanks for looking at the code.
I knew I had to wait 7-22 cycles to assure the validity of the returned data but overlooked (possibly) playing with the address of the RDLONG instruction prior to the 7-22 cycles. This would make sense!
HOWEVER!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
If that is the case, why would adding NOPs ·much further down in the code also work? From the RDLong I have 5 lines of code (minimum) prior to hitting the "ParseQueData" routine then 7 more instructions before I had added the extra code in my original post?
I just kept moving the extra NOPs further up stream until the code no longer functioned.
Does it make a difference that ReadQue returns sometimes and doesn't return at other times (jumps to mainLoop_z)? There's no stack, so the fact that ReadQue_ret is overwritten on the next call shouldn't matter.
Could the problem be one of code positioning rather than timing? By having two instructions, the following code is displaced forward in memory by two long words.
already anded that with $0F earlier. The 'cmp' before 'if_be' for adjusting the queue pointer might
also benefit from being a 'cmps'.
Is it worth posting your entire code in case it's something elsewhere we are not seeing which is having
an effect on behaviour ? Not that I can see any obvious reason why adding the NOP's fixes whatever
problem there is. Do they really fix the problem, or is that simply masking whatever's actually going
wrong ?
Thanks for the insite on how the COG waits as I was unsure if I played with the address before the RDLONG finished what would happen. Based upon your reply I do not have to be concerned as nothing happens until the RDLONG is finished.
The strange thing is the value read from the RDLONG is correct regardless if the rest of the code is working or not. I can see that in the HUB Ram.
Thank you for taking the time to look at the code.
"Could the problem be one of code positioning rather than timing? By having two instructions, the following code is displaced forward in memory by two long words."
Good thought! That is why I verified the JMP to StatusJumpTbl addressing. It seemed correct!
While I believe every variable in this assembly routine has been declared as a long, your comment merits some investigation. I would expect strange results like this·if I had not been careful with declaring bytes at improper boundaries.
·I will play with the number of inserted instructions to see if there is a pattern. I have tried 1,2,3 and 1 does not work but 2 or 3 does.
This assembly routine is one of 2 in the same SPIN file if that makes any difference. I have checked for declaration conflicts and different entry points etc. SIDE COMMENT: Is there any way to have just assembly code in its own file rather than some SPIN calling 1 or more assembly routines. I understand the need for at least one spin routine but I would like separate files for pure assembly.
Mike & Hippy let me play with this some more and if I cannot spot the issue, I'll post the code. As it is under development I would want to add comments as to where the functioning code ends and the under development starts. Right now I just plant a bunch of JMP #MainLoop_z around to bypass the under development code.
Thanks again to both of you for taking the time to review this.
The compiler will align items to their proper boundaries with instructions on long word boundaries.
It may well be that there's an error in the assembly code that you left out, but it's manifesting itself in what you've shown.
only a suggestion: You could optimize your code easily to have a deterministic execution time of ReadQue. This would perhaps avoid unnecessary waiting on RDLONG.
Did you have used any assembly debugger to analyse your trouble?
Thomas
I wonder if maybe the JMP instruction has been pre-fetched into the pipeline and maybe tmpz has as well, so you've got a stale version of tmpz in addition to the jump. That would suggest that adding a nop between the add and the jmp should make it reliable but I've not tried that.
--Chuck
The insertion of the 2 NOPs is now located in the ReadQue routine on the 2nd and 3rd lines right after the rdlong instruction. All my testing seems to point to the an improper jump to the #StatusJumpTbl near the bottom of the ParseQueData routine if these added instructions are not in the program.
I have read the tutorial numerous times but, while very helpful, I obviously have not picked up on my error. There also seems to be 2 schools of thought, based upon some responses, as to what cautions need to be taken immediately after a HUB based instruction. At this point the code runs so fast I am not dwelling on perfect optimization.
The full code (minus today's edits) is in the previous post.
loopcounter is only used for the TestWrite routine
In the code below, with the mov instruction remarked out I get the·bottom image (attached). When I leave this dummy mov instruction in I get the·top image (attached)
What is supposed to happen is I capture from the Circular Queue, starting @ 1573 on the attached image "B0_07_xx" with xx being the variable in this message.
In 1571 you "SHOULD" see a $0B which is written by the TestWrite routine.
In 1568 you "SHOULD" see the variable from the B0_07_xx message in 1573 placed in the byte 1 position and byte 0 is left at its original value (it is)
You will notice when I do NOT get the $0B in 1571 I do get the proper result in 1568 (makes no sense at all). I cannot explain where the $04 is generated.
When I do not get the $0B I do get the byte written into 1568 as expected (again this makes not sense)
I have moved the testwrite before and after the SUB tmp_z, #$0A and have yet to·see a "1" which should be the correct result as tmp_z = $0B.
Please note the different values I receive depending upon the placement of Call #TestWrite
All I did was to cut and paste the mov and call to testwrite in 3 different places. In the code below I describe the values observed.
If I add the 2 NOPs shown, I can place the "mov wtf, tmp_z" anywhere in the lines of code shown and see the proper value in PropTerm and the rest of my code works. The jump to StatusJumpTbl works exactly as expected.
I even loaded the latest IDE 1.06 since my last post with no change in results.
I cannot base a project on something this flakey (assuming for just a moment it is not my code). The question was and remains, "why are 2 NOPs required after a SHR instruction"
This is back to where I started this thread.
I thought of your issue yesterday, when I wrote "...one unsolved due to the missung FULL code ..."
But you posted your full code don't you? No!
See, as soon as the COG is loaded with your code nothing can harm it any longer.... but before, it can!
I - and most likely others as well - have thoroughly looked through it. It was obvious from the beginning that this all had nothingti do with timing or so
(And no, there are no "two schools of thinking" only one!)
The first idea - before you posted this greater part of your program - was some misfit of RES, maybe a preset variable behing a RES,
which can happen very easily. However this was not the case, your total amount of machine code was rather low, and that left me with only
one solution: Your main program overwrites some of the assembly code before it is copied to the COG!
I am especially unhappy with these lines from your code here:
Such things scvhould not exist at all... They are bound to give you trouble, if not immediately then later...
Sorry I can't help more.... An idea to check this:
Just before the ORG 0 of your COG code, add a LONG 0[noparse][[/noparse]300]. If the smptoms disappear, then we are on the right track!
Post Edited (deSilva) : 2/23/2008 6:06:18 PM GMT
I think reality is that it is your code, even if not the code where the problem appears to be. I've seen
no inherent flakiness in the Propeller although I'm quite sure all of us have been convinced that our
problems must be down to hardware faults and not our software at some time.
Adding the NOP's doesn't identify the underlying problem nor resolve it, but simply masks it. I think
you are too focused on that fix and not on the bigger picture which is where the problem originates.
There's nothing fundamentally wrong with the PASM code which adding NOP would fix as best anyone
who has looked at the code can tell ( deSilva has a good point about hard-wired constants ), so the
obvious conclusion to me is that the underlying problem must be elsewhere.
The only suggestions I have are to use some debugging within the code ( difficult if moving things
around alters how it works ) or take a deep breath, stop digging one hole and move on to digging
another. Rip up all you've done and implement again using what you've learned along the way. A
rewrite is often not the painful experience it may look to be and can often be implemented quite
quickly and the result is often better for it.
As frustrating as it is, you won't be the first person up a blind-alley who has 'ballsed-up their code'
with no idea as to how or where they've done that. It's all part of the programmer's territory as is
knowing when to stop banging ones head against the wall and to move to a different approach or to
start again. Been there, done that, I have a whole drawer full of T-shirts.
While I did post all of the offending COG code·I did·not post the entire Spin object which contained 2 Dat statements hence 2 separate assembly program segments.
I thought I was saving everyone's time in not sending code that was 100% operational and "SHOULD" not have been related to the problem. I value your time as much as my own. In fact, I wasted your time. Sorry!
This morning I remarked out the entire offending code and created a separate spin file and place the code there with the associated spin code to launch the assembly portion and with one notable exception it compiled properly and ran fine. No added NOPs required.
In the StatusJumpTbl there was a JMP #ChannelMessage which was a section of code not yet developed and only had a JMP #MainLoop_z so I never expected any problem there.
However, the actual label was :ChannelMessage but when this code was in its original place with the other assembly program the compiler never complained about the mismatch as in the other assembly program there was a label "ChannelMessage".
As my problem code "SHOULD" have never jumped to ChannelMessage under these testing conditions I did not pay attention to the label conflict.
One last request guys! I would rather not face this issue again so I want to keep the assembly programs as completely separate as possible.
I have 1 spin program and 6 assembly routines. What is the safest way to launch all of these without tying up cogs with "helper" spin objects to launch all the assembly programs?
·
Of course I loaded your code into the PropellerTool and it complained!
I silently fixed that!
I thought: " some experimenting of Wulitzer... Never expected he send me anything functional anyhow..."
Would not have been this idiotic quarrel with Mosquite the other day, you should have received
a sharp remark from me, and we would have solved this puzzle already the day before yesterday
Post Edited (deSilva) : 2/24/2008 3:26:23 PM GMT
The normal way to initiate an assembly program in a cog while packaging up the assembly program in a object is to have a single Spin start routine in that object that simply does a COGNEW and returns a zero (false) if there are no free cogs or the number of the cog + 1 if a cog is started. This takes only a few bytes:
Sorry, my little idea here does not work!!
Post Edited (deSilva) : 2/24/2008 5:02:59 PM GMT
Jumping from on Cog into another Cog ... yes, somewhere round here I have that T-Shirt
Glad you got it sorted.
Segregation is not free of charge...
In contrast to what Mike implied (sorry Mike, I know you did not say it!) is that a PASM COG need not be initiated from the same object!
That is just a constraint for SPIN-COGs due to heavy internal workings (we now understand better than before).
Whenever you've got an address, you can "cog" it!
Hoewever you cannot easily get an address out of an Object It always needs a routine.
So the alternative to Mike's "standard procedure" is:
No real advantage in fact, but maybe interesting in some other context!
Note that the compiler distinguishes the both kinds of COGNEW by whether it gets a routine or a variable as first argument..
So COGNEW(modul.getAsmAddr,..)
is no good at all
Post Edited (deSilva) : 2/24/2008 5:21:49 PM GMT
The reason I was attempting to place 2 assembly routines in a single spin file was I thought I might get to the point where I had 7 cogs running and launching another assembly program using another spin object would not be possible.
I know now that while 2 assembly routines can reside in a single spin file I have to be 100% positive there are no duplicate labels. The compiler does not catch my stupid mistakes with labels.
Thanks again for you input.
No, there is no INSTANCE OF SPIN. this all works by one and the same SPIN interpreter in your main COG.
STOP will stop the assembly routines you so carefully have set up a moment ago
You most likely need no stop at all in the first place
STOP will stop the assembly routines you so carefully have set up a moment ago
You most likely need no stop at all in the first place
"
What I did, to completely eliminate the possibility of dup labels or variable names was from the top object:
Public Main
'then launch· spin file object MidiIn.
MidiIn.Start(26, @MasterArray) 'this object file contains minimal spin just to launch assembly program #1
'wait·enough time to allow this spin file to launch the assembly program·then stop the MidiIn spin object.
MidiIn.stop 'this only stops the spin portion the assembly continues to run. I made sure I stopped the right cog
'then launch a·2nd spin file object.
MidiParse.Start(26,@MasterArray) 'this object file contains minimal spin just to launch assembly program #2
'wait·enough time to allow this spin file to launch the assembly program·then stop the MidiIn spin object.
MidiParse.Stop 'again making sure the "cog stop", stopped the proper cog.
etc, etc until all the required cogs (minus 1) are merrily running their respective assembly routines.
Then finally 'launch the· assembly routine in the top object file
This seems to work well.
This may not be the best way to do this, but after the last week I wanted to make sure I did not duplicate the same problem.
But reading it again... No, I do not understand it...
How do your "STOP" routines look like???.
Post Edited (deSilva) : 2/25/2008 5:54:43 PM GMT