Shop OBEX P1 Docs P2 Docs Learn Events
From SPIN to PASM - Code Documentation Style - Just curious — Parallax Forums

From SPIN to PASM - Code Documentation Style - Just curious

Beau SchwabeBeau Schwabe Posts: 6,568
edited 2010-08-31 22:21 in Propeller 1
I realize that this can't be done in every case because of the complexities of the program, but on occasion when the initial program you write starts in Spin and you later convert portions of it to PAsm, would the type of documentation I have included below be helpful from an educational perspective for someone trying to learn PAsm?
RepeatLoop1                                                                     'repeat

              mov       temp,                   _ProgramMemoryAddress           '  characterbyte := ProgramMemory[offset]
              add       temp,                   _offset
              rdbyte    _characterbyte,         temp

              add       _offset,                #1                              '  offset += 1
              
              add       _ColumnIndex,           #1                              '  ColumnIndex +=1

              mov       temp,                   #text#cols                      '  if ColumnIndex > (text#cols -2) or characterbyte == 13
              sub       temp,                   #2                                    
              cmp       temp,                   _ColumnIndex wc                 {  if ColumnIndex > (text#cols -2) } 
                        'C flag is clear if V1 => V2
                        'C flag is set if V1 < V2
              cmp       _characterbyte,         #13          wz                 {  if                                 characterbyte == 13 } 
                        'Z flag is set if V1 = V2
                        
   if_c_or_z  add       _LineIndex,             #1                              '     LineIndex += 1
   
   if_c_or_z  mov       _ColumnIndex,           #0                              '     ColumnIndex := 0

              cmp       _LineIndex,             _Index       wc                 '  if LineIndex => Index                       
                        'C flag is clear if V1 => V2
                        'C flag is set if V1 < V2
                                                        
        if_c  jmp       #RepeatLoop1                                            '     quit
                                                                                ''    Jump to RepeatLoop1 if above condition is false


''            cmp       _characterbyte,         #13          wz                 '  if characterbyte <> 13
                                                                                ''(redundant code, Z flag previously set with same compare in above code)

        if_nz sub       _offset,                #1                              '     offset -= 1


...code on the left is PASM, code on the right is a descriptive SPIN equivalent. The single remark stroke is the literal translation from Spin for that 'block'. A 'block' defined by the group of PASM instructions NOT separated by a space. A double remark referring to an implied translation from Spin for that 'block'. Squiggle brackets indicate where in the PASM block a Spin conditional (i.e. IF/THEN/ELSE) takes place.

Comments

  • potatoheadpotatohead Posts: 10,261
    edited 2010-08-29 11:36
    I think so.
  • Mike GreenMike Green Posts: 23,101
    edited 2010-08-29 12:43
    Beau,
    It all depends on the quality of the original code and its comments. In the case you've posted, the Spin code and its comments are pretty good in terms of understandability. I can well imagine another program with cryptic variable names and useless comments that is converted to assembly with the comments being the original Spin code and the converted code being even less understandable than the original Spin code.

    I can also imagine assembly code that's not a direct translation of Spin code, that's optimized in some fashion where using the Spin code as comments may make it less understandable than well crafted comments that apply to the actual assembly code.

    It all comes down to trying to comment as if you're telling a story about the code and what it does. Sometimes it's useful to tell part of the story literally with lots of details and sometimes it's useful to tell other parts of the story metaphorically using the meaning of the actions rather than the actions themselves.
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2010-08-29 13:39
    Mike,

    Understood, ... this is more of an exercise for someone wanting to learn PASM that has a good grasp with SPIN. ...and agreed, there are some things that are difficult at best to describe with comments because the translation from one to the other is implied more so that it is followed in a literal step-by-step or line-by-line architecture. So heavy commenting can be more cumbersome trying to convey what's implied sometimes.


    Below is a whole section where original SPIN code was converted to PASM with comments in the style mentioned above. I just wonder if this helps or confuses anybody more ...
    ''###########################################################################################################################
    '                                               Spin to PASM conversion
    ''###########################################################################################################################
                  mov       _MemoryFlag,            #0                              'MemoryFlag := 0
                  
                  mov       _LineIndex,             #0                              'LineIndex := 0
                  
                  mov       _ColumnIndex,           #0                              'ColumnIndex := 0
                  
                  mov       _offset,                #0                              'offset := 0
    
    RepeatLoop1                                                                     'repeat
    
                  mov       temp,                   _ProgramMemoryAddress           '  characterbyte := ProgramMemory[offset]
                  add       temp,                   _offset
                  rdbyte    _characterbyte,         temp
    
                  add       _offset,                #1                              '  offset += 1
                  
                  add       _ColumnIndex,           #1                              '  ColumnIndex +=1
    
                  mov       temp,                   #text#cols                      '  if ColumnIndex > (text#cols -2) or characterbyte == 13
                  sub       temp,                   #2                                    
                  cmp       temp,                   _ColumnIndex wc                 {  if ColumnIndex > (text#cols -2) } 
                            'C flag is clear if V1 => V2
                            'C flag is set if V1 < V2
                  cmp       _characterbyte,         #13          wz                 {  if                                 characterbyte == 13 } 
                            'Z flag is set if V1 = V2
                            
       if_c_or_z  add       _LineIndex,             #1                              '     LineIndex += 1
       
       if_c_or_z  mov       _ColumnIndex,           #0                              '     ColumnIndex := 0
    
                  cmp       _LineIndex,             _Index       wc                 '  if LineIndex => Index                       
                            'C flag is clear if V1 => V2
                            'C flag is set if V1 < V2
                                                            
            if_c  jmp       #RepeatLoop1                                            '     quit
                                                                                    ''    Jump to RepeatLoop1 if above condition is false
    
    ''            cmp       _characterbyte,         #13          wz                 'if characterbyte <> 13
                            'Z flag is set if V1 = V2                               ''(redundant code, Z flag previously set with same compare in above code)
    
            if_nz sub       _offset,                #1                              '   offset -= 1
            
                  mov       _LineIndex,             #0                              'LineIndex := 0
                  
                  mov       _ColumnIndex,           #0                              'ColumnIndex := 0
    
                  mov       _i,                     #0                              'i := 0
    
    RepeatLoop2                                                                     'repeat
    
                  mov       temp,                   _ProgramMemoryAddress           '  characterbyte := ProgramMemory[i+offset]
                  add       temp,                   _i
                  add       temp,                   _offset
                  rdbyte    _characterbyte,         temp
    
                  cmp       _characterbyte,         #255          wz                 { if characterbyte == 255 } 
                            'Z flag is set if V1 = V2
    
             if_z mov       _MemoryFlag,            _LineIndex                      '     MemoryFlag := LineIndex                                              
    
             if_z jmp       #RepeatLoop2Done                                        '     quit     
    
                  add       _i,                     #1                              '  i += 1
    
                  add       _ColumnIndex,           #1                              '  ColumnIndex +=1
    
                  mov       _ColumnOffset,          #1                              '  ColumnOffset := 1
    
                  mov       temp,                   #text#cols                      '  if ColumnIndex > (text#cols -2)
                  sub       temp,                   #2                                    
                  cmp       temp,                   _ColumnIndex wc
                            'C flag is clear if V1 => V2
                            'C flag is set if V1 < V2
    
             if_c mov       _ColumnOffset,          #1                              '     ColumnOffset := 2        
    
                  cmp       _characterbyte,         #13          wz                 {  if ColumnIndex > (text#cols -2) or characterbyte == 13 } 
                            'Z flag is set if V1 = V2
    
        if_c_or_z add       _LineIndex,             #1                              '     LineIndex += 1
                  
        if_c_or_z mov       _ColumnIndex,           #0                              '     ColumnIndex := 0
    
    '             cmp       _characterbyte,         #13          wz                 {  if characterbyte <> 13 } 
                            'Z flag is set if V1 = V2                               ''(redundant code, Z flag previously set with same compare in above code)
    
             if_z jmp       #ConditionFalse1                                        '' Jump to ConditionFalse1 if characterbyte == 13
                                                                                    '' otherwise perform the proceedure below.
    
                                                                                    '' This jump just eliminates the need to place if_nz
                                                                                    '' on every command below that pertain to the condition 
    
                  mov       temp,                   _DisplayMemoryAddress           '     Display[ColumnIndex+(LineIndex*text#cols)-ColumnOffset] := characterbyte
                  add       temp,                   _ColumnIndex                    ''<-- Add ColumnIndex to the Display offset     
                  mov       count,                  #text#cols                      ''<1- The next three lines Multiply (LineIndex*text#cols) and add to the Display offset  
    Multiply1     add       temp,                   _LineIndex                      ''<2-
                  djnz      count,                  #Multiply1                      ''<3-
                  sub       temp,                   _ColumnOffset                   ''<-- Subtract ColumnOffset from the Display offset
                  wrbyte    _characterbyte,         temp                            ''<-- Finally write characterbyte to the Display offset result
                                                              
    ConditionFalse1
    
                  cmp       _LineIndex,             #text#rows       wc                 '  if LineIndex => text#rows                       
                            'C flag is clear if V1 => V2
                            'C flag is set if V1 < V2
                                                            
            if_c  jmp       #RepeatLoop2                                            '     quit
                                                                                    ''    Jump to RepeatLoop2 if above condition is false
                  
    RepeatLoop2Done
    
  • potatoheadpotatohead Posts: 10,261
    edited 2010-08-29 14:49
    Well, one disadvantage of the longer line is scrolling on lots of displays, or here in the forum. I don't see a nice way to resolve that though. Whatever line length makes sense is good, so long as it's kind of consistent. No need to be perfect, just generally formatted the same way for generally the same kinds of things being commented.

    Here's how I like to do it: I find vertical scrolling easier to consume than horizontal, but that's just me.
    DAT   'This is the PASM program data.  PASM programs originate in the hub, and are copied into COGs before
          'they are executed.  If PASM programs need to operate with data in the HUB, the address of that data
          'needs to either be hard coded into the COG program, or be passed to it via the PAR register, or
          'single, common hard coded HUB memory location.
    
          'For this example, the PAR register method will be used.
    
          'You should note the order of tasks and differences in variable use.
          
    
    cogstart                org     0               'This is the beginning of the PASM program to be loaded into
                                                    'one of the other COGs
    '***********************************************************
    ' PRI  init(pin) | i
    '***********************************************************
    
                            mov     _demo_sound, PAR        'the address of demo_sound was passed to the cog, meaning
                                                            'this is a pointer, not a value.
                                                            
                            rdlong  _I, _demo_sound          'get the number of sound performance data sets, into I
                            mov     _J, _demo_sound          'move pointer to J, and ...
                            add     _J, #4                   'add 4 bytes to it, so that J points to first sound data
                                                            'element.
    
                            'At this point, this particular COG has not had the pin and direction setup done, so let's
                            'do that.
    
                            mov     _K, _I                   'Number of demo sound data sets
                            shl     _K, #8                   'Multiply by 8, because that's the data set size
                            add     _K, _demo_sound          'add to base pointer to obtain address of out_pin!
    
                            rdlong  _out_pin, _K             'Get the value of out_pin
                            add     _out_pin, #1             'add one for left channel output (comment for mono setups)
                            
    '***********************************************************
    '     pin_direction := 1 << pin
    '***********************************************************       
                            mov    _pin_direction, _one      'shift a 1 _out_pin digits left to form bit mask
                            shl    _pin_direction, _out_pin
                                    
    '***********************************************************
    '     audio_out_pin_off :=  pin_direction ^ one
    '***********************************************************
                            mov    _audio_out_pin_off, _pin_direction      'prepare to form other bit_mask
                            xor    _audio_out_pin_off, _all_set
                                        
    '***********************************************************
    '     DIRA |= pin_direction
    '***********************************************************
                            or    DIRA, _pin_direction
                            
    '***********************************************************                        
    '     OUTA &= audio_out_pin_off
    '***********************************************************
                            and   OUTA, _audio_out_pin_off     
    
    
    
    '*************************************************************
    '  PUB play_sound | i, j, k
    '*************************************************************
    
    Each SPIN statement is followed by the block of code that aligns with it. That does not always happen, but when it does, it's kind of nice. Other more elemental comments then run along side the code, in conversational style, detailing the sub-operations necessary to reproduce the SPIN program element in the block.

    Overall, doing this is a help, no matter the style. After looking at the code Kye has done, I think lots of comments is always better, unless the comments are just Smile. Even then, if there is a chance that something good is in those comments, I'll take it. For my own code, I find the block style above readable, and the most valuable part of it is seeing the little "chunk tasks" to combine with the whole. It's not always clear to me what the progression of thinking was, and often that's important. I sometimes go long periods of time between being able to build on a project. Lots of little things get forgotten. The blocks tend to help me reconstruct where I was at on things, or if looking at another persons code, where they were headed as well.

    There is also a difference between a learning code example, and just comments on code that is just code, authored for whatever reason. Once a person begins to grok how PASM is going to work, I'm sure they will move to the next level, just doing the kinds of things Eric mentions below.
  • ericballericball Posts: 774
    edited 2010-08-29 14:54
    I'm with Mike. Although it may be possible in some cases to translate SPIN (or any other HLL) to SPIN, I often find that my PASM code contains "tricks" which aren't possible or necessary in the HLL. This could be self-modifying code for indexing, carrying the flags deeper in a routine than a simple IF, or having multiple entry & exit points for a subroutine.
  • lardomlardom Posts: 1,659
    edited 2010-08-29 16:15
    Beau, if you post it I will be one of many who study it. Pbasic and Spin have labs which make progress steady. Documented code will help immensely. I have just turned my attention to Pasm having completed a Spin project. I have to start at blinking LEDs and "Hello World". Pasm is very different.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-08-29 16:32
    I think the commenting looks brilliant. Especially the conditional jumps - that makes things a lot easier to follow.

    One thing I'm still not clear about is which characters are 'special'. @ seems to be special in Spin. Ditto ~. So is #. Using the same logic, is _ special? ie is _offset different to offset, or is it just a variant on my_variable?

    A style of commenting that I find very helpful is to comment excessively the first instance of something that a reader might find confusing. Later instances of that code could have briefer comments.
  • HumanoidoHumanoido Posts: 5,770
    edited 2010-08-29 17:56
    Beau Schwabe, this is definitely a step in the right direction. Like the obex, we need a place to pick up routines that are identical in spin and pasm. In the format, it could be improved. Each language can have a rem statement with a number. Pasm section 1 and then followed by spin section 1 form more easy comparison. I suggest 2 code sections that come one after another, separated, but not mixed together.

    Several times I have looked at pasm and thought it was spin, due to some similarities. I believe they mesh together and it would be beneficial to know how to put small sections of pasm in spin and call it into play. This fits well with what you are doing. We could have a place to consult with lots of these routines, especially the ones that are small, easily called and used.

    Right now, there's information all over the place. Only those guys working with the prop chip for years seem to have put it all together in their minds. Just starting out, its a challenge. So a place with converted routines would be welcomed for spin programmers wanting to incorporate some pasm into their code, for gains of speed and less code overhead.

    Humanoido
  • $WMc%$WMc% Posts: 1,884
    edited 2010-08-29 17:58
    Beau and Potatohead
    '
    Sounds like the start of an awesome project!
    '
    I think you would both get carpal tunnel before you had to many REM's for me, But I do agree that some times the end result is more important then the code that got it there.
    Its kind of a paradox I guess.
    '
    I find what both of you have posted to be very informative and easy to understand.
    '
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2010-08-29 18:41
    Thanks everyone!

    I wish I had more time to do this sort of thing, oddly I sorta like mundane, tedious translations like this ... :-) ... I'll see what I can do during longer LVS (Layout Versus Schematic) runs with Propeller II if I can fit something like this in as I'm writing objects.


    Dr_Acula,

    The @, @@, ~, ~~, # aren't necessarily any more special in Spin than they are in Pasm. One big difference is that Pasm has specific fields such as Instruction, Destination, Source, Conditional, Flags, etc. that aren't always conducive to using those 'shortcuts' that are actually easier to use in Spin.

    The _ is something that I use, especially when converting something from Spin to Pssm. The reason is that during the 'migration' and testing the same variable name can't be used in both Spin and Pasm simply because it's already been declared. Adding a _ to the beginning tells me that it possibly has a duplicate. This method also has a way of keeping the meaning of the variable intact when I see it visually.

    ALL

    Here is a kick-start of the type of vision I have... This is basically a step-by-step approach to converting a Spin PUB routine into a callable Pasm routine.


    Suppose we have a program like the one below and we want to convert the SpinSimple
    routine to Assembly...
    PUB StartMain|LED
        LED := SpinSimple(1,2)
        dira[23..16]~~
        outa[23..16] := LED
        repeat
               
    PUB SpinSimple(A,B)|C
        C := A + B
        C := C + C + C
        Result := C
    

    ...The first thing that we are going to want to do is create an Assembly header
    that can be called from SpinSimple.
    PUB StartMain|LED
        LED := SpinSimple(1,2)
        dira[23..16]~~
        outa[23..16] := LED
        repeat
               
    PUB SpinSimple(A,B)|C
        C := A + B
        C := C + C + C
        Result := C
    
    DAT
    
    PasmSimple
    

    ...next we need to asses the variables we are using in Spin and create duplicates
    in Pasm.
    PUB StartMain|LED
        LED := SpinSimple(1,2)
        dira[23..16]~~
        outa[23..16] := LED
        repeat
               
    PUB SpinSimple(A,B)|C
        C := A + B
        C := C + C + C
        Result := C
    
    DAT
    
    PasmSimple      
    
    _A      long  0
    _B      long  0
    _C      long  0
    

    ... now we need to call the Pasm from Spin and copy the necessary variables from
    Spin into Pasm.
    PUB StartMain|LED
        LED := SpinSimple(1,2)
        dira[23..16]~~
        outa[23..16] := LED
        repeat
               
    PUB SpinSimple(A,B)|C
        cognew(@PasmSimple,@A)
        C := A + B
        C := C + C + C
        Result := C
    
    DAT
    
    PasmSimple
            mov             temp,   par             '' get address of variable 'A' ; PAR + 0
            rdlong          _A,     temp            '' read contents of address into _A
    
            add             temp,   #4              '' get address of variable 'B' ; PAR + 4
            rdlong          _B,     temp            '' read contents of address into _B
    
            cogid           temp                    '' terminate cog
            cogstop         temp
    
    _A      long  0
    _B      long  0
    _C      long  0
    
    temp	long  0
    


    ...next we want to take the first equation 'C := A + B' and incorporate that within Pasm.
    at this point we can remove or remark the Spin version of this equation.
    PUB StartMain|LED
        LED := SpinSimple(1,2)
        dira[23..16]~~
        outa[23..16] := LED
        repeat
               
    PUB SpinSimple(A,B)|C
        cognew(@PasmSimple,@A)
    
    '    C := A + B
        C := C + C + C
        Result := C
    
    DAT
    
    PasmSimple
            mov             temp,   par             '' get address of variable 'A' ; PAR + 0
            rdlong          _A,     temp            '' read contents of address into _A
    
            add             temp,   #4              '' get address of variable 'B' ; PAR + 4
            rdlong          _B,     temp            '' read contents of address into _B
    
            mov             _C,     _A              '' C := A + B
            add             _C,     _B
    
            add             temp,   #4              '' get address of variable 'C' ; PAR + 8
            wrlong          _C,     temp            '' write contents _C to address
    
            cogid           temp                    '' terminate cog
            cogstop         temp
    
    _A      long  0
    _B      long  0
    _C      long  0
    
    temp    long  0
    

    ... but why doesn't this program work? ... Welcome to Parallel processing and one of the first gotchas!!

    The program does exactly what you told it to do, but there are no LED's lit? with the example numbers 1 and 2,
    C should equal 9, and LED's 0 and 3 should be lit. Why not?

    The answer is because when you launch the cognew, the Pasm program loads and starts the process of Adding
    A and B, but at the 'same time' (or at least after the cog has been loaded) the original cog that
    called the Pasm has already started evaluating the answer of _C from C + C + C and arives at an answer of
    zero BEFORE the cog that was called has time to finish.

    What to do? There are a couple of things that can be done. One is to insert a delay after the new cog
    has been started. This is wasteful as far as idle cpu time.
    PUB StartMain|LED
        LED := SpinSimple(1,2)
        dira[23..16]~~
        outa[23..16] := LED
        repeat
               
    PUB SpinSimple(A,B)|C
        cognew(@PasmSimple,@A)
    
        waitcnt(clkfreq+cnt)
    
    '    C := A + B
        C := C + C + C
        Result := C
    
    DAT
    
    PasmSimple
            mov             temp,   par             '' get address of variable 'A' ; PAR + 0
            rdlong          _A,     temp            '' read contents of address into _A
    
            add             temp,   #4              '' get address of variable 'B' ; PAR + 4
            rdlong          _B,     temp            '' read contents of address into _B
    
            mov             _C,     _A              '' C := A + B
            add             _C,     _B
    
            add             temp,   #4              '' get address of variable 'C' ; PAR + 8
            wrlong          _C,     temp            '' write contents _C to address
    
            cogid           temp                    '' terminate cog
            cogstop         temp
    
    _A      long  0
    _B      long  0
    _C      long  0
    
    temp    long  0
    

    ...another method is to use another variable as a 'flag' within Pasm that can be cleared when it's job is
    complete.
    PUB StartMain|LED
        LED := SpinSimple(1,2)
        dira[23..16]~~
        outa[23..16] := LED
        repeat
               
    PUB SpinSimple(A,B)|C,flag
        flag := 1
        cognew(@PasmSimple,@A)
        repeat while flag == 1
    
    '    C := A + B
        C := C + C + C
        Result := C
    
    DAT
    
    PasmSimple
            mov             temp,   par             '' get address of variable 'A'                          ; PAR + 0
            rdlong          _A,     temp            '' read contents of address into _A
    
            add             temp,   #4              '' get address of variable 'B'                          ; PAR + 4
            rdlong          _B,     temp            '' read contents of address into _B
    
            mov             _C,     _A              '' C := A + B
            add             _C,     _B
    
            add             temp,   #4              '' get address of variable 'C'                          ; PAR + 8
            wrlong          _C,     temp            '' write contents _C to address
    
            add             temp,   #4              '' get address of variable 'flag'                       ; PAR + 12
            wrlong          _Flag,  temp            '' write contents _Flag to address
    
            cogid           temp                    '' terminate cog
            cogstop         temp
    
    _A      long  0
    _B      long  0
    _C      long  0
    
    temp    long  0
    
    _Flag    long  0
    

    Now, the last two programs should work, one uses a delay (<- ok) , and the other uses a flag (<- better). What we
    want to do now is move the remaining portion of Spin into PAsm also. Right now all we have is a hybrid PUB routine.
    So moving C := C + C + C into Asm would look like this ...
    PUB StartMain|LED
        LED := SpinSimple(1,2)
        dira[23..16]~~
        outa[23..16] := LED
        repeat
               
    PUB SpinSimple(A,B)|C,flag
        flag := 1
        cognew(@PasmSimple,@A)
        repeat while flag == 1
    
    '    C := A + B
    '    C := C + C + C
    
        Result := C
    
    DAT
    
    PasmSimple
            mov             temp,   par             '' get address of variable 'A'                          ; PAR + 0
            rdlong          _A,     temp            '' read contents of address into _A
    
            add             temp,   #4              '' get address of variable 'B'                          ; PAR + 4
            rdlong          _B,     temp            '' read contents of address into _B
    
            mov             _C,     _A              '' C := A + B
            add             _C,     _B
    
                            'Note: since we are done with _B, I'm using it here as a temp variable
                            '      so the actual contents of the real temp variable can be preserved
            mov            _B,      _C              '' C := C + C + C
            add            _B,      _C              
            add            _C,      _B              '<- This saves a line of code ; could have done another
                                                    '   add            _B,      _C   ... but then you'd still
                                                    '   need to get _B into _C or just use _B later (two instructions down)
                                                    '   when writing _C back.
    
            add             temp,   #4              '' get address of variable 'C'                          ; PAR + 8
            wrlong          _C,     temp            '' write contents _C to address
    
            add             temp,   #4              '' get address of variable 'flag'                       ; PAR + 12
            wrlong          _Flag,  temp            '' write contents _Flag to address
    
            cogid           temp                    '' terminate cog
            cogstop         temp
    
    _A      long  0
    _B      long  0
    _C      long  0
    
    temp    long  0
    
    _Flag    long  0
    



    ...So that's basically a step-by-step for creating a custom Pasm PUB routine.
  • HumanoidoHumanoido Posts: 5,770
    edited 2010-08-31 00:47
    ...So that's basically a step-by-step for creating a custom Pasm PUB routine.
    Beau Schwabe



    This is outstanding!
    It's like you were reading my mind and know what we need for our projects.
    Humanoido
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2010-08-31 08:49
    Thanks Humanoido!

    Ok so the general consensus, I gather ... not only from this thread, but several people that I talked to at the EXPO ... Is that I will try to adopt and/or adapt this method of commentary to my future objects where this type of commentary is applicable. This way if you at least understand Spin you get a good idea of what the Pasm code equivalent is doing.

    At the same time I will try to compile various snippets, tricks, shortcuts that I may discover and learn along the way. And who knows, at some point maybe a book will form out of the debris ... "101 ways to PASM your SPIN" :-)
  • RinksCustomsRinksCustoms Posts: 531
    edited 2010-08-31 09:47
    i agree the style commenting does wonders to help better understand (probably more so) objects especially those with "clever" one-liners of code. looks great! Also do i agree it should be done when your time permits, though it will greatly assist the process of learning just what cna be done with respect to cleverness within our own PASM objects. :D
    Thanks & keep up the good work!

    OT - is there a "rumored" date on the release of the Prop II? I do hope its 32bit with 64 i/o's :D:D
  • bill190bill190 Posts: 769
    edited 2010-08-31 12:12
    I think one of the things which helped me most to learn assembly (non-Parallax stuff and way back when), was disassemblers. You can see the assembly version of your program. Or see the assembly programming of any other program.

    MPLAB has a View/Disassembly Listing which is quite handy. Like the following...

    128: if (character >= '0' && character <= '9')
    0070 0E30 MOVLW 0x30
    0072 5CDF SUBWF 0xfdf, W, ACCESS
    0074 E336 BNC 0xe2
    0076 50DF MOVF 0xfdf, W, ACCESS
    0078 0839 SUBLW 0x39
    007A E333 BNC 0xe2

    The 128: line is a line from your C program, then below that is the assembly code for that one line. You can see that the assembly code is using ASCII 0 (30) and ASCII 9 (39) [as it should in this case].

    And you can see how the same line in your higher level language program is translated by the compiler into assembly.

    Then you can look up one assembly line at a time (MOVLW, SUBWF, etc.) and learn what each is doing.

    And along with that read the processor data sheet and learn what each command is doing "inside" the processor.

    I think one of the first tools of this nature I used was DDT or Dynamic Debugging Tool. With that I could "peek inside" programs and see how they worked. Here is a bit on that...
    http://www.iso.port.ac.uk/~mike/interests/chistory/documents/cpm-22-manual/ch4.html

    FYI - ASCII codes...
    http://en.wikipedia.org/wiki/ASCII
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2010-08-31 13:25
    bill190,

    This is also how I learned Assembly and sort of where I would like to see this go as a teaching tool. Not necessarily a symbolic compiler from Spin to Pasm, but a text translator instead.

    Using your example the output might look like this...
    'if (character >= '0' && character <= '9')
    
                  cmp       character,              #$30    wc               
             if_c jmp       #fail
                  cmp       character,              #$39    wc,wz
     if_nc_and_nz jmp       #fail
    
    pass          {do stuff here if condition is true}
    fail          {continue here if the condition is true or false}
    
  • Heater.Heater. Posts: 21,230
    edited 2010-08-31 13:54
    I much prefer the in line approach to the side-by-side layout.

    The latter is hopeless when displayed in the forums code boxes as you have to scroll left and right all the time to follow it. Even when opened in a wide text editor window its hard work to read. The example in Beau's last post is much easier on the eyes.

    Like Beau I got into assembler this way however in my day it was:

    1) Write what you want to do in ALGOL. This was never compiled but just used as a pseudo code, like Spin or C in the examples here.

    2) Write your 6809 assembler code from the above pseudo code.

    3) Manually translate the assembly language to hex bytes for the EPROM programmer.

    It was some years later that I first had the luxury of a real compiler and could see what it produced.

    Beau:
    ...teaching tool. Not necessarily a symbolic compiler from Spin to Pasm, but a text translator instead.

    Well of course that is what a lot of compilers do. High level language text in, low level assembly language text out. You then use an assembler and linker to get the final binary.

    So that teaching tool would be a real compiler. It's PASM output could be compiled into a binary with the Prop Tool.
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2010-08-31 20:20
    Well, you have a point there ...
    heater wrote:
    ... Well of course that is what a lot of compilers do. High level language text in, low level assembly language text out. You then use an assembler and linker to get the final binary.

    So that teaching tool would be a real compiler. It's PASM output could be compiled into a binary with the Prop Tool.

    ... I guess that means ... Hmmm, time to go to the think tank. :smilewinkgrin:
  • Cluso99Cluso99 Posts: 18,069
    edited 2010-08-31 21:08
    Alternative tool ???

    1. In my Zero footprint debugger, it is possible to see (by tracing) exactly what spin code translates to in pasm. Of course, all the overheads of spin are present including all the pushes and pops.

    2. A much easier way is to look at the optional listing that bst and homespun create.

    Now of course, both these are bytecode expansions and not the original spin code.

    I wonder if a tool to list the pasm equivalent of a line of Spin code could be useful??? Or will it generate just too much 'fluff' to be useful ???
  • bill190bill190 Posts: 769
    edited 2010-08-31 22:15
    Cluso99 wrote: »
    I wonder if a tool to list the pasm equivalent of a line of Spin code could be useful??? Or will it generate just too much 'fluff' to be useful ???

    I think that would be wonderful!

    It is very difficult to "think" in assembly. Everything goes so slow and one small step at a time. (Slow as in all the lines of code you need to write to do something simple.)

    If a person learning this can see how a line from spin translates into many lines of assembly, then look at it and think huhhhh?????

    Then figure out what the first assembly line is doing.
    Then the next line.
    And the next...

    Then suddenly... :idea:

    FYI - I learned how things worked when I was young by taking things apart and seeing what was inside.

    And that was what I was doing too when I learned assembly. I had a simple working program to examine. Then a tool to look inside and see what was there (the disassembled code). Then a book to look up what each assembly command did.
  • Heater.Heater. Posts: 21,230
    edited 2010-08-31 22:21
    Cluso:

    Yes, but the topic here is about imparting PASM, not byte code, skills to beginners by documenting PASM code with spin syntax comments.

    This has led Beau to the natural conclusion of, why not a translator Spin to PASM? Which is actually a compiler anyway.

    I'm also concerned about the "fluff" problem here. The interesting techniques in PASM cannot even be represented in Spin, at least not in any concise sensible way. For example:

    1) The heavy use of JMP in a non structured ways.
    2) The difference between variables in COG or in HUB.
    3) The distinction between signed and unsigned arithmetic.
    4) How would you represent the coroutines of FullDuplexSerial?
Sign In or Register to comment.