Shop OBEX P1 Docs P2 Docs Learn Events
Propeller Assembly Q&A - Page 2 — Parallax Forums

Propeller Assembly Q&A

2

Comments

  • Fred HawkinsFred Hawkins Posts: 997
    edited 2007-07-14 15:56
    Mike Green said...
    Interestingly, someone just pointed out that these, and the JMP instruction, are all the same instruction except for the NR flag.

    Anyway, the storing of the return address happens at execution time (it has to ... think about it).

    Call and return are "macros" in that they're shortcuts that are handled by the assembler for convenience. The Call is really just a JmpRet and the Return is just a Jmp. When the assembler sees a "Call #Routine", it produces a "JmpRet Routine_ret,#Routine".
    When the assembler sees a "Ret", it produces a "Jmp #0".

    ·That's what I was seeing in the F8 show hex --·more zeros than I expected.·The actual address loading is·done when the JmpRet is executed.

    The assembler does no checking of what's at the "Routine_ret" label. It's normally not the job of an assembler to enforce good coding habits. It might be useful as an option for an assembler to issue warning messages, but this assembler/compiler has no provision for warning messages.

    ·It does (expletive) (and stop) if your return label lacks "_ret". But it didn't care about a NOP.
    Thanks Mike. So now all I have to do is figure what could be made useful by changing the second byte (or so) of a register.
  • Mike GreenMike Green Posts: 23,101
    edited 2007-07-14 16:23
    Yep, if you use "Call #Routine", the assembler generates a reference to "Routine_ret" and you better have a label like that somewhere in your program.

    So what's this about the 2nd byte (or so) of a register?
  • Fred HawkinsFred Hawkins Posts: 997
    edited 2007-07-14 18:36
    Mike Green said...
    Yep, if you use "Call #Routine", the assembler generates a reference to "Routine_ret" and you better have a label like that somewhere in your program.

    So what's this about the 2nd byte (or so) of a register?
    Bad joke maybe, or worse, an attempt to figure how to misuse JmpRet for my own purposes. Which is, I think, figuring which 9 bits are changed in
    the _ret address. Any cog address can be viewed as part of just another 'register'. My best guess was that it was·the second byte·of the target; the·second word is $.c 5c, if it·is in fact a jmp.

    Turns out it's a little endian value with the least significant 8 bits in the first byte and the ninth bit·as the least significant of·the second byte.
    $ff·01·for the last location in cog space, for example. That's what I think I am seeing in the hex dump.

    It seems to be a way to set a flag or to stash up to $1ff in another address·as you jmp to a routine*. Assuming that you take care of the routine's exit yourself.

    * or on exit of a routine,·perhaps. Of course that just might·give the assembler the sniffles.

    (Parenthetical why consider this stuff? Answer: bent on rolling my own forth and still dumb enough to chase up the wrong tree[noparse][[/noparse]s] for a time.·I never have programmed a RiSC with memory issues before....)
  • Mike GreenMike Green Posts: 23,101
    edited 2007-07-14 20:26
    Fred,
    It's really simple ... the JMPRET stores the return address in the source field of the long word whose address is in the destination field of the JMPRET. The layout of the instruction is documented in all sorts of places ... the Propeller Manual, the Propeller datasheet. As is true of RISCs, the format of the instruction is always the same.
  • Nick McClickNick McClick Posts: 1,003
    edited 2008-07-09 21:00
    Digging up an old thread...
    My demo board (nice build quality, btw), has LED's from pins 16-23. I want to do timed animations on them. I thought I could define the paterns;
    DAT
    Pattern1    BYTE    %1111_0000
    Pattern2    BYTE    %0000_0000
    Pattern3    BYTE    %1100_0000
    
    



    and go thru the patterns by writing that byte to outa starting at bit 16. Is there a way I can address the outa register 'mid byte' like that? It does work if I make the patterns longs.

    another question;

    can't I do;
    waitcnt   cnt, #300
    
    



    to pause the cog for 300 clocks? Doesn't seem to work.
  • Paul BakerPaul Baker Posts: 6,351
    edited 2008-07-09 21:28
    For your first question, use "shl·pattern, #16". this will shift the data to start at pin 16.
    For your second question, in assembly you must provide the clock tick to wait for in the destination field. The instruction will add the source to the destination, but only after the instruction has executed (so its good for establishing the next wait period, but not this one). You only specified cnt, which means it will wait until the counter is equal to the count again which is ~54 seconds later. You need to do something like the following:
    mov val, cnt
    add val, #300
    waitcnt val, #300   'the increment value (300) only needs to be included when you need to wait the same period again 
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker
    Propeller Applications Engineer

    Parallax, Inc.
  • Nick McClickNick McClick Posts: 1,003
    edited 2008-07-15 03:52
    This is kind of driving me crazy;
    PUB main
    cognew(@begin,0)
    DAT
    begin
            ORG   0                 '
            shl   dirs, #16         'shift sixteen bits
            mov   dira,dirs         'make pin 16 output
            mov   outa,dirs         'make the output high
    :loop
            mov time,cnt            'take current system counter, move to time long
            add   time,waitper      'add wait period to time
            waitcnt  time,#1        'wait until cnt == time plus waitper.  Add #1 to time, but time will get rewritten
            shl   dirs,#1           'shift to pin 17
            mov   dira,dirs       'Make pin 17 output
            mov   outa,dirs      'Make that 17th pin high
            jmp   #:loop         'repeat forever
    
    time    RES   1
    dirs    LONG  1
    waitper LONG  10_000
    
    



    I would expect it to display LED #16, wait 10_000 clocks or so, and shift to pin 17. Instead, when I run this, pins 18, 21 & 22 go high, it pauses for about a minute and pins 19, 22 & 23 go high. then 23 & 20. then just 21.
  • TimmooreTimmoore Posts: 1,031
    edited 2008-07-15 03:59
    res should be last. time with be overlayed on dirs
    I would move the set of outa before dira. In your case doesn't matter much but its better to set the output before you enable the output
  • Nick McClickNick McClick Posts: 1,003
    edited 2008-07-15 04:32
    Thanks for saving my sanity! That makes sense and it works now. I guess the assembler reads everything from ORG sequentially into memory.
  • hippyhippy Posts: 1,981
    edited 2008-07-15 14:28
    @ Nick : Yes, a Cog is loaded with longs sequentially from Hub, but RES isn't stored in
    Hub by the compiler. Thus what's loaded from Hub just before your code comes to be
    executed is ...

    jmp   #:loop         'repeat forever
    
    time    LONG  1
    dirs    LONG  10_000
    waitper LONG  ???? ' <--- Probably wrong value
    
    
    
  • SailerManSailerMan Posts: 337
    edited 2009-09-28 13:42
    Bringing up an old thread because I never really spent time on PASM like I planned to..

    I'm trying to understand MOVD,MOVI etc

    Looking at Mike Green's Code

    '' Here's a simple example of table lookup in assembly.
    '' This particular example takes a table index in "ptr"
    '' and sets the "data" to the word value from the table.
    '' There's no range checking.  If you want long values,
    '' just eliminate the shifts and masks (*).  You can use
    '' the same kind of logic for byte values.
    
    DAT
                org   0
    test      mov   ptr,#2        ' for an example, get third value
                call  #look            ' note table indices are 0 to n-1
    :stop     jmp   #:stop        ' no checking for out of range
    
    look         mov    data,ptr      ' ptr is a table index (0 to n-1)
                  shr   data,#1       ' divide input value by 2 to get (*)
                  add    data,#table    '   long word index, add table address
                  movs   :inline,data   ' have to use instruction modification
                  nop                        ' need pause here for pipelining
    :inline    mov    data,0-0         ' get long value from table
                  test   ptr,#1   wz     ' do we want odd or even half  (*)
        if_z     and    data,mask       ' if even, take lower 16 bits (*)
        if_nz   shr    data,#16         ' if odd, take upper 16 bits  (*)
    look_ret   ret
    table       word    $0000
                  word    $C0C1
                  word    $C181
                  word    $0140
                  word    $C301
                  word    $03C0
                  word    $0280
    mask        long   $FFFF
    data          res     1
    ptr             res     1
    



    I have some questions actually many but I will start with a beginners question...
    movs   :inline,data   ' have to use instruction modification
                  nop                        ' need pause here for pipelining
    :inline    mov    data,0-0
    



    I thought
    :inline
    

    was a label to jump to.. can you explain in beginner lingo how
    movs   :inline,data
    

    works.

    Also..
    0-0
    

    what does this mean.

    If you have time I'd really like to pick apart this litte code snipet

    Remember I'm still after 3 years a real beginner... I have written some ASM programs on my own.. but I'm trying to get my head around this stuff.

    Thank,
    Eric
  • potatoheadpotatohead Posts: 10,261
    edited 2009-09-28 14:12
    The "0-0" is a place holder for something in the code that will be modified at run time. It really means nothing until you, or the Propeller, parses the program. In the COG, there are just longs. Each long could be an instruction. If the program ends up executing it, then it is an instruction. If not, then it's data. Makes no difference to the COG.

    Instructions have some pieces, or elements encoded into bit-fields:

    There is the instruction opcode, and it's impacted by the MOVI instruction. These bits detail what the instruction actually does.

    There is the instruction source, and it's impacted by the MOVS instruction. These bits detail the source data value or location(address).

    The instruction destination is impacted by the MOVD instruction. These bits detail the destination address.

    Instructions have some other bits, like the conditional bits, if_Z, if_NOT_C, etc... (Whether or not the thing is actually executed)

    Finally there is the address mode bit, either literal, or not literal. --->the octothorpe '#' as in 'JMP #label' being different from 'JMP label' (literal, or indirect addressing mode)

    The last two can be modified, but you have to do it with a bitmask operation. I don't think there is any way to do it with an official instruction opcode.

    About that #....

    I find it easiest to explain this using the case of the JMP instruction. The # means the value in the instruction bits is the value that will be loaded into the program counter when the JMP is executed. If you leave off the # then the value in the instruction bits is the address of the COG memory location that contains the value that will be loaded into the program counter. The former is literal, the latter is indirect. This little bit of info is valuable when considering the MOVS instruction to modify another instruction before it is loaded and executed.

    So, let's say you want to load a register with some element of a table contained in the COG. That would look like this right?

    MOV destination, #55

    Let's also say the table exists from COG memory 50 to 60. Then the value at COG 55 would be copied into the long referenced by the label destination. So far so good. Now, let's say that you want to do some math, and reference the table location that equals the result of that math. Now you need the MOVS instruction!

    So you do the math, and let's say that the result is 7. Add 7 to 50, yielding 57. Do a MOVS on the MOV instruction above, and the #55 would change to a #57. Since this happens at run time, you would have code that looks like the following:

                    [noparse][[/noparse]some math that puts a value into result]
    
                    add    result, #table        'calculate table + index into table = table element address!
                    MOVS   loadtable, result     'this instruction modifies the source field to contain the desired table element address
                    nop                          'for the pipeline
    loadtable       MOV    destination, #0-0     'source gets self-modified
    
    
    
    result          LONG  0
    
    table           LONG  $ffeeddaa     element 0
                    LONG  $DDEEFFAA     element 1
                    LONG  $0000FFFF     element 2
                    etc.....
    
    



    When doing this, you have to keep an eye out for the pipeline. Instructions take 6 cycles, but execute in 4. An instruction gets loaded before it's executed. If the instruction is loaded, then you modify something, it won't impact the instruction. The nop shows this. You have to have just one instruction between a self modify instruction, and the instruction that it actually modified.

    To sum up then:

    You use MOVi to change the instruction itself. So, a MOV could be NOP, for example. Some part of your code ahead of the MOV, could decide that it's not needed, and change it to a NOP. I know the conditionals are there for that, but it's an example I could think of quickly.

    You use a MOVs when you want to change the source address, or value of a given instruction. That's the table index load example above. Be careful of the # and what it means. IMHO, you want the # more often than not with MOVs. We will see what the real pros here say about that, but that's been my experience. BTW, I don't think you can change this in a self modify. The addressing mode form of an instruction is set when you compile, and the self-modify instructions don't change it, they change only the value the instruction operates with.

    You use a MOVd, when you want to change the destination address an instruction is working with. That might be writing values back to a table, for example.

    Hope this helps some!

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Propeller Wiki: Share the coolness!
    Chat in real time with other Propellerheads on IRC #propeller @ freenode.net
    Safety Tip: Life is as good as YOU think it is!

    Post Edited (potatohead) : 9/28/2009 2:40:09 PM GMT
  • TimmooreTimmoore Posts: 1,031
    edited 2009-09-28 16:25
    You can change the # mode, its the I in ZCRI.
  • potatoheadpotatohead Posts: 10,261
    edited 2009-09-28 19:06
    Absolutely it can be changed. I didn't mean to imply it couldn't. I was trying to get at it being easiest if the instruction has the right addressing mode, before modifying it with MOVd and MOVs...

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Propeller Wiki: Share the coolness!
    Chat in real time with other Propellerheads on IRC #propeller @ freenode.net
    Safety Tip: Life is as good as YOU think it is!
  • SailerManSailerMan Posts: 337
    edited 2009-09-28 20:00
    Wow... my head is still spinning..... [noparse]:)[/noparse] Where is 0-0 in the Assembly manual.


    So LoadTable is an Instruction in this case not a label.. Since I'm new be easy on me. [noparse]:)[/noparse] I thought that these Were labels for like jump returns and things of that nature.

    So in order to use commands like MOVS, MOVD and MOVI

    The Line must have a "label" to refer to it from the MOVS.. right?

    So if Result == 2
                     add result,#table         'Result is now 2+#table
                     MOVS   loadtable, result  ' Points to element 2  Value $0000FFFF
                     Nop
     loadtable   Mov destination,0-0 gets replaced by the value of Result(element 2  Value $0000FFFF)
    
    



    Am I thinking in the right direction?

    Eric
  • potatoheadpotatohead Posts: 10,261
    edited 2009-09-28 20:14
    0-0 is a convention carried over from other systems. It's a nice placeholder that doesn't flag an error. Truth is, you can put whatever you want there. Why?

    Because your program will modify it at run time.

    On the matter of labels, a label is a label. If you JMP to it, it's a JMP label. If you self-modify it, it's a instruction label. All one and the same.

    All a label really does is equate a cog memory address with some word that's easier for us to remember and work with.

    Yes, the value 2 will end up as the source in the instruction labeled loadtable. That would look like MOV destination, 2, and behave the same way.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Propeller Wiki: Share the coolness!
    Chat in real time with other Propellerheads on IRC #propeller @ freenode.net
    Safety Tip: Life is as good as YOU think it is!
  • localrogerlocalroger Posts: 3,452
    edited 2009-09-28 22:58
    Just noticing where this thread has gone, I'd like to put in a quick note about books for learning assembly.· The best books for this are OLD books written for 8 bit CPU's in the late 1970's and early 1980's.· These books generally start with binary/hex math, explain addressing, and go through the instruction functional groups in a clear and accessible manner.· Not that I refer to them much any more, but dear to my heart (and within easy reach) are...

    Programmer's Guide to the 1802 by Tom Swan, Hayden Press, 1981

    The 8080A BugBook (my personal gateway guide) by Rony, Larsen, and Titus for Radio Shack, SAMs Press 1977

    Z80 User's Manual by Joseph J. Carr, Reston Publishing, 1980

    The Z-80 microcomputer Handbook by William Barden, Jr., SAMS Press, 1978

    The Complete Timex TS1000/Sinclair ZX81 ROM Disassembly by Dr. Ian Logan and Dr. Frank O'Hara, Melbourne House, 1982

    If you don't know assembly at all, books of this vintage are much easier to comprehend than later books because the processors they document are much simpler and more straightforward, so more time is spent explaining and demonstrating concepts (nearly all of which scale to the prop, which despite its bit width is a fairly simple processor too by modern standards) and much less on enumerating a zoo of instruction types in different bit widths, addressing modes, and so on.· In those days you had to learn assembly to make the machine do anything useful, so there were books on the subject that really were meant to be comprehensible by kids (or at least teenagers).· There were also books specifically about the Commodore 64 (including a full system manual) and Apple ][noparse][[/noparse], which I don't have because I didn't have those then relatively expensive systems back in the day.
  • potatoheadpotatohead Posts: 10,261
    edited 2009-09-29 00:28
    I agree with this.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Propeller Wiki: Share the coolness!
    Chat in real time with other Propellerheads on IRC #propeller @ freenode.net
    Safety Tip: Life is as good as YOU think it is!
  • SailerManSailerMan Posts: 337
    edited 2009-09-29 01:15
    I'm sure you are right local roger... I will look into them .... And with all due respect ... These forums are to pose questions right?

    My biggest problem is that my brain is stuck in "BASIC" and thinking in ASM has been a little bit of a struggle.

    By the way potatohead... Thanks for taking the time to explain. It's appreciated.
  • potatoheadpotatohead Posts: 10,261
    edited 2009-09-29 02:21
    Have you tried giving my beginner tutorial a read?

    It's really basic and has some of the assembly mindset expressed in simple and friendly terms.

    http://forums.parallax.com/forums/attach.aspx?a=28716

    I update it from time to time. This kind of stuff is just a bit beyond it, but there are a lot of basics covered. Maybe a read through might shake you off of BASIC!

    No worries on time. If I have some, I'll contribute here. Don't always get it perfect, but somebody else usually steps in and the end result is all good!

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Propeller Wiki: Share the coolness!
    Chat in real time with other Propellerheads on IRC #propeller @ freenode.net
    Safety Tip: Life is as good as YOU think it is!
  • SailerManSailerMan Posts: 337
    edited 2009-09-29 02:37
    PotatoHead... Yep I read it... Very helpful... I think it's great that people take the time to contribute so much.


    I wrote this little ASM code... Basically it just counts down from whatever is in counter... Each time It reaches zero it sends that data to LEDs attached to Pins 0-7.. and is supposed to read the next countdown value from a table.

    The first time though it counts down from 255.

    The next iteration it reads the table ... It is suppose to start at $0f but it starts at $1b, then $36 then $51 etc... I'm not sure what I'm doing wrong can someone take a quick peek? It should pick the 1st Long in the table.

    Thanks in advance for any help you can offer.

    Eric
    CON
            _clkmode = xtal1 + pll16x
            _xinfreq = 5_000_000
    OBJ
            Debug:  "TV_Text"
            Num:    "Numbers"
    Var
            Long Index
             
    PUB Main
       
      Debug.Start(12)
      Num.Init
      
      Index:=$FF
      
      cognew(@Entry, @Index) 'Launch new cog
      
      Repeat
        Debug.Out($01)
        Debug.Str(Num.ToStr(Index,Num#Hex))
        Debug.Str(String("  "))
          
        
    
    DAT
     
    org 0  
    Entry         Mov       Count,Par               'Count = Index=255
                  RdLong    Counter,Count
                  Mov       DIRA,#$ff               'Pins 0-7 OutPuts
                  Mov       OUTA,#$00               'Pins 0-7 Low
                  Mov       OutPut,#0
                  Mov       TableIndex,#Table
                  Add       Time, cnt               'Calculate delay time
                  Add       Time, #500              'Set minimum delay here
    :Loop        
                  WrLong    Counter,Count 
                  Waitcnt   Time, Delay 'Wait
                  Sub       Counter,#1 
                  TJNZ      Counter,#:Loop  
                  Add       Output,#1
                  Mov       OutA,OutPut              'Send OutOut to Pins
                  Call      #GetCount                'Get Next CountDown
                  JMP #:loop
                  
        
    GetCount    MOVS      :loadtable, TableIndex    'this instruction modifies the source field to contain the desired table element address
                  nop                          
    :loadtable    MOV       Destination, 0-0         'source gets self-modified
                  Mov       Counter,Destination
                  Add       TableIndex,#1             ' Increment TableIndex- no checking for end of table...Should still run 6 interations.
    GetCount_Ret  Ret
    
    
                  
    Fit 496              
    Delay         long      10_000_000
       
    Count         Long      0
    Counter       Long      0
    
    TableIndex    Long      0
    
    Table         Long      $0F,$0E,$0D,$0E,$0B,$0A
              
                  
    Time          Res       1
    OutPut        Res       1
    Destination   Res       1
    

    Post Edited (SailerMan) : 9/30/2009 12:53:33 AM GMT
  • SailerManSailerMan Posts: 337
    edited 2009-09-29 03:32
    I removed the FIT496 and Changed the Add to a MOV.. --- < dumb mistake...now the program continues to countdown from $1D... I'll figure this out eventually. Thanks! [noparse]:)[/noparse]
  • kuronekokuroneko Posts: 3,623
    edited 2009-09-29 03:46
    SailerMan said...
    I removed the FIT496 and Changed the Add to a MOV.. --- < dumb mistake...now the program continues to countdown from $1D... I'll figure this out eventually. Thanks! [noparse]:)[/noparse]
    The problem is this line:

    MOV       Destination, #0-0
    


    In your initial code example $1B is the address of Table. This gets correctly inserted into the mov instruction. However, you specified # which means load the immediate value $1B into Destination. What you want is the register location $1B. So just remove the offending #. The immediate-or-not bit is located somewhere else in the instruction and not modified by e.g. movs. Be careful what you wish for [noparse]:)[/noparse]
  • SailerManSailerMan Posts: 337
    edited 2009-09-29 12:59
    Last night I ran this program through a debugger and noticed the table was @ $1D (Light Bulb) ... I was going to troubleshoot this tonight when I got home...Thanks! for your help.
  • SailerManSailerMan Posts: 337
    edited 2009-09-29 23:05
    I still can't seem to find what the problem is... What debug tool is the best?
  • kuronekokuroneko Posts: 3,623
    edited 2009-09-29 23:41
    SailerMan said...
    I still can't seem to find what the problem is... What debug tool is the best?
    Could you post your most recent code?
  • SailerManSailerMan Posts: 337
    edited 2009-09-30 00:06
    I just updated The above code and is the most recent.
  • kuronekokuroneko Posts: 3,623
    edited 2009-09-30 00:29
    SailerMan said...
    I just updated The above code and is the most recent.
    In the Entry section set TableIndex to #table, not #0. And in GetCount remove/skip the first line, first instruction should be movs. Otherwise you keep adding the base address for the table.

    With those changes I have it working on the demo board. That just leaves the end-of-table check.

    Also

    Sub       Counter,#1 
    TJNZ      Counter,#:Loop
    


    can be more conveniently written as

    DJNZ      Counter,#:Loop
    
  • localrogerlocalroger Posts: 3,452
    edited 2009-09-30 00:39
    SailerMan, I started with BASIC myself and an infamous comment by Djikstra notwithstanding, I think BASIC is a much better place to start than other high-level languages if you want to progress to ASM and an understanding of how the machine works. And the Prop is an EXCELLENT machine to start with, giving you some relatively modern capabilities with underpinnings that would have felt very natural to my teenaged ca. 1980 self.
  • TimmooreTimmoore Posts: 1,031
    edited 2009-09-30 00:54
    Time is not initialized so the first delay could be a long time.
Sign In or Register to comment.