Assm questions - getting started....
Chris_D
Posts: 305
Hello,
I am starting to work with Asm.· I have never done asm, but want to explore it on the propeller.· I have written a spin routine and am working towards converting it to asm, but need to understand what I am doing along the way.· This code below is basically nonsense at this point, but it serves as a tool to help me understand how to do things.· Below the code are my explanations of what I think should happen, I am hoping someone can take some time to clarify or correct any missunderstandings....
*******************
Dat
······················· Org 0
:Loop·················· CMPS _TenMsecFlag,CNT·· WC
············· If_N···· Jmp #:Loop
······················· Mov _TenMsecFlag, Cnt
······················· Adds _TenMsecFlag, Ten_Msec
······················· Jmp #:Loop
Ten_Msec····· Long····· 800000·····
_TenMsecFlag· RES······ 1
*******************
Dat {this line informs the prop tool that I am defining an asm routine (or other data statements)
Org 0 {informs the compiler to start at cog address zero
:Loop {is a label to which I can reference with a Jmp statement
CMPS _TenMsecFlag,CNT·· WC {This is the first of two lines needed to perform a conditional statement (IF~then).· It will compare two signed variables, checking to see if _TenMsecFlag is less than CNT and if it is, will set the C register as 1.· The WC is what actually causes the C register to be written.
If_C···· Jmp #:Loop { IF_C tests the C register and will perform the rest of the line if C =·1 (meaning that CNT is·less than _TenMsecFlag).· The JMP instruction specifies that flow is redirected to the label :LOOP.· This keeps the loop repeating until the CNT passes the _TenMsecFlag value.
Mov _TenMsecFlag, Cnt {this moves the value·at CNT into _TenMSecFlag.
Adds _TenMsecFlag, Ten_Msec {This adds the signed value of Ten_MSec (Defined lower down) to _TenMSecFlag.· With this, is sets up the target for the CNT to catch up too which should be 10 milliseconds.
Jmp #:Loop {As previous, this will redirect program flow back up to the Loop label
Ten_Msec····· Long····· 800000 { This creates a sort of constant in the form of a Long variable which is equal to 800000.
_TenMsecFlag· RES······ 1 {This reserves a single Long variable, which is local to this routine, that can be manipulated etc.
I suspect I am way off base on most of this, but it is a place to start from. So, if anyone cares to help me out, I sure would appreciate it.· I am sure there will be MANY other questions along the way too.
Thanks!
Chris
·
I am starting to work with Asm.· I have never done asm, but want to explore it on the propeller.· I have written a spin routine and am working towards converting it to asm, but need to understand what I am doing along the way.· This code below is basically nonsense at this point, but it serves as a tool to help me understand how to do things.· Below the code are my explanations of what I think should happen, I am hoping someone can take some time to clarify or correct any missunderstandings....
*******************
Dat
······················· Org 0
:Loop·················· CMPS _TenMsecFlag,CNT·· WC
············· If_N···· Jmp #:Loop
······················· Mov _TenMsecFlag, Cnt
······················· Adds _TenMsecFlag, Ten_Msec
······················· Jmp #:Loop
Ten_Msec····· Long····· 800000·····
_TenMsecFlag· RES······ 1
*******************
Dat {this line informs the prop tool that I am defining an asm routine (or other data statements)
Org 0 {informs the compiler to start at cog address zero
:Loop {is a label to which I can reference with a Jmp statement
CMPS _TenMsecFlag,CNT·· WC {This is the first of two lines needed to perform a conditional statement (IF~then).· It will compare two signed variables, checking to see if _TenMsecFlag is less than CNT and if it is, will set the C register as 1.· The WC is what actually causes the C register to be written.
If_C···· Jmp #:Loop { IF_C tests the C register and will perform the rest of the line if C =·1 (meaning that CNT is·less than _TenMsecFlag).· The JMP instruction specifies that flow is redirected to the label :LOOP.· This keeps the loop repeating until the CNT passes the _TenMsecFlag value.
Mov _TenMsecFlag, Cnt {this moves the value·at CNT into _TenMSecFlag.
Adds _TenMsecFlag, Ten_Msec {This adds the signed value of Ten_MSec (Defined lower down) to _TenMSecFlag.· With this, is sets up the target for the CNT to catch up too which should be 10 milliseconds.
Jmp #:Loop {As previous, this will redirect program flow back up to the Loop label
Ten_Msec····· Long····· 800000 { This creates a sort of constant in the form of a Long variable which is equal to 800000.
_TenMsecFlag· RES······ 1 {This reserves a single Long variable, which is local to this routine, that can be manipulated etc.
I suspect I am way off base on most of this, but it is a place to start from. So, if anyone cares to help me out, I sure would appreciate it.· I am sure there will be MANY other questions along the way too.
Thanks!
Chris
·
Comments
CNT is unsigned, so used the unsigned versions of everything.
RES will reserve space, so no code afterwards!. There is no concept of local variables. Just local symbols between global symbols.
The rest seems ok. I do not remember if IF_N is equivalent to IF_C though. IF_B (below) will probably serve the same purpose.
You can't really compare _TenMsecFlag and CNT directly, signed or unsigned. Well, you can, it just won't do what you want.
Consider the case when incrementing _TenMsecFlag causes it to overflow. Signed or unsigned, it will be less than CNT.
The "Mov _TenMsecFlag, Cnt" after the wait loop is unnecessary and will in fact introduce a small error in the wait time. On the other hand, you'll want to initialize _TenMsecFlag before entering the loops.
Too bad you chose such a tricky task for your first foray into assembly language, but you seem to be well on your way.
They do the same thing except long 0 will let you know for sure what the starting value is and res does not. Though if you are extremely short on space res will save you a long in your final program.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Need to make your prop design easier or secure? Get a PropMod has crystal, eeprom, and programing header in a 40 pin dip 0.7" pitch module with uSD reader, and RTC options.
Ok, mpark is right, your trying to duplicate the waitcnt. But I think in some environments it would be handy to have a own implementation of waitcnt. If you have to wait for a long time (and a 10th of a second is definitely a long time for a uC - it could exercise 2million operations in that time) it could make sense to do something else in between continuously.
In fact the problem is that CNT is a neverending counter with an ending number of possible values. So, when the limit is reached ($ffff_ffff_ffff_ffff) it starts from the beginning. So, in the area close to the wrap-around your comparation will not work. I guess you have to check the result of your add. If this wrapped around (C flag is set), you have to skip the cmp unless CNT is positive.
Have to think about that when I'm back home.
Did you understand what mpark tried to tell you in with "Mov _TenMsecFlag, Cnt" ... ?
Just want to point that out a little bit clearer for newbies:
If you do it your way, you get the counter-value at a certain point of time. The mov instruction needs 4 clocks. You don't know exactly at which of those 4 ticks the CNT is read (only Chip knows that ;o). And the commands before doing the mov need some time as well. So, in the end the time you wait will be Ten_Msec plus the runtime of the commands that have been executed since you detected the match with _TenMsecFlag. If you have a wait time which is not an even multiple with the clockticks you need for the commands this can even vary.
So, this is not good for programming a clock as the deviation grows with the runtime.
If you want the timing to be more accurate, you should do it that way:
Dat
Org 0
_TenMsecFlag mov _TenMsecFlag, CNT ' <- nice trick to save one LONG by reusing the command as variable afterwards (of course you should not run this 'code' again)
:Loop CMPS _TenMsecFlag,CNT WC ' <- this is the part I still have to think about as it does not work as desired
If_N Jmp #:Loop
Adds _TenMsecFlag, Ten_Msec ' this always adds to the last time you waited for, so the delta will be always the same
Jmp #:Loop
Ten_Msec Long 800000
Please note: Of course a self-defined waitcnt can never be as accurate as the waitcnt. CNT reached the exact value of _TenMsecFlag somewhere in the middle of executing the :Loop and the extra handling for the wraparound will not make it better. But it is only a small deviation which does not sum up.
Post Edited (MagIO2) : 3/25/2009 12:42:41 PM GMT
I can see from your comments that I am confused about a few things though.
Regarding the CNT function.· If I understand your cautions, this counter runs from 0 to the overflow of a Long $ffff_ffff_ffff_ffff as opposed to running from max negative value to max positive value?· If this is the case, then I will have to rethink the wrap around problem. For some reason, I had it in my mind that CNT was a LONG and would wrap around the same way LONGs do.· This is also the reason I went with the COMS and ADDS functions.
Now onto a few other items I need to clarify...
In the following...·· Jmp #:Loop·· ... why do I use the # symbol?·I thought I understood this but when I re-read the manual again, I got a bit confused about its use here.
This command...··· Ten_Msec····· Long····· 800000·· ...creates a Long variable (pointer) and assigns it the value of 800000.· Instead of using a variable, could I have used the literal value of 800000 in the program?
Thanks guys
Chris
·
Immediates can only be 9 bit. So, if your Ten_Msec is only 9 bit (max. 511) wide you can use # instead. If it is bigger than you need to store this value as long somwhere.
CNT runs from 0 to $ffff_ffff_ffff_ffff and then starts from 0 again. In terms of signed values this would be a switch from MAX_NEGATIVE to 0. And another switch would be from $07fff_ffff_ffff_ffff to $1000_0000_0000_0000. That's why Ale told you that CNT is unsigned. Because this means a switch from MAX_POSITIVE to -1 in sense of signed numbers. So, when using CMP instead of CMPS you would have one problem less.
Say your CNT is $ffff_ffff_ffff_0000 when you reach your mov/add. The result would be $0000_0000_0000_B350.
If you do CMP now CNT will be bigger than that until it wrapped around. That's why you have to wait with the CMPs until CNT wrapped around. Then you would compare $0000_0000_0000_B350 against 0 and upwards, which gives you the right result.
PS: xxx -> bugfix
Post Edited (MagIO2) : 3/25/2009 12:46:02 PM GMT
This is starting to make more sense to me now.· Once I digest this first step a bit more I will be expanding on my quest.· I have been programming in BASIC for about 25 years now, tried C a couple times and just hated it.· But so far, Asm seems very interesting!·
Thanks!
Chris
You can·consider CNT signed or unsigned. In either case,·its value continually increases by 1 until a certain point when its value suddenly decreases by 232-1. The only difference is where that point is. CMP doesn't buy you anything over CMPS in this situation, imho.
·
With CMP you only have the problem of a not working compare once. (at $ffff_ffff->$0000_0000)
With CMPS you have the problem twice. (at $ffff_ffff->$0000_0000 and at $7fff_ffff -> $1000_0000)
And I am not sure what the adds is doing when you add 800000 to $7fff_ffff. Guess the result will be with sign bit still set to 0.
In reading through the manual, it seems to indicate that all Longs are signed but yet many operations provide for unsigned.· So then I tried searching through the manual to determine how to create an unsigned variable and didn't find anything.·
I suspect I am missing something or am confused about something regarding this.
How would I go about making an usigned variable?
Chris
Spin uses all signed arithmetic, but, for most operations, there's no difference between signed and unsigned values. In particular, addition and subtraction work the same. There's no way to tell Spin that a variable contains an unsigned 32-bit value in the range $80000000 - $FFFFFFFF. You just need to be careful about comparisons, multiplication, and division.
Chris
When we talk about signed and unsigned we talk about the usage of the bits. In unsigned usage bit 31 is a binary digit. In signed it is the bit which tells you, whether the value is negative (1) or positive (0). Because of this different meanings you need different compare, add and subtract commands.
For shorter examples I now use signed/unsigned bytes where bit 7 is the sign and this is no working code (as you can't add two immediate values), it only shows the principle
add #%01111111, #%00000001 WC -> result is %10000000 with carry not set
adds #%01111111, #%00000001 WC -> result is %00000000 with carry set (or is it %10000000 but carry is set - not sure about that currently)
cmp #%00000000, #%10000000 WC -> carry is set because %10000000 is bigger than %00000000
cmps #%00000000, #%10000000 WC -> carry is not set because %10000000 (-1) is not bigger than %00000000 (0)
Of course you should very clearly know what you do, when you mix signed and unsigned commands on the same register.
Spin seems to be a bit more restrictive in the usage of a long as the most numerical operators assume it to be signed.
Post Edited (MagIO2) : 3/25/2009 2:25:15 PM GMT
I finally got some programming time this afternoon so I wrote a very simple SPIN program to find out the range of·CNT.· While I am not sure CNT is the same in Spin or ASM, when used in this example, CNT wraps around· and starts back up at -2137508107 (obviously the timing is such that I am not printing at the exact moment of wrap around).· So I would expect that CNT wraps around the same way when used in ASM?
Chris
PUB start
· SerIO.start(31,30,0,115200)
·· Delay := 20000000
· 'start led flashing
· led.start(16)············· ' Start the led object (pin 16 flashes a VGA led on the demoboard)
· repeat·· 'loop forever more
···· WaitCnt(Delay + CNT)
···· SerIO.Dec(CNT)
···· SerIO.str(string("<",13))
····
/Nicke
·
No .. I did not think about it hard enough. Of course it is as you said, which makes calculation easier. You still can add positive numbers to negative numbers with the right result.
Secondly, CNT is incremented on each clock cycle and an instruction takes (mostly) 4 clock cycles. So don't fall into the trap of trying to compare equal because there is a 3/4 chance it will never equal.
Thirdly, the instruction cycle pipeline is IdSDeR where I=fetch instruction fetch, d=decode (and previous instruction wRiteback), S=source data fetch, D=destination data fetch, e=execute instruction (and fetch next I=instruction), R=wRiteback result (and decodes next instruction),....
So your instruction Mov _TenMsecFlag, Cnt should fetch CNT on the "S" cycle.
However, the waitcnt instruction I believe CNT is compared on the "e" cycle, with the "e" cycle being repeated until a match is found.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Links to other interesting threads:
· Home of the MultiBladeProps: TriBladeProp, SixBladeProp, website (Multiple propeller pcbs)
· Single Board Computer:·3 Propeller ICs·and a·TriBladeProp board (ZiCog Z80 Emulator)
· Prop Tools under Development or Completed (Index)
· Emulators: Micros eg Altair, and Terminals eg VT100 (Index)
· Search the Propeller forums (via Google)
My cruising website is: ·www.bluemagic.biz·· MultiBladeProp is: www.bluemagic.biz/cluso.htm