1
\$\begingroup\$

In an efford to save space I try to reduce the generated C code with inline asm(""); Looking at the original disassembly and good working code I do not understand line 0x17E4.

void MENU_BUTTON(uchar Parm){

    // This loop ends when the enter key is pressed
    while EnterOpen(){                 // SW3 = RE2

       if ArrowUpClosed(){             //    RE0 
           while ArrowUpClosed(){};
            asm("call MBD");
            if ArrowUpOpen()       
                  LCDGotoPos(ArrowUp[index]);
        };
        if ArrowDownClosed(){          // SW3 RE1
            while ArrowDownClosed(){};
            asm("call MBD");
            if ArrowDownOpen()
                 LCDGotoPos(ArrowDown[index]);
        };
        if ArrowRightClosed(){          // SW4 RC1
            while ArrowRightClosed(){};
            asm("call MBD");
            if ArrowRightOpen()
                   LCDGotoPos(ArrowRight[index]);
        };
        if ArrowLeftClosed(){           // SW5 RC0
            while ArrowLeftClosed(){};
            asm("call MBD");
            if ArrowLeftOpen()
                   LCDGotoPos(ArrowLeft[index]);
        };
    };
    Pause(300);
    return;
    // a little trick to save space
    asm("MBD:");
    delay();
}; // End of MenuButtons.


    // This loop ends when the enter key is pressed
    while EnterOpen(){                 // RE2
    0x178E: BCF STATUS, 0x5
    0x178F: BCF STATUS, 0x6
    0x1790: BTFSS PORTE, 0x2
    // jumping out of the loop as expected
    0x1791: GOTO 0x7E5    
    // returns to the beginning? but during execution goes to 0x1792.
    // The way I expect
    0x17E4: GOTO 0x78E                 
    // during execution 
    //if ArrowUpClosed()             //   RE0 
    0x1792: BTFSC PORTE, 0x0

    //other code
    } // end of while EnterOpen

    //    Pause(300);
    0x17E5: MOVLW 0x8
\$\endgroup\$
  • \$\begingroup\$ This is disassembly. What is the original assembly code (and do you have it)? \$\endgroup\$ – Anonymous May 12 '17 at 8:25
  • 1
    \$\begingroup\$ Erm is it just the ordering of the lines which is wrong? I mean 0x1792 should be in front of 0x17e4. And your question is a bit hard to understand as there are comments which are part of the question and comments for old code (I think?). \$\endgroup\$ – Arsenal May 12 '17 at 8:25
  • \$\begingroup\$ I have added the complete C code of the routine. The disassembly code is comming from the C code \$\endgroup\$ – Decapod May 12 '17 at 8:39
  • \$\begingroup\$ According to @Arsenal's finding I would question quality of disassember output. In its output it has sequence of 0x1791, then 0x17E4, and then 0x1792. The middle location, 0x17E4, is out of the program counter sequence. \$\endgroup\$ – Anonymous May 12 '17 at 8:54
  • \$\begingroup\$ Meanwhile I agree with Anonymous. I changed the instruction handling to my knowledge and datasheet and the routine works also in asm \$\endgroup\$ – Decapod May 12 '17 at 9:01
2
\$\begingroup\$

Caveats: It has been more than a decade since I routinely wrote PIC assembly code (and used their C compiler -- not sure if my license is any good anymore, either.) You also didn't say which compiler you are using, so I can't go get a copy and see what it generates. And I don't even know if you are using a PIC16 or a PIC18 (different creatures, with the PIC18 having nicer arrangements for assembly code.) Finally, I am assuming you pretty much are up on things with the PIC and that in some ways you will be more able than I am to answer specific questions about its assembly code because of your current work versus my decade old (and more) knowledge about it.

Assumptions:

  1. It's a PIC16 (or lower) family part and not a PIC18.
  2. When you write in assembly code you are using a real assembler and not hand-coding machine code.
  3. You haven't been looking closely at the generated machine code.
  4. You're unstated confusion is over the address there.

Assuming the above are correct, I suspect you may have missed out noticing that the GOTO instruction only uses 11 bits for the address. It can only branch within the current code "page," where the upper address bits remain unchanged in the branch to a new address.

When you write in assembly, you usually use labels. Even if you used absolute addresses, the assembler still does "do the math" for you to make sure that the address you specify is "within reach." So you wouldn't even know one way or another what is going on unless you look closely at the machine code it generates.

Let me document what I see above (along with a missing column which I sincerely wish you'd have also included):

Address    Machine Code    Code Lines             Description
0x178E                     BCF STATUS, 0x5        These two lines make sure that data access
0x178F                     BCF STATUS, 0x6            is on page 0 where PORTE is at.
0x1790                     BTFSS PORTE, 0x2       Skip GOTO if the loop should continue.
0x1791                     GOTO 0x7E5             Go out of loop to 0x17E5 (past the GOTO.)
0x1792                     BTFSC PORTE, 0x0       "if ArrowUpClosed()"
0x1793                                            <>
0x1794                                            <>
  ...                                             ...
0x17E1                                            <>
0x17E2                                            <>
0x17E3                                            <>
0x17E4                     GOTO 0x78E             Branch to address 0x178E (start of loop.)
0x17E5                     MOVLW 0x8

If you note that there are only 11 bits in the encoded machine instruction for the GOTO, then you can see why the entire "0x17E5" or "0x178E" can't be encoded into the machine word. However, that fact doesn't prevent the assembler and/or compiler from knowing that the address can in fact be properly reached (or not, indicating that more code may be needed to make a more distant transfer.)

The compiler's code works, I think you say. You also say that when you wrote some assembly code of your own that it also worked. I believe both of these are true. But the issue may simply be that you haven't realized how an assembler (more importantly, how a linker) works inside to generate correct code or to generate errors.

The assembler and C compiler will generate an object file that contains what amounts to "strings" of literal byte values + periodic "patch up" records which instruct the linker exactly where and exactly how to fix up bits and pieces of those literal strings, during the linking phase when the code actually gets placed somewhere. For assembly generated code, I'm pretty sure this is a very simple process and the linker will complain if you write instructions that would require an out-of-range encoding. For the C compiler, I'm not so sure about the complexity extended to the linker. But it is possible that the C compiler doesn't want to worry about whether or not some bit of code spans across a code page and instead just provides the linker with enough added information so that the linker can make the determination and "automatically extend" a string of bytes as needed (making the linking process more complex but also leading to less confusion of the typical C coder.)


I've had my say about it. It could be that I totally misunderstood your quandary here. I just woke up and who knows if I'm just sleep-walking through this. But there it is. Perhaps you could clarify the device, etc., too?


For those interested in some history about the XC8 C compiler, as well as some information about the optimizations differences in the PRO versions, I found this link immediately useful. From it, I can see that the XC8 compiler is basically the Hi-Tech compiler code, with added "impairments" in order to "improve" motivations to buy the PRO version of their compiler toolset. Interesting details there.

\$\endgroup\$
  • \$\begingroup\$ The device is a pic16F877A. Programming done with XC8 compiler. I was not confused by the 11 bits needed for GOTO. The GOTO 0x78E looks to be skipped (not clear why?) since the program continues at 0x1792 as it should. \$\endgroup\$ – Decapod May 12 '17 at 17:01
  • \$\begingroup\$ @Decapod I'm confused about your confusion. The BTFSS skips the following GOTO if the loop should be executed. It does not skip the GOTO if the loop should not be executed. That makes sense to me, completely. They just reverse the sense of the BTFSS/C test, as needed. The GOTO immediately after the BTFSS is the way the loop is killed, which is why it branches after that last GOTO. \$\endgroup\$ – jonk May 12 '17 at 17:12
  • \$\begingroup\$ Oh oh. I must have been sleeping. Understanding the function and functionality and then overseeing this situation leading to this. Where were my brains. Sorry!! Focus to close on reducing code I guess. Meanwhile I have found out that the code eaters are the lookup tables A direct approach in asm with retw saves a lot of space. \$\endgroup\$ – Decapod May 12 '17 at 17:16
  • \$\begingroup\$ @Decapod The BTFSS/C instruction pair is a VERY POWERFUL tool in reducing code size. I spent a lot of time writing assembly for PIC10, PIC12, PIC14, PIC16, and PIC18 families and there were countless situations the compiler could not even begin to see where those instructions saved countless cycles and instruction lines. Do NOT assume they only skip GOTO instructions. They are useful for lots of other things. I even used them back-to-back, with one skipping the other for rather useful, complex logic. \$\endgroup\$ – jonk May 12 '17 at 17:21
  • \$\begingroup\$ I discovered that also and found their usefullness in other parts of the program . \$\endgroup\$ – Decapod May 12 '17 at 17:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.