Shop OBEX P1 Docs P2 Docs Learn Events
[RESOLVED] SPIN stub code for learning ASM — Parallax Forums

[RESOLVED] SPIN stub code for learning ASM

MightorMightor Posts: 338
edited 2007-09-08 14:57 in Propeller 1
hey there,

I am trying to learn ASM for the Prop but the one thing I keep running into is how to make use of these bits of ASM code that I create. For example, I copied the Divide algorithm from deSilva's ASM guide:
' Divide x[noparse][[/noparse]31..0] by y[noparse][[/noparse]15..0] (y[noparse][[/noparse]16] must be 0)
' on exit, quotient is in x[noparse][[/noparse]15..0] and remainder is in x[noparse][[/noparse]31..16]
'
divide shl y,#15 'get divisor into y[noparse][[/noparse]30..15]
mov t,#16 'ready for 16 quotient bits
:loop cmpsub x,y wc 'if y =< x then subtract it, set C
rcl x,#1 'rotate c into quotient, shift dividend
djnz t,#:loop 'loop until done
divide_ret ret 'quotient in x[noparse][[/noparse]15..0], rem. in x[noparse][[/noparse]31..16]



However, I am not sure how I could write some SPIN code that could get the results back from this kind of routine. I have figured out how to pass stuff to it using the cognew and passing an address to an array of longs, for example. This is what I did for my PWM code.
I'd like to be able to use something like FullDuplexSerial to display the results of my little ASM programs so I can see what tweaking this, that or the other does to a result. Right now I have no idea how to do something like that. If some of the gurus could shed some light on this, that would be great! There is no way to call the ASM code (and get the return value) from inside SPIN code without calling cognew, is there?

Gr,
Mightor

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
| To know recursion, you must first know recursion.
| I'm afraid I might be phobophobic.

Post Edited (Mightor) : 9/9/2007 11:49:56 AM GMT
«1

Comments

  • deSilvadeSilva Posts: 2,967
    edited 2007-09-01 16:23
    Have a look at example 2 on p. 11: You can see how the COGs access "aCounter"; they can as well write to it.
    The simplest way is to store addresses of the variables you want to communicate through into DAT cells (as I did it with "patternAddr").

    You can use as many as you like.
    The good thing is, that you need not go through incrementing PAR all the time!

    There is a special technique for ADVANCED users: you can also use the DAT cells "remaining" in the COG as commucation cells themselves. But wait until the COG is loaded before using them smile.gif

    See my example for.... (searching...)
    Edit: It's here http://forums.parallax.com/showthread.php?p=669873 the 9th posting or so...

    Post Edited (deSilva) : 9/1/2007 9:31:37 PM GMT
  • MightorMightor Posts: 338
    edited 2007-09-01 18:20
    I must be missing something here, because this code is not working at all. The "answer" variable never gets the value 16 as it should.
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    VAR
      long answer
      
    OBJ
      Debug: "FullDuplexSerial"
    
    PUB start
      aAddr := @answer
      Debug.start(31, 30, 0, 57600)
      cognew(assign, 0)
      waitcnt(clkfreq + cnt)        ' wait a sec for cog to be ready
      repeat
        Debug.str(string("answer: "))
        Debug.dec(answer)
        Debug.str(string(10,13))
        waitcnt(clkfreq/10 + cnt)
      
    
    DAT
            org
    assign  mov _t, #16      ' assign the value 16 to _t 
            wrlong _t, aAddr ' write the value to the HUB variable
    
    assign_ret    ret
    
    aAddr         long 0
    _t            long 0
    
    



    I really kept it as simple as I possibly could. I must be doing something wrong, obviously, or the program would be printing "answer: 16" all the time. Could someone please tell me? I'm quite lost. I am sure the answer to this problem will result in a very loud "DOH!" from my side.

    Gr,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
  • Mike GreenMike Green Posts: 23,101
    edited 2007-09-01 18:49
    Do look at the example deSilva mentioned. You can't do the "wrlong _t,aAddr" because the compiler doesn't know the address of "aAddr" when it assembles the instruction. Use "cognew(@assign,@answer)" to start the assembly routine and use "wrlong _t,PAR" to copy the result back to "answer".
  • MightorMightor Posts: 338
    edited 2007-09-01 19:04
    Mike,

    I did look at it and for the first cog, it passes "0" as the argument and uses RDLONG DIRA, patternAddr (it's in the ex02a section). patternAddr has been assigned the address of aCounter as its value. So why would it work here and not in my example? I am storing the address of the answer variable in aAddr in much the same way. I'm not quite following this.

    Gr,
    Mightor

    This is the code I was looking at:
    VAR
      LONG aCounter
    
    PUB ex02
      patternaddr := @aCounter
      COGNEW(@ex02A, 0)
      COGNEW(@ex02B, @aCounter)
      REPEAT
        aCounter++
    
    DAT
            ORG 0
    ex02A
            MOV DIRA, #$FF ' 0..7 for output
    :loop
            RDLONG OUTA, patternAddr
            JMP #:loop
            patternAddr LONG 0      ' address of a communication variable
                                    ' must be stored here before loading COG
            ORG 0
    ex02B
            MOV DIRA, altPins ' 8..15 for output
    :loop
            RDLONG r1, PAR
            SHL r1, #8
            MOV OUTA, r1
            JMP #:loop
    altPins LONG $FF<<8
    r1 LONG 0
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
  • Mike GreenMike Green Posts: 23,101
    edited 2007-09-01 19:57
    OK, what you are seeing are two different ways to pass information back and forth. ex02A illustrates a technique that initializes the assembly program before it's started by storing data into the program before it gets loaded into the cog. Since the compiler doesn't know where the variables will be stored when the program is compiled, your program has to figure that out when it runs. ex02B shows a different technique where the address of some long word is passed to the cog in the PAR register when it's started. Typically this is the address of a variable or a group of variables in successive memory locations that your program can access.
  • R PankauR Pankau Posts: 127
    edited 2007-09-01 20:23
    I'm having similar issues, I read deDilva's tutorial and thought I understood it but when my asm wites back to cog memory it goes to the wrong place, I know because other variables are being over-written. I say wrong, but I'm sure it's my code that's wrong.
    I think I agree with Mightor in that in ex02A the variable patternaddr exists in both spin and dat space and is initialized in spin with the address of aCounter, so within asm we should have knowledge of aCounters location and should be able to use wrlong to write to it. does the address of aCounter have to be a multible of 4? how can we ensure that?

    my code isn't working so I don't want to argue that I'm right, I just don't see yet where I'm not correct.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    I'd rather have a bottle in front of me than a frontal labotomy
  • R PankauR Pankau Posts: 127
    edited 2007-09-01 20:30
    I should have said, "asm writes back to HUB memory" in my last post.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    I'd rather have a bottle in front of me than a frontal labotomy
  • R PankauR Pankau Posts: 127
    edited 2007-09-01 20:45
    along this same line of thinking are there limits to how many successive locations can be written back to HUB memory?
    for example say I have a var long buffer[noparse][[/noparse]7000] and when a new cog is started, par contains the first location of buffer. In asm will there ever be a problem with writing back to all 7000 locations?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    I'd rather have a bottle in front of me than a frontal labotomy
  • Mike GreenMike Green Posts: 23,101
    edited 2007-09-01 21:42
    Randy,
    Your last situation is never a problem. The assembly code can calculate any address in HUB memory and then can read and write to it. Remember that HUB addresses are byte addresses, so you'd have to multiply your buffer index by 4 (shift it left by 2), then add PAR to it. The result can be used with a RDLONG/WRLONG.

    It's been mentioned before that RDWORD/WRWORD ignores the least significant bit of the address and RDLONG/WRLONG ignores the least significant 2 bits of the address.
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-01 21:45
    @Mightor:
    (1) The COG is not loaded correctly smile.gif You have to provide the ADDRESS of "assign" (= @assign) as parameter to COGNEW

    (2) The COG just starts at its address 0, there is no "call" or whatever, so there can be no RET! Use either an endless loop, or kill the COG
    COGID x 
    COGSTOP x
    
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-01 21:50
    @Randy: What made you put your question? I mean, It would never enter my mind that there COULD be a limitation of such kind. But I think there is a reason, you would suspect it!?
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-01 21:55
    Mike Green said...
    Do look at the example deSilva mentioned. You can't do the "wrlong _t,aAddr" because the compiler doesn't know the address of "aAddr" when it assembles the instruction. Use "cognew(@assign,@answer)" to start the assembly routine and use "wrlong _t,PAR" to copy the result back to "answer".

    Mike, this is confusing what you were saying. "aAddr" has been set quite correctly by Mightor in the first line of his main routine.

    I am not a friend of using PAR, as it creates an additional level of indirection, that can be useful, but in most cases is not
  • R PankauR Pankau Posts: 127
    edited 2007-09-01 22:20
    Reason 1, working in the basement too late, with too little coffee
    I was running into roadblocks with instructions not taking any more than 9 bits. I should not have been surprised I guess but in the back of my mind I was thinking, "32 bit processor should be able to mov 32 bits", such as the decimal value 7000 from an initialized var in DAT to t1 for example for decrementing and testing at th end of a loop. I ended up splitting it up into two eight bit sections and shifting the MSBs up and adding the lower.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    I'd rather have a bottle in front of me than a frontal labotomy
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-01 22:42
    @Randy:It was never clearly stated what the issue with your program was, but what you describes as "solution" does not sound very healthy. Shall we dig deeper for it?

    As to one of your other questions: When stating something as LONG, whether in DAT or in VAR it will always be aligned correctly. When you are not in real need for space I should not recommend using WORD, as there are too many peculiarities with it. They can be confusing, and in most cases LONG will do fine. There is no penalty for it, as you might expect from your knowledge of other processors.
  • R PankauR Pankau Posts: 127
    edited 2007-09-02 03:17
    Well, now that you mention it, I did use a few bytes actually but they were declared after the shared memory. could that have been a reason for a shift?

    BTW my code is at another computer and I need to go look at it again with fresh eyes. The main symptom is this (trying to remember from last night). the code is a take off of the IR_kit, In spin I simply sampled the input connected to an IR 38KHz receiver and put ones or zeroes in successive locations in a buffer after the first input pulse was detected.
    Then I thouhgt why not convert it to asm and learn something useful. So I ported it to asm and now when I first hit the input with my IR remote it spits out serial data but it should not. The serial dump should not take place until I see a byte in my serial receive variable. The receive variable is what I declared as a byte after the buffer of 7000 longs that I used as a destination for the ir input bits. (one per long) I'm afraid that the asm code is writing to my serial input byte somehow.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    I'd rather have a bottle in front of me than a frontal labotomy
  • R PankauR Pankau Posts: 127
    edited 2007-09-02 03:46
    Now I'm really confused, but in a good way. I'll never figure out what I did yesterday to make me think that I could not take a 32 bit var and perform a mov with it as the source. I knew that a literal was limited to 9 bits, but I was getting that error or so I thought, with the variable as the source. We'll see what happens after tonight, maybe I was just being stubborn and too tired to catch my mistakes yesterday.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    I'd rather have a bottle in front of me than a frontal labotomy
  • R PankauR Pankau Posts: 127
    edited 2007-09-02 04:28
    I have it working now. Here are some things I changed.
    No longer am I using PAR to find my buffer's address. The method of declaring the var in DAT using SPIN to initialize a pointer works well. don't know why PAR did not work for me.

    I had to change my DAT var from a reserved word to a long (the buffer pointer). What is the difference? Looks like the RES is only viewabe in the asm ie cog and the "var_name long value" declaration is available to the whole object. Spin and asm. or at least a copy is sent to the new cog with the address that SPIN put there before spawning the new cog.

    The SPIN variable for receiving serial data is still a byte and it is declared before the variables that are used by the asm. All asm vars are declared with one "LONG" and seperated with commas.

    Also learned that cnt cannot be used directly as source data with wrlong since it is the address to the cnt register. mov ing it to a local var first then using wrlong does the job.

    can you tell I'm new at asm? Since SPIN is not compiled and does run rather slowly I thouht it was a good enough excuse to spend some time learning asm. I did it once in school for the 8088, this seems better than what I remember. I appreciate the enthusiastic help! thanks.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    I'd rather have a bottle in front of me than a frontal labotomy
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-02 08:34
    Randy P said...
    No longer am I using PAR to find my buffer's address. The method of declaring the var in DAT using SPIN to initialize a pointer works well. don't know why PAR did not work for me.
    Both should work; consider this:
    VAR
         LONG hubCell
    PUB
    
    ' this:
        COGNEW(@myAsm, @hubCell)
    
    ' .. is equivalent to this:
        hubAddr := @hubCell & $FFFC
        COGNEW(@myAsm,0)
    ' Of course you will not do the "&" - it's just a describtion what happens in the machine wrt PAR
    
    DAT
               ORG 0
      myAsm
    
    hubAddr LONG 0
    

    said...

    I had to change my DAT var from a reserved word to a long (the buffer pointer). What is the difference? Looks like the RES is only viewabe in the asm ie cog and the "var_name long value" declaration is available to the whole object. Spin and asm.
    RES takes no space in the HUB! When defining it, it is just a bookkeeping remainder for the assembler which cell number (!) to put into an instruction or DAT preset when used. There is no reason why SPIN shall not "know" of this number, but it is NOT a HUB variable, and the simple minded user could be confused...

    RES is usefull to save HUB memory, when you use COG vectors of - say - length 256 in 8 different COGs which otherwise would lead to 8kByte absolutly redundant HUB!
    said...
    or at least a copy is sent to the new cog with the address that SPIN put there before spawning the new cog.
    Right!
    said...
    The SPIN variable for receiving serial data is still a byte and it is declared before the variables that are used by the asm.
    Always remember: VAR is re-sorted (LONG, WORD, BYTE) - DAT is padded
    said...
    All asm vars are declared with one "LONG" and seperated with commas.
    This is just fine
    said...
    Also learned that cnt cannot be used directly as source data with wrlong since it is the address to the cnt register.
    No, not exactly! All "special registers" have a "shadow" (=last 16 cells in each COG). Only "dest" is wired to these cells, not "source". This becomes aware to you only in case of CNT, INA, INB, PHSA and PHSB -- just thinking: PAR as well?? I'll check! So the general rule is: Never READ a special register from "dest" position "READING" happens in case of all read-modify-write instructions, which are many. The COG is a Two-Address-Machine and so the name "dest" is a little bit confusing - it is ALSO the destination smile.gif

    Post Edited (deSilva) : 9/2/2007 9:53:06 AM GMT
  • MightorMightor Posts: 338
    edited 2007-09-02 13:26
    My stupidity must run deep because I made some changes and it's just not working. I am missing something really fundamental here but I can't figure out what. This is my code now:
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    VAR
      long answer
      
    OBJ
      Debug: "FullDuplexSerial"
    
    PUB start
      Debug.start(31, 30, 0, 57600)
      cognew(assign, @answer)
      waitcnt(clkfreq + cnt)        ' wait a sec for cog to be ready
      repeat
        Debug.str(string("answer: "))
        Debug.dec(answer)
        Debug.str(string(10,13))
        waitcnt(clkfreq/10 + cnt)
    
    DAT
            org
    assign  mov _t, #16      ' assign the value 16 to _t 
            wrlong _t, par ' write the value to the HUB variable
    
    _t            long 1
    
    



    It's not printing out "16" like it should. What am I missing here?

    Gr,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
  • R PankauR Pankau Posts: 127
    edited 2007-09-02 13:26
    OK so the special registers are only wired to the destination.· It is confusing that wrlong for instance has dest as the source data.·

    As far as the RES vs. declare long in DAT, I was made aware that I was doing something wrong because the compiler complained apparently because Spin was trying to access a RES declaration and it looked like an uninitialized variable error.·

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    I'd rather have a bottle in front of me than a frontal labotomy
  • MightorMightor Posts: 338
    edited 2007-09-02 13:32
    Randy,

    Could you attach some of the code you've written so I can have a look at it?

    Thanks,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
  • R PankauR Pankau Posts: 127
    edited 2007-09-02 14:04
    Sure, I'll try to get it up here this morning.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    I'd rather have a bottle in front of me than a frontal labotomy
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-02 14:09
    Mightor, why don't you read the answer I gave you 20 hours ago. I did explain your error!!!!!
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-02 14:23
    Randy P said...
    OK so the special registers are only wired to the destination.
    Don't turn my words. I said "dest" is wired to the "cells" $1f0 to $1ff. Whereas "source" is reading (and reading only!) the "real stuff".
    said...
    It is confusing that wrlong for instance has dest as the source data.
    Except MOV nearly all instructions have "dest" as source data smile.gif
    said...
    As far as the RES vs. declare long in DAT, I was made aware that I was doing something wrong because the compiler complained apparently because Spin was trying to access a RES declaration and it looked like an uninitialized variable error.
    It says it just does not know this name; that will happen also when you use the name of a local variable of another routine..
  • MightorMightor Posts: 338
    edited 2007-09-02 14:55
    deSilva said...
    Mightor, why don't you read the answer I gave you 20 hours ago. I did explain your error!!!!!
    Hehe, you may have explained it, but I probably didn't get it [noparse]:)[/noparse] I am sure that once the quarter drops I'll have one of those "OMFG, I can't believe I didn't get that" moments, right now though it's more of a slow "Uhhhhhhhhhhhh". Are you referring to the post at about 11:45 PM (GMT +2)? I did initialise the cog with @answer now as its parameter as you state in point 1.

    Gr,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-02 15:04
    Mightor! My positive opinion of you is crumbling!
    I was referring to the FIRST PARAMETER of COGNEW, which is the address where it loads the COG from. You have to state "@assign" here, not "assign"!
    Do I here: "Oh what an idiot I have been"?
  • MightorMightor Posts: 338
    edited 2007-09-02 15:10
    deSilva said...
    Mightor! My positive opinion of you is crumbling!
    I was referring to the FIRST PARAMETER of COGNEW, which is the address where it loads the COG from. You have to state "@assign" here, not "assign"!
    Do I here: "Oh what an idiot I have been"?
    Yeah, I'll just nip out to the back and shoot myself in the head! For some reason by first parameter, I thought you meant the variable that is referenced to by PAR! Stupid me! It should come as no surprise now that my now code works [noparse]:)[/noparse]

    Thanks for your seemingly inexhaustible patience!

    Gr,
    Mightor

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    | To know recursion, you must first know recursion.
  • R PankauR Pankau Posts: 127
    edited 2007-09-02 15:47
    Mightor,
    take a look at your wrlong instruction, it accesses PAR directly and it is not allowed, move Par to a local first and try that, I did the same thing.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    I'd rather have a bottle in front of me than a frontal labotomy
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-02 16:07
    ???
  • deSilvadeSilva Posts: 2,967
    edited 2007-09-02 17:25
    Well I won't let my "???" stand alone too long smile.gif
    PAR behaves as expected as a "typical" shadowed special register
    Study this test program ... It's always the same program btw, variants of which I certainly posted 4 or 5 times now...
    {
     Exploring the shadow registers  v02
     2007 deSilva 
    }
    CON
    
      _clkmode = xtal1 + pll8x
      _xinfreq = 10_000_000
         
    OBJ
      pc   :       "PC_Interface"  ' Ariba's PropTerminal
      
    PUB main | parameter 
      pc.start(31,30)
    
      cntcell := @cntcell
      shadow :=  @shadow
      opflag := @opflag
    
      parameter := $1235
      cognew(@asm, parameter)
    
      
      repeat
        setpos(0,0) 
        pc.str(string("Values from shadow cells.. press any key"))
        pc.str(string(13, "parameter     "))
        pc.hex(parameter,8)
        pc.str(string(13, "PAR           "))
        pc.hex(cntcell,8)
        pc.str(string(13,"the shadow    "))
        pc.hex(shadow,8)
    
        if pc.gotKey   ' press a key to add to CNT shadow
          pc.clearKeys
          opflag := 0  
        waitcnt(clkfreq/20+cnt)
    
        
    DAT
        org    0
    asm
        mov    RA, PAR
        wrlong RA, cntcell            ' show the real thing
        wrlong PAR, shadow            ' show the shadow
    
        rdlong RA, opflag  WZ
        if_Z   add     PAR,  #1       'use shadow
                                      'acknowledge command:
        if_Z   wrlong  opflag, opflag 'write any value as long as it's not 0  
        jmp    #asm
    
      shadow   long 0
      cntcell  long 0
      opflag   long 0
      RA        res  1
    
    PRI setpos(px, py)
      pc.out(10)
      pc.out(px)
      pc.out(11)
      pc.out(py)
    
Sign In or Register to comment.