Shop OBEX P1 Docs P2 Docs Learn Events
self-modifying code — Parallax Forums

self-modifying code

rjo_rjo_ Posts: 1,825
edited 2011-12-31 09:34 in Propeller 1
Hi Guys,

I have tried to read everything about self-modifying code... and I just don't get it.

I have used self-modifying code in higher level languages... So, I grasp the basic concept... but I have read Graham's questions and the responses, and I have done searches from several different directions. It seems like there is somethng missing. I feel so ignorant I don't even know how to ask the right question. The best issue seems to be table indexing in assembly.

Could someone post a complete example that does nothing but index through a dat table and pass the values back to a Spin array... ?

I'll be running around too much to respond for a few days... (a teenage daughter[noparse]:)[/noparse]

Rich

Comments

  • Paul BakerPaul Baker Posts: 6,351
    edited 2007-05-14 07:48
    Hi Rich,

    If you want to take a look at some self modifying code in a real application, take a look at the code for the logic analyser I wrote: http://forums.parallax.com/showthread.php?p=606048

    If you scroll down to the the assembly code, GetFast's storeloop is about as straightforward as it goes for a real world example. It·also shows you can modify code without using the movd or movs instructions.·The constant d_inc is the value 1<<9 or the least significant bit of the destination register, so by adding this value to the previous mov instruction, each successive mov is writing the contents of ina to the next location in memory.

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

    Parallax, Inc.
  • rjo_rjo_ Posts: 1,825
    edited 2007-05-14 17:10
    Paul,

    Thanks. My search skills seem to be deficient as well[noparse]:)[/noparse]

    Rich
  • Paul BakerPaul Baker Posts: 6,351
    edited 2007-05-14 17:26
    Use http://search.parallax.com if you aren't already, but in this instance there wasn't going to be any logical keyword regarding self modifying which would have led you to my program. I knew it because I wrote it.

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

    Parallax, Inc.
  • AribaAriba Posts: 2,690
    edited 2007-05-15 06:24
    rjo_ said...

    Could someone post a complete example that does nothing but index through a dat table and pass the values back to a Spin array... ?
    Yes I can...

    {Assembler Data Table read}
    
    CON     _clkmode        = xtal1 + pll16x
            _xinfreq        = 5_000_000
    
    VAR     long cog
            long Array[noparse][[/noparse]10]
    
    OBJ     tv : "TV_Text"   'or VGA_Text or PC_Text
    
    PUB Start  | n
        tv.start(12)
        cog := cognew(@asm_entry, @Array)           'start Assembly routine
        repeat until Array[noparse][[/noparse]9] > 0                   'wait until finished
        cogstop(cog)                                'free cog
        repeat n from 0 to 9                        'show Array
          tv.dec(Array[noparse][[/noparse]n])
          tv.out(13)
        repeat
        
    DAT
                  org
    
    asm_entry     mov       ArrayPntr,par           'address of Spin Array
                  mov       ReadCnt,#10             'number of longs to read
                  movs      ReadPntr,#Table         'replace 0-0 with table address
                  nop                               'wait 4 cycles before read
    ReadPntr      mov       temp,0-0                'read Table value
                  wrlong    temp,ArrayPntr          'write in Spin Array
                  add       ArrayPntr,#4            '#2 for words, #1 for bytes
                  add       ReadPntr,#1             'next long in table
                  djnz      ReadCnt,#ReadPntr       'repeat until all done
    Halt          jmp       #Halt
    
    
    Table         long      1,2,4,8,16,32,64,128,256,512
    
    ArrayPntr     res       1
    ReadCnt       res       1
    temp          res       1
    
    



    Andy
  • rjo_rjo_ Posts: 1,825
    edited 2007-05-16 12:47
    Paul,

    If the forum was periodically offered on a $CD... I· could have found your post on my Mac[noparse]:)[/noparse]!!!

    Andy,

    Exactly!!!· I am in the Blue Grass State, without my Prop.· Won't be home til Sunday... try it then,

    Thanks.



    Rich
  • rjo_rjo_ Posts: 1,825
    edited 2007-05-16 21:09
    Paul,

    Your example was perfect... and as usual, you gave me something to think about...

    BTW... you have gauged me correctly... if you hadn't explained the d_inc... (1<<9 ) ... I would have thought you were joking[noparse]:)[/noparse]



    Rich
  • HemPeterHemPeter Posts: 28
    edited 2011-12-30 20:17
    Hey guys,


    I realise this is a long dead thread, and there are probably numerous others referring to the same thing, but I am having serious difficulty with self modifying code. I have looked for keywords on the forums and google (movd, self modifying, etc) for the answers, but all the code examples seem to be similar, and no one seems to have had the troubles I see.
    I'm attempting to write an audio tuning application for the demoboard using an audio sampler, a number of FFTs, VGA and serial. Trying to pass hub ram FFT input array pointers to the sampler cog.
    The following example, taken from various forum posts and web pages, attempts to add 1 into the destination field of the rdlong instruction, labelled :patch.
    d0 being a long defined as 1 << 9, and param_count declared as a CONstant
    I understand the need to have an instruction in between the instruction to modify and the modified instruction because of the interleaved fetch execute cycles.
                  mov       in_ptr,PAR
                  movd      :patch,#asm_flag_ptr
                  mov       t2,#param_count              'load number of parameters, giving time to modify :patch instruction
    :rdloop
    :patch        rdlong    0,in_ptr
                  add       :patch,d0                 'increment destination operand by 1 / point at next cog long
                  add       in_ptr,#4                  'set to read next long from hub ram, giving plenty of time for fetch/execute of updated :patch long
                  djnz      t2,#:rdloop
    

    I get some seriously strange behaviour, my serial cog starts flooding me with lots of #, ? and $ symbols, with the occasional sporadic other character in there too...
    The screen starts making some seriously funky fast flashing colourful patterns too. Obviously the "add :patch,d0" instruction isn't having the intended effect.
    The only way I've found I can get the behaviour I want, within a loop, is to use an intermediate pointer and re-hit :patch with the movd instruction:
                  mov       in_ptr,PAR
                  mov       out_ptr,#asm_flag_ptr
                  movd      :patch,out_ptr
                  mov       t2,#param_count
    
    :rdloop       rdlong    t1,in_ptr
    :patch        mov       0,t1
                  add       out_ptr,#1
                  add       in_ptr,#4
                  movd      :patch,out_ptr
    '              add       :patch,d0   'can try commenting out the line above and uncommenting this one for quick switching between methods
                  djnz      t2,#:rdloop
    

    Can any propeller gurus tell me what I'm doing wrong? Why won't adding d0 to :patch increment the destination operand in the expected way?
    I'm glad I've got it working to some degree, after a day of forehead vs keyboard interaction and epilepsy inducing visions on the screen, but it seems more round-about and less elegant than the method advertised.

    Thanks all,
    Pete


    Page 23 of this PDF is where I got some ideas:
    http://www.cp.eng.chula.ac.th/~piak/project/propeller/machinelanguage.pdf
  • kuronekokuroneko Posts: 3,623
    edited 2011-12-30 20:43
    Can you post the complete code (at least the PASM part)? As you said, no-one else seems to have trouble so it's usually something in the part not shown.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-12-30 22:30
    Pete,

    In your app, do you have d0 set as follows?
    d0        long    512
    

    or to something else that evaluates the same?

    Also, just as an FYI, it's become customary for PASM arguments that are the target of self-modifying code to be expressed as 0-0.

    -Phil
  • HemPeterHemPeter Posts: 28
    edited 2011-12-31 06:22
    Hi guys, thanks for getting back to me so quickly =) It's one of the nice things about this forum!

    I modified the version of Beau's fixed point FFT to accept pointers of hub ram instead of constant memory locations. (Out of interest, is there a way of telling the compiler to allocate a 1024 word buffer at a certain hub location, e.g. $5000... It allocates it at compiler time right? So can you tell it you want it to be explicitly in a certain location?)
    Got it from here:
    http://propeller.wikispaces.com/FFT

    I modified the microphone to VGA demo to write to FFT buffers. Since the audio sampler seemed to be about 2 1/2 times quicker than the FFTs (to the best of my ability to tell using serial to print out clock values) I had four running and used the flag pointers to communicate when their buffers were full or finished. Each FFT plots to the screen by XORing pixels, so it seems to work nicely at 19.5 KHz and 38 KHz, but any higher and the sampler starts over writing the input buffer before the FFT completes and it shows on the screen.. ;)

    In my main object I have declared these:
    VAR
      'buffers for FFT cogs
      word  real_buffer1[fft#NN]
      word  imag_buffer1[fft#NN]
      word  real_buffer2[fft#NN]
      word  imag_buffer2[fft#NN]
      word  real_buffer3[fft#NN]
      word  imag_buffer3[fft#NN]
      word  real_buffer4[fft#NN]
      word  imag_buffer4[fft#NN]
    
    
      'flags used to measure run time and tops used to show detected frequency spikes
      long  fft1_flag
      long  fft1_top[peaks]
      long  fft2_flag
      long  fft2_top[peaks]
      long  fft3_flag
      long  fft3_top[peaks]
      long  fft4_flag
      long  fft4_top[peaks]
    
      long  aud_flag
    
      'pointers for audio sampler cog
      long  audio_flag_ptr
      long  array_size
      long  buffer1_ptr
      long  buffer2_ptr
      long  buffer3_ptr
      long  buffer4_ptr
      long  fft1_flag_ptr
      long  fft2_flag_ptr
      long  fft3_flag_ptr
      long  fft4_flag_ptr
    
    OBJ
      vga   : "vga_320x240_bitmap"
      fft   : "fft"
      pst   : "Parallax Serial Terminal"                   ' Serial communication object
      aud = "sampler"
    
    PUB launch
       'start vga
      vga_cog := vga.start(16, @colors, @pixels, @sync)
    
       'init colors to gold on blue
      repeat i from 0 to tiles - 1
        colors[i] := %%3300_0020 
    
      long[@audio_flag_ptr] := @aud_flag
      long[@array_size] := fft#NN
      long[@buffer1_ptr] := @real_buffer1
    ..
      long[@fft1_flag_ptr] := @fft1_flag
    .........
      pst_cog := pst.Start(115200)             ' Start the Parallax Serial Terminal cog
    .........
      fft1_val := fft.start(@fft1_flag,@real_buffer1,@imag_buffer1,@pixels)
    .........
      audio_cog := aud.start(@audio_flag_ptr)
    

    here's the sampler code in its entirety..
    {Sampler - This object takes samples from the microphone and stores them in Main RAM}
    
    
    CON
            _clkmode = xtal1 + pll16x                                               'Standard clock mode * crystal frequency = 80 MHz
            _xinfreq = 5_000_000
    
    ' At 80MHz the ADC/DAC sample resolutions and rates are as follows:
    '
    ' sample   sample
    ' bits       rate
    ' ----------------
    ' 5       2.5 MHz
    ' 6      1.25 MHz
    ' 7       625 KHz
    ' 8       313 KHz
    ' 9       156 KHz
    ' 10       78 KHz
    ' 11       39 KHz
    ' 12     19.5 KHz
    ' 13     9.77 KHz
    ' 14     4.88 KHz
    
      bits = 12                     'try different values from table here
    
      param_count = 10
    
    VAR
      long  cog                                             'Cog flag/id
    
    PUB start (flagptr): okay
    
    '  longmove(@asm_flag_ptr,flagptr,10)
      stop
      okay := cog := cognew(@asm_entry, flagptr) + 1 'launch assembly program into a COG
    
    PUB stop
    '' stop sampler and release the cog
    
      if cog
        cogstop(cog~ - 1)
    
    DAT
    
                  org       0
    
    asm_entry     mov       dira,asm_dira                   'make pin 8 (ADC) output
    
                  movs      ctra,#8                         'POS W/FEEDBACK mode for CTRA
                  movd      ctra,#9
                  movi      ctra,#%01001_000
                  mov       frqa,#1                         'microphone now setup
    
                  cogid     cog_id
    
                  mov       in_ptr,PAR                      'get the address of the first hub ram pointer
                  mov       out_ptr,#asm_flag_ptr           'copy the address of the cog ram
    
                  movd      :patch,out_ptr                  'patch the mov instruction
                  mov       t2,#param_count                 'setup loop counter
    
    :rdloop       rdlong    t1,in_ptr                       'get the pointer address
    :patch        mov       0,t1                            'move to relevant cog ram location
                  add       out_ptr,#1                      'point at next cog cell
                  add       in_ptr,#4                       'point at next hub ram long
                  movd      :patch,out_ptr                  'patch the mov instruction again
                  djnz      t2,#:rdloop                     'go round until all parameters are retrieved
    
                  mov       buffer_number,#0
                  mov       in_ptr,asm_buffer1_ptr          'use in_ptr as input to the array
    
                  sub       asm_array_size,#1               'more convenient for checking for end of array
    
                  mov       asm_cnt,cnt                     'prepare for WAITCNT loop
                  add       asm_cnt,asm_cycles
    
    loop          waitcnt   asm_cnt,asm_cycles              'wait for next CNT value (timing is determinant after WAITCNT)
    
                  mov       asm_sample,phsa                 'capture PHSA and get difference
                  sub       asm_sample,asm_old
                  add       asm_old,asm_sample
    
                  wrword    asm_sample,in_ptr               'write sample to fft array
                  add       in_ptr,#2
                  add       array_offset,#1                 'keep count of how far into array we are
    
                  test      array_offset,asm_array_size     wz
            if_nz jmp       #loop                           'wait for next sample period
    
                  mov       array_offset,#0
    
                  add       buffer_number,#1                'change fft buffer
    
                  testn     buffer_number,#1        wz
            if_z  mov       in_ptr,asm_buffer2_ptr
            if_z  wrlong    zero,asm_fft1_ptr
    
                  testn     buffer_number,#2        wz
            if_z  mov       in_ptr,asm_buffer3_ptr
            if_z  wrlong    zero,asm_fft2_ptr
    
                  testn     buffer_number,#3        wz
            if_z  mov       in_ptr,asm_buffer4_ptr
            if_z  wrlong    zero,asm_fft3_ptr
    
                  testn     buffer_number,#4        wz      'counting 0-3
            if_z  mov       buffer_number,#0
            if_z  mov       in_ptr,asm_buffer1_ptr          'reset input to the relevant array
            if_z  wrlong    zero,asm_fft4_ptr               'trigger fft cog
    
    
                  mov       asm_cnt,cnt                     'prepare for WAITCNT loop
                  add       asm_cnt,asm_cycles
                  wrlong    asm_cnt,asm_flag_ptr            'tell caller how long I took
    '              wrlong    buffer_number,asm_flag_ptr     'let outside object know buffer in use via the flag
    
                  jmp       #loop
    
    '
    '
    ' Data
    '
    asm_cycles              long    |< bits - 1           'sample time
    asm_dira                long    $00000200             'output mask
    
    asm_cnt                 res     1
    asm_old                 res     1
    asm_sample              res     1
    asm_data                res     1
    
    asm_flag_ptr            long    0
    asm_array_size          long    0
    asm_buffer1_ptr         long    0
    asm_buffer2_ptr         long    0
    asm_buffer3_ptr         long    0
    asm_buffer4_ptr         long    0
    asm_fft1_ptr            long    0
    asm_fft2_ptr            long    0
    asm_fft3_ptr            long    0
    asm_fft4_ptr            long    0
    
    
    
    array_offset            long    0
    array_end               long    0
    cog_id                  long    0
    in_ptr                  long    0
    out_ptr                 long    0
    t1                      long    0
    t2                      long    0
    buffer_number           long    0               'Numbering 0-3
    zero                    long    0
    d0                      long    1 << 9
    

    I realise this gets a bit messy without comments, and the last bit of my code could use some work.. Anyone got good methods of switching between 4 different buffers?
  • kuronekokuroneko Posts: 3,623
    edited 2011-12-31 06:27
    Just as I thought. Please move your 4 res lines (asm_cnt..asm_data) to the end of your data definitions (after all longs). In there they simply mess up your addressing and d0 won't have the data you're expecting. After that you can re-enable the add :patch, d0 code.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-12-31 09:06
    Adding to Kuroneko's explanation, the reason that all res lines have to be last is that they do not occupy any space in hub RAM. So when the PASM block gets loaded into a cog, anything after a res gets loaded immediately after whatever came before it, without a gap. If the res lines come last, everything before will be loaded in sequence, followed by garbage from whatever is next in hub RAM. But that doesn't matter since, presumably, all res variables will get initialized in the PASM code. However, if the res lines appear before other PASM-addressable entities, their assigned cog memory locations will get overwritten with the subsequent data during the cognew/coginit transfer from hub to cog, and that subsequent data will not appear at their intended cog addresses.

    -Phil
  • HemPeterHemPeter Posts: 28
    edited 2011-12-31 09:34
    Ahhaaa, Thank you! Now you mention it I read up about RES yesterday, but didn't think to check for it at the end of the code... You know how it is when modifying existing code..
    Cheers for explaining that! Not more tears ^_^
Sign In or Register to comment.