Connect with us

Can I return to a return?

Discussion in 'Microcontrollers, Programming and IoT' started by Oatamelian, Apr 29, 2013.

Scroll to continue with content
  1. Oatamelian

    Oatamelian

    21
    0
    Mar 9, 2013
    So here it is.

    My PIC project code is like this:

    ;****** MAIN PROGRAM ******

    MAIN goto Simsi1
    goto DET1

    ;***************************

    Simsi1 bsf PORTB,0
    bsf PORTB,4
    call Pause1
    INTB btfss PORTA,0
    goto INTB
    return

    ;****************************

    DET1 goto ButC1
    return

    ;****************************

    ButC1 btfss PORTA,0
    goto testo
    return
    testo btfss PORTA,1
    goto ButC1
    goto wrong


    I've typed it on here... this is a simplified version of my current code

    just realised you can upload .txt files FML

    Anyway, as you can see, it starts with the MAIN program
    goes to 'Simsi1'
    then returns to the main program
    goes to DET1, from here it goes to ButC1 then returns to DET1
    there is a 'Return' on DET1 will this lead the program back to the MAIN program?

    I can post the current program if you can't understand.
     
  2. Oatamelian

    Oatamelian

    21
    0
    Mar 9, 2013
    It hasn't included the spaces... ffs
     
  3. CocaCola

    CocaCola

    3,635
    5
    Apr 7, 2012
    Disclosure every compiler is unique so there are no clearly defined way any particular compiler will handle any command...

    But, in general RETURN is used with GOSUB, not GOTO... There is no return from GOTO it's a permanent jump...

    BTW your program is the reason why GOTO is now taboo in programing... Flow chart your program and device a logic that does not use 50% GOTO jumps, as it's a horrible way to program and you will have a beast of a time trying to debug...
     
  4. Oatamelian

    Oatamelian

    21
    0
    Mar 9, 2013
    So if I was to use "call" instead of 'goto' then all would be well?
     
  5. CocaCola

    CocaCola

    3,635
    5
    Apr 7, 2012
    Just noticed it's assembly, only a quick glance before my last reply...

    You need to eliminate all those jumps, use other programming logic to accomplish your task... Jumping around like a squirrel on crack is simply the wrong approach to programming...
     
  6. Oatamelian

    Oatamelian

    21
    0
    Mar 9, 2013
    Righteo, I'll try to sort out these jumps
     
  7. KrisBlueNZ

    KrisBlueNZ Sadly passed away in 2015

    8,393
    1,270
    Nov 28, 2011
    I can't follow your program at all, but in general, it's perfectly valid to return to a return instruction. For example:

    01. main:
    02. . . // do stuff
    03. . . call subroutine1
    04. . . jmp main
    05.
    06. subroutine1:
    07. . . // do stuff
    08. . . call subroutine2
    09. . . ret
    10.
    11. subroutine2:
    12. . . // do stuff
    13. . . ret

    When subroutine1 is called, it does some stuff (line 07) then calls subroutine2. subroutine2 then does some stuff (line 12) and returns (line 13). When the CPU returns at line 13, it returns to line 09 (the address immediately following the call to subroutine2). The next instruction (line 09) is another return, so the CPU returns to line 4 (the address immediately following the call to subroutine1).

    This is a simple case of nested calls. The CPU takes care of making sure everything works as it should. If you're not sure you understand this, carefully read the section about how the stack is used by the call and ret instructions and consider how the CPU will handle the above code.

    Also beware if you're using a small PIC - some of these have a very small stack.

    In practice the above code wouldn't be written like that. If subroutine1 needs to call subroutine2 then return, the call and return in subroutine1 would be replaced by a jump to the start of subroutine2, or in this case, since subroutine2 immediately follows subroutine1, no jump is needed as execution would "fall through" to subroutine2.
     
    Last edited: Apr 30, 2013
  8. (*steve*)

    (*steve*) ¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd Moderator

    25,418
    2,788
    Jan 21, 2010
    Kris's suggestion of using a goto to a subroutine with a return at the end is the stuff of nightmares.

    HOWEVER, many microcontrollers have a very short stack and minimising the depth of subroutine calls may be very important. His suggestion does minimise the utilization of the stack.
     
  9. KrisBlueNZ

    KrisBlueNZ Sadly passed away in 2015

    8,393
    1,270
    Nov 28, 2011
    It also reduces code size and execution time.
    I always place a comment "Fall through..." when one subroutine falls through to the start of another one.
    Why do you say it's "the stuff of nightmares"?
     
  10. (*steve*)

    (*steve*) ¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd Moderator

    25,418
    2,788
    Jan 21, 2010
    When you're working on pieces of code with maybe hundreds of thousands of lines of code...

    Some very useful documentation is a list of where calls to a procedure or function come from. In several of my systems such documentation is generated automatically (it would be a nightmare to try to keep it up to date manually).

    As such, if I have to make a change to code, I know where it's called from and this allows me to check for possible problems and to carry out testing.

    Code containing gotos, especially ones which perform these gotos across function boundaries can throw this into some disarray.

    Furthermore, if you have different calling conventions, you may find that you don't get the right cleanup of the stack. This may sound unlikely, but it appears to be the cause why a particular third party tool we have fails to work in certain circumstances.

    In general terms, it's simply bad practice.

    Having said that, you're probably talking about code that's much smaller, is spread over less source modules, is operating in a machine with a simple architecture, and where you may only have one person working on it. In addition to that, you're also possibly programming in assembler, and/or needing to perform optimisations which reduce the readability of the code.

    A good optimising compiler will look for calls placed just before returns and convert them to jumps where possible. However this needs to take into account the method of parameter passing and how the stack is cleaned up -- less of an issue in a uC environment.

    Unfortunately, some uC compilers are sold with optimisation as an added-cost item. This simply encourages poor practice amongst those who can't afford a good compiler :(

    Where needed, a sacrifice of readability for size or time can be worth while. However to routinely sacrifice readability where the efficiency gains are not required simply leads to code which is difficult to maintain.

    A wise person once asked me "What is source code for?"

    The correct answer is "It is for people to read."
     
  11. KrisBlueNZ

    KrisBlueNZ Sadly passed away in 2015

    8,393
    1,270
    Nov 28, 2011
    That's a valid point.

    Not relevant for assembly-language programs where parameters are usually passed and returned in registers and calling conventions are explicitly stated for each function. In high-level-language programs, the only way to chain to a second function at the end of the first function is with a goto, which I don't recommend in high-level languages. The idea of execution falling through from the end of one section of code into the start of a second function is meaningless in HLLs that I'm familiar with. This thread relates to assembly language programming only; my example code is clearly assembly language (call and ret instructions, and subroutines identified by labels with colons at the end).

    I disagree. If you think that's a rule, then I think it's a rule that is made to be broken in certain circumstances. Efficient assembly language coding is one circumstance. I have never found it confusing.

    Yes I am specifically talking about assembly language, as that is the language used in my example and in the first post in this thread.

    I disagree that it reduces code readability. I find it very natural and logical in cases like the example I gave.

    You're talking about high-level languages again.

    I disagree. Assembly language code is generally harder to maintain than high-level-language code and falling through to one function from the end of another section of code is, in my experience, a normal part of assembly language coding.

    No; books and documentation are for people to read. Source code is for people to read _and_ for a processor to execute!
     
  12. Oatamelian

    Oatamelian

    21
    0
    Mar 9, 2013
    Skips the entire program and just ends with the winner subroutine

    Ok... If I was to post the whole program on here, would someone be willing to explain something else?

    Quite simply this is what it does:
    - shows a sequence of LED flashes
    - 1 more LED will flash in sequence through a pre-programmed 25 stage memory game
    - similar to SIMON, the program detects the inputs on PORTA and responds accordingly

    The issue:
    When I start the program on the board it displays the first stage but then once an input is pressed it skips straight to the "winrar" sub-routine.
     
  13. Oatamelian

    Oatamelian

    21
    0
    Mar 9, 2013
    Right... The site doesn't like .ASM and the .TXT was too big. I've cut out a good portion of the repeated commands for the main sequence. It would normally go to 25.
     

    Attached Files:

  14. (*steve*)

    (*steve*) ¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd Moderator

    25,418
    2,788
    Jan 21, 2010
    What PIC are you using? (Duh -- 16C84, having a 8 level seep call stack)

    It appears that your code calls subroutines quite deeply.

    Are you trying to run this using in circuit debugging?

    If this is an issue, I would recommend that you flatten the code in your delays since the additional call level here seems superfluous.

    Do you have an emulator/simulator you can run this on?

    I note that some parts of your code are well documented, but others have very sparse documentation. I often find that adding documentation "magically" solves some problems.

    I also wonder if your CALL's in the vectors shouldn't be GOTO's, and whether you should put a RETI (or similar) at the end of your (unused) interrupt routine. Note that your comments say "goto" but your code says "call".

    After a brief examination, I can't see anything that sticks out as the obvious cause.

    I suspect that Kris might do a lot better :)
     
    Last edited: May 3, 2013
  15. KrisBlueNZ

    KrisBlueNZ Sadly passed away in 2015

    8,393
    1,270
    Nov 28, 2011
    These are the notes I made while reading through your program. I haven't identified the bug, but I strongly suggest you follow this advice. When you do, the cause of the problem will _probably_ (based on my previous experience) become obvious to you. Even if it doesn't, improving the quality of documentation in your code will be hugely beneficial when you come back to it in six months to add a feature, when the details of how it works are not fresh in your mind.

    Your program should start with a comment block that contains the following information:

    Program name
    Location and name of this source file (handy if you print it out; when you find an old printout later, you will have a better chance of locating the file that the printout was made from);
    Your name and email address;
    Purpose of the program and a brief description of what it does.

    Then you should record the changes made to the file. Each time you make a change you should record the when, who, what and why. In other words, the date, your name or initials, what was changed, and if it's not obvious, why the change was made. Also when you make a change, consider how to make the comment useful to your future self. So don't just say "changed npulses to 5", write "change npulses from 4 to 5 because ..." and for bug fixes, mention the known or possible symptoms of the bug that you fixed.

    This may sound like a lot of work, but it's an investment that will, on average, save you a lot more work later.

    It seems odd to me that the jumps to start and inter, at 0x0000 and 0x0004, are coded as calls instead of jumps (gotos). Maybe it's something strange about the PIC. (I'm not familiar with PICs.)

    You should clearly comment the purposes of your time delay functions. The most basic information - that is, how long each function delays for - is missing!

    It is normal to place a comment block at the start of each function, that documents its purpose and also states which registers and variables it uses for parameter input (if applicable) and output (if applicable), and which registers and variables it modifies. This can help you avoid problems caused by calling a function that destroys a register or a variable that is being used by the calling function for a different purpose.

    On the same subject, I would use meaningful names for your temporary counter variables used by the functions. Names like ted, ned and edd are meaningless; as soon as you use a variable for a specific purpose, you should give it a meaningful name. This will also help you avoid problems caused by different parts of the program using the same variables and effectively scrambling the values that are being relied on by other parts of the program.

    All your "button detect" functions need proper comment blocks. The purpose of this is three-fold. First, it clarifies the purpose of the function in your mind. Second, it allows you to quickly see whether the logic of the function matches the description. Thirdly, when you call the function, you can check its description to make sure that it's the function you intended to call.

    Also your functions should be named with something more than a number to distinguish them. For example, a function that delays for one millisecond should be called delay_1ms instead of delay2.

    I don't understand the meaning of the lines that say "movlw 500 ; decimal number 100 ..." Have you updated the number in the instruction but not updated the comment?

    Your main function has a section with 360 calls in a row. This is ridiculous and unmanageable. You need to restructure your code to avoid this, using loops with control variables. Once you have documented what your functions do, and reposted the code, someone here will be able to suggest how to do that.

    That should be enough for you to go on with. Please take my suggestions seriously; they are based on 25 years of hobby and professional embedded systems programming experience and they are intended to make your job easier.
     
  16. (*steve*)

    (*steve*) ¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd Moderator

    25,418
    2,788
    Jan 21, 2010
    So what's source code for again...? ;)
     
  17. KrisBlueNZ

    KrisBlueNZ Sadly passed away in 2015

    8,393
    1,270
    Nov 28, 2011
    Source code consists of two parts: comments, and code.

    The comments are solely for people (including the author - actually, mainly the author) to read. The code is for the author to read and for the processor to execute. (And for the compiler to compile, if it's a compiled language.) All three requirements exert a pull (usually in different directions) on how the code should be written.

    I guess that's a more thorough description of how I see it.

    Edit: In my earlier post when I was describing what the source file should contain, I forgot to mention that you need to record version information. How you handle versioning depends on practical requirements. For a simple single-source-file program with only one build variant, a simple number is enough; typically version, subversion and modification-version, e.g. "2.3.1". If possible the production code should be able to indicate its version number somehow. In your case, a very short display on the LEDs at startup might be appropriate. For example, for version 2.3.1, flash the LEDs twice, pause, flash three times, pause, and flash once. For zero you could use a long flash, or you could offset the number of flashes by 1 so that 2.3.1 is displayed as 3, 4 and 2 flashes and a 0 digit is displayed as one flash. Just suggestions.
     
    Last edited: May 3, 2013
  18. CocaCola

    CocaCola

    3,635
    5
    Apr 7, 2012
    One other comment I will add about programming, SAVE ALL REVISIONS, don't keep saving over and over the same file, instead create a timeline of source code... Trust me you might think it's pack ratting to have a folder with dozens upon dozens of revisions with only minor differences but in the long run you will thank me when you want to roll back a feature, change your direction or fix something you broke with new code...

    BTW I even save as a new file after I spend some time tinkering with the source even if I didn't accomplish what I was trying to do that day, as sometimes a clean fresh mind the next day allows you to see the stupid mistake you made that will all the sudden allow that broken revision to work without starting all over... I usually just save these files with the same revision number but put a _broke_001 suffix to the saved source...
     
    Last edited: May 3, 2013
  19. Oatamelian

    Oatamelian

    21
    0
    Mar 9, 2013
    I'm using a 16F84 PIC chip.

    I shall edit the documentation with the program so that you have a clearer understanding of what is going on and then get back to you, I shall use the suggestion for different versions in the future.

    I'll post it back on here when I've completed this.
     
    Last edited: May 3, 2013
Ask a Question
Want to reply to this thread or ask your own question?
You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.
Similar Threads
There are no similar threads yet.
Loading...
Electronics Point Logo
Continue to site
Quote of the day

-