Shop OBEX P1 Docs P2 Docs Learn Events
Definition of "self modifying code"? — Parallax Forums

Definition of "self modifying code"?

I'm going through a PASM object line by line with a pen and paper to help me follow what's happening at the bit level. I got stuck on the movd instruction. The manual says it's helpful with 'self modifying code'.
I would like to know what self modifying code is.

Comments

  • It means that you are altering instructions at runtime. In the case of MOVD, it means that you are modifying the D-field of another instruction. So, instead of writing to the original register specified in the code, that instruction will instead write to the new register specified in the d-field that was set by the MOVD.
  • tonyp12tonyp12 Posts: 1,951
    edited 2017-04-14 16:54
    movs, movd and movi have a mask of 9bits and leave the other bits untouched.
    They all uses the lower 9bits in the value you feed it, movs is just plane copy w/ mask, movd and movi (the later not used often) are shifted-up/inserted to correct slot.

    They are useful when you need to reset a loop source/destination pointer, as this is the only way a P1 can do it as it don't have indirect addressing.

    The actual +1 or is done with add label, #1 or
    add label, _9bit 'the label name of 1<<9 value.
  • lardomlardom Posts: 1,659
    edited 2017-04-14 19:56
    :confused: I am embarrassed to be this dense but say for example the label :loop is located at register 73. Could I move :loop address to register 74 with the MOVD instruction?
  • SeairthSeairth Posts: 2,474
    edited 2017-04-14 20:07
    lardom wrote: »
    :confused: I am embarrassed to be this dense but say for example the label :loop is located at register 73. Could I move :loop address to register 74 with the MOVD instruction?

    Suppose you had the following instruction:
    :j_1    djnz counter, #:loop
    

    When compiled, this will jump to the register 73 (as per your example). Now, suppose you wanted to jump to the next instruction instead. You could do this by calling
            movs :j_1, #:loop+1
    

    This would update the s-field of the register located at :j_1 to 74. Now, when the DJNZ executes, it will jump to register 74 instead of 73. It will continue to do this until you either use MOVS to modify it or reload the cog.

  • PASM instructions are encoded as 32 bits each, which includes a Source field and Destination field. If you look in the propeller manual at the assembly section, you'll see instruction opcode diagrams like this:
     -INSTR-  ZCRI   -CON      -DEST       -SRC
      000011  0001   1111    ddddddddd   sssssssss
    
    That's showing that the first 6 bits are the instruction value, the next 4 are the "effects" field (zero, carry, result, and immediate/indirect. The next 4 are the conditionals flags, then the dest and source registers are 9 bits each. MOVI sets the INSTR bits, MOVD sets the DEST bits, and MOVS sets the SRC bits.

    In a COG, the instructions being executed are just 32 bit values stored in memory. You can do things like this:
    
    loopStart:
    
        mov  changed, #add5    'replace the NOP at the 'changed' label with the instruction at the 'add5' label
        nop                    'add a NOP here because changed code needs one instruction of lead time for it to work
    
    changed:
        NOP                    'this instruction will be replaced
        jmp #loopStart:
    
    add5:    add   target, #5
    sub5:    sub   target, #5
    
    long target
    
    This is a contrived example, but you can do really useful stuff with self modifying code. Lots of things use it to change the addresses of variables being worked on because you can't do indirect addressing with values in a COG, but you can just modify the code as it runs.
  • The program in this thread uses a whole lot of MOVD and MOVS,basically to index through tables.

    Finding "0-0" as the source or destination field of a PASM instruction probably means that instruction is gonna get modified.
  • Cluso99Cluso99 Posts: 18,069
    The program in this thread uses a whole lot of MOVD and MOVS,basically to index through tables.

    Finding "0-0" as the source or destination field of a PASM instruction probably means that instruction is gonna get modified.

    Yes, that is the preferred method to make the reader aware that this part of the instruction is being modified by another instruction(s) elsewhere in the code.

    Be aware that when you use movs, movd, movi, that there must be an instruction between the instruction and the instruction being modified due to pipelining.
  • Thanks everyone for your contributions. My original question has generated a number of related questions which I have to learn first. I can describe a lot of Spin code in plain English but Spin does not require you to understand an opcode table or a concise truth table.
    If I don't understand a concept well enough to explain it to myself in plain English then I don't truly understand it.
    I am going to read the PDF manual from beginning to end starting today and I will refer back to this thread after I have done so.
  • tonyp12 wrote: »
    movs, movd and movi have a mask of 9bits and leave the other bits untouched.
    They all uses the lower 9bits in the value you feed it, movs is just plane copy w/ mask, movd and movi (the later not used often) are shifted-up/inserted to correct slot.

    They are useful when you need to reset a loop source/destination pointer, as this is the only way a P1 can do it as it don't have indirect addressing.

    The actual +1 or is done with add label, #1 or
    add label, _9bit 'the label name of 1<<9 value.
    It's starting to make more sense. I was asking how was it different from an OR instruction when I looked at the output on the second line of the concise truth table. The bits have been shifted left 9 bits.
    875 x 180 - 21K
  • tonyp12tonyp12 Posts: 1,951
    edited 2017-04-15 23:33
    As a P1 instructions always have a 9bit dest + 9bit source in the lower 18bits of a 32bit op-code.
    It's RAM, so there is nothing stopping you from changing those numbers on the fly (but not just not before it's executed due to pipeline)

    for example, If you have a lookup table that a loop x8 spits out data.
    _mainloop    movs _loop, #_data  ' reset pointer at 0-0 location
                 mov  loopcnt,#8
    _loop        mov [dest], 0-0     ' dest something or do a serial-out routine here etc
                 add _loop, #1       ' the 0-0 above is increased
                 djnz loopcnt, #_loop
                 jmp #_mainloop
    
    _data long $65665, $554442,.......
    

  • I got it! Phil Pilgrim's "Propeller Tricks and Traps" should be pinned if it's not already.
    His example below makes it perfectly clear.
    528 x 273 - 64K
Sign In or Register to comment.