Shop OBEX P1 Docs P2 Docs Learn Events
[resolved][puzzle] tight spot — Parallax Forums

[resolved][puzzle] tight spot

kuronekokuroneko Posts: 3,623
edited 2012-09-25 22:32 in Propeller 1
The (broken) code fragment below is from a VGA renderer, the first N-1 back porch lines need carry clear while the last one requires carry to be set (as an extra parameter so to speak).
'-----------------------------------------------
                [COLOR="orange"]mov     ecnt, #29 -1[/COLOR]
                [COLOR="orange"]call    #blank[/COLOR]                  ' back porch
                [COLOR="orange"]djnz    ecnt, #$-1[/COLOR]

                [COLOR="blue"]call    #blank[/COLOR]                  ' last blank line done manually
'-----------------------------------------------

blank           ...
blank_ret       ret
The entry condition for the fragment is carry clear, the blank subroutine returns with carry clear. How can the objective be achieved by only changing code between the lines but not adding extra instructions and/or data? Extra points for keeping the total runtime to a minimum.
«1

Comments

  • Mark_TMark_T Posts: 1,981
    edited 2012-09-23 04:51
    '-----------------------------------------------
                     [COLOR=orange]mov     ecnt, #29[/COLOR]
                     cmp     ecnt, #2  wc
                     [COLOR=orange]call    #blank[/COLOR]                  ' back porch
                     [COLOR=orange]djnz    ecnt, #$-2[/COLOR]
    '-----------------------------------------------
    blank           ... 
    blank_ret        ret
    
  • kuronekokuroneko Posts: 3,623
    edited 2012-09-23 05:04
    Yup, that's one solution but it's the one which made me add the run-time comment. I know it's not much of an overhead but it just feels wrong ...
  • Mark_TMark_T Posts: 1,981
    edited 2012-09-23 05:54
    If blank doesn't affect the Z-flag, and we assume ecnt is zero elsewhere in code, then something like this?
    '-----------------------------------------------
                      [COLOR=orange]muxnc   ecnt, #29-1  wz
    [/COLOR]                  [COLOR=orange]call    #blank[/COLOR]                  ' back porch
            if_nz     [COLOR=orange]djnz    ecnt, #$-1  wz,wc
    [/COLOR]       if_z_and_nc jmp     #$-3  wc
    '----------------------------------------------
    

    But I can't quite get it to work... preventing the final jmp from happening more than once is the issue.
  • kuronekokuroneko Posts: 3,623
    edited 2012-09-23 06:47
    For this exercise lets assume that the zero flag isn't affected. Also, the horizontal sync lines are done before so ecnt is in fact zero on entry. Then this will work:
    ' ecnt zero, C clear on entry, Z preserved over a call to blank
    
                    muxnc   ecnt, #29-1 wz
                    call    #blank                  ' back porch
            if_nz   djnz    ecnt, #$-1
            if_nz   jmpret  ecnt, #$-3 nr,wc
    
    But it's not the solution I'm after, interesting approach though.
  • ChrisGaddChrisGadd Posts: 310
    edited 2012-09-23 06:57
    Perhaps I'm missing something, but if all that's needed is for C to be set before the final call, then simply
                    mov     ecnt, #29 -1
                    call    #blank                  ' back porch
                    djnz    ecnt, #$-1
    
                    call    #blank        [color=red]wc[/color]          ' last blank line done manually
    
    From the manual: The C flag is set (1) unless PC+1 equals 0; very unlikely since it would require the CALL to be executed from the top of cog RAM ($1FF; special purpose register VSCL).
  • kuronekokuroneko Posts: 3,623
    edited 2012-09-23 07:12
    ChrisGadd wrote: »
    From the manual: The C flag is set (1) unless PC+1 equals 0; very unlikely since it would require the CALL to be executed from the top of cog RAM ($1FF; special purpose register VSCL).
    Unfortunately the manual is wrong. Carry is unsigned borrow for a jmpret.
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-23 07:24
    Yes, the manual is wrong. I think this would work, but it changes code outside of the lines.
    '-----------------------------------------------
                    mov     ecnt, #29-1  wc
                    call    #blank                  ' back porch
                    djnz    ecnt, #$-1
    
                    call    #blank       wc          ' last blank line done manually
    '-----------------------------------------------
    
    blank_ret       ret
    blank           ...
                    jmp     blank_ret
    
  • kuronekokuroneko Posts: 3,623
    edited 2012-09-23 07:30
    @Dave: call #blank wc would compare a jump insn ($5xxx_xxxx) against a 9bit literal. That's definitely !C.
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-23 09:17
    kuroneko wrote: »
    @Dave: call #blank wc would compare a jump insn ($5xxx_xxxx) against a 9bit literal. That's definitely !C.
    My simulation of the call instruction in SpinSim looks like this:
    	    case 7: // ret, jmp, call, jmpret
    	    cflag = ((uint32_t)value1) < ((uint32_t)value2);
    	    result = (value1 & 0xfffffe00) | pasmvars->pc;
    	    pasmvars->pc = value2 & 0x1ff;
    	    zflag = (result == 0);
    	    break;
    
    I might be wrong, but I'm pretty sure I tested it. The C flag is computed based on the values read from the dst and src fields and the Z flag is computed from the result. I think this is correct because the call instruction is in the same group of instructions as the min and max instructions, which compute the C flag this way also.
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-23 09:21
    Oh, I see what your saying. So the blank routine should look like this:
    blank_ret nop
    blank     ...
              jmp blank_ret
    
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-09-23 10:11
    That would work, but only because the address of the djnz is smaller than the last return address. IOW, you're not comparing with the nop, which has already been obliterated.

    -Phil
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-23 11:10
    Actually it works because the address of the djnz instruction is smaller than the address of blank. A nop compiles to 0, but "blank_ret long $000001ff" would have worked just as well. The 9 LSBs don't matter since they get overwritten with the address of djnz.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-09-23 11:15
    I don't think so. Isn't the comparison done between the old contents of blank_ret and the new contents? At least that's what kuroneko's #9 post suggests. Oh,never mind; I see what you're saying. It compares the contents of the destination with the source, which is the "go-to" address of the jmpret.

    -Phil
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-23 11:23
    I believe it is compared against the src value. For a call instruction this is the "9bit literal" encoded in the instruction.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-09-23 11:30
    But the "9-bit literal" rule would only apply for immediate addressing mode, right? IOW, the comparison would be against the "go-to" address, regardless of where it came from.

    -Phil
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-23 12:04
    For a call instruction this is the "9bit literal" encoded in the instruction. In general, a jmpret can use either an immediate address or an address in a register. The call and jmpret instructions use the same opcode, but they have a different syntax at the source level.
  • kwinnkwinn Posts: 8,697
    edited 2012-09-23 12:07
    I hope I am interpreting the question correctly, but wouldn't adding a "wc" to the "djnz" instruction accomplish this? Not sure if the value ecnt needs to be changed, however that would depend on the length of the back porch desired.
    '-----------------------------------------------
                    mov     ecnt, #29 -1            ' carry is clear on entry - sets ecnt to 28
                    call    #blank                  ' call back porch - returns with carry clear
                    djnz    ecnt, #$-1 wc           ' decrement ecnt and jump to current location - 1 if not 0 (call  #blank)
                                                    ' set carry if ecnt is zero and jump is not taken
                    call    #blank                  ' last blank line done manually
    '-----------------------------------------------
    
    blank           ...
    blank_ret       ret
    
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-09-23 12:09
    Dave,

    Yeah, I know that. The question is what gets compared with the contents of the return statement in the case of a non-immediate jmpret: the 9-bit literal in the jmpret instruction or the value fetched from the address it points to? I'm betting the latter, since that's the "source" value, not the 9-bit value encoded in the instruction itself.

    -Phil
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-23 12:23
    @Phil, yes it is the source value, which would be the contents of the src register if the immeidate bit is not set.

    @kwinn, that won't work because the C flag is set only if the count is -1.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-09-23 12:28
    Dave Hein wrote:
    @kwinn, that won't work because the C flag is set only if the count is -1.
    To clarify: the C flag is only set if count starts at zero and decrements to -1, producing an unsigned borrow.

    BTW, kwinn, if there were an ijnz instruction, your method would work, since incrementing from -1 to zero would cause an unsigned carry.

    -Phil
  • kwinnkwinn Posts: 8,697
    edited 2012-09-23 15:47
    Yes, I misinterpreted the explanation of the djnz instruction in the propeller manual. Changing the call instruction to a jmpret will set the carry flag and accomplish what is desired as long as the PC + 1 does not equal 0, which is usually the case.
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-23 15:54
    The C flag has nothing to do with the current value of the program counter. It is set when the contents of the return-address register are less than the address you are jumping to.
  • kuronekokuroneko Posts: 3,623
    edited 2012-09-23 16:04
    I've been away for about 8 hours and it's still going on, not bad.
    Dave Hein wrote: »
    It [C flag] is set when the contents of the return-address register are less than the address you are jumping to.
    Strictly speaking that's incorrect. The comparison is between destination and source operand. The latter is not always the same as the jump target.
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-09-23 16:09
    The 9 LSBs of the operand are identical to the jump target, but the other bits could be non-zero. Is that what you mean by them not being the same?
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-09-23 16:10
    When would the jump target ever be different from the source operand, given that the term "operand" is correctly applied to the literal in the instruction for immediate instructions and the contents at the literal address otherwise? Edit: Nevermind; I get it. Those contents could be bigger than 9 bits.

    -Phil
  • kuronekokuroneko Posts: 3,623
    edited 2012-09-23 16:11
    Dave Hein wrote: »
    Is that what you mean by them not being the same?
    Yes. Which is more or less the key to another solution.
  • Mark_TMark_T Posts: 1,981
    edited 2012-09-23 19:07
    '-----------------------------------------------
                     mov     ecnt, #29-1
                     call    #blank
                     cmp     ecnt, #2  wc
                     djnz    ecnt, #blank
        
     '-----------------------------------------------
    
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-09-23 19:34
    Mark,

    Nice one! I like it!

    -Phil
  • Mark_TMark_T Posts: 1,981
    edited 2012-09-23 19:40
    Also does this do it:
                    mov     ecnt, #$18    '27 zeros then 2 ones
                    call    #blank
    :loop           shl     ecnt, #1  wz,wc
            if_nz   jmp     #blank
    

    After shifting out the first 1 (into carry flag, thus calling with carry) the next shift leaves register zero, terminating loop
  • kuronekokuroneko Posts: 3,623
    edited 2012-09-23 19:46
    @Mark_T: It's getting close (2 insn slower than my current solution). However, in order to make it work you'll have to increase ecnt (same number of calls) and the comparison operand (in order for cmp ecnt, #2 wc to set carry, ecnt needs to be 1 in which case the loop exits). So this comes down to:
    '-----------------------------------------------
                    mov     ecnt, #29
                    call    #blank
                    cmp     ecnt, #3 wc
                    djnz    ecnt, #blank
    '-----------------------------------------------
    
Sign In or Register to comment.