To be very clear: I fully agree that following MISRA-C or DO178B will result in a better end product.
If you just follow them because it gives you a feeling of quality, without thinking about any quality assurance process, all bets are off.
Absolutely –– but I meant that if you follow them fully,
including the review and QA processes –– then the end result will be better. They specify the product development process, not a subset of C to use to get better results.
Taking just a small part, say the subset of C these standards define, gets you absolutely nowhere (except for delusions). These standards are about the entire process: a framework you work in, not a set of useful ideas you can pick and choose and be good.
So I believe things like MISRA make only very small difference in the big picture, if any.
For individual programmers working alone, they give very little, unless you delve very deep into the very process like DiTBho described wrt. DO178B and "myC", and emphasizing the documentation and review steps needed to
actually fulfill the ideas behind those standards. If I've understood correctly, the entire "myC" idea was to subset-and-extend C to better fit the entire
process, specifically debug/review/verification. (Do firmly point it out if I've misunderstood its purpose, DiTBho!)
Neither MISRA-C nor DO178B should be considered a "programming language standard"
at all. They are about the full development process, with the subset of C chosen to fit that process. They don't work when you work alone; they're organizational process standards.
I claim that statements like ST declaring their code to be "MISRA-C compatible" is just marketing wank, and means basically nothing. It is like declaring oneself to be vegan, because the meat one consumes is all from herbivorous animals. It is picking one detail, and pretending it is the main point in an effort to try and mislead others. For an organization, it means they'll still have to do the entire testing-review-QA cycle –– and knowing ST's code quality as shown online, will have to rewrite most of it to actually
pass and satisfy the present needs.
If you just introduce MISRA and think that this now gives you aviation quality code, without introducing the formal verification that needs to go with it, you just introduced new sources of bugs from ignoring patterns like DRY, well known to improve quality outside of fields like aviation.
Yes, exactly. Or, conversely, that limiting oneself to the subset defined in MISRA-C or DO178B somehow magically makes oneself produce better code.
The methodology or approach to write better code in the first place is usually called
software engineering, because you apply the same engineering principles to software development as you do when designing larger systems with lots of components.
To circle back to the topic at hand, converting machine code or assembly to C, the simple answer is that you don't.
What you do, is convert the machine code to any intermediate form or programming language that makes it easier for you to decipher its operation and the intent of the original developer(s). You write a detailed description of it, either formally (recommended if group effort) or commenting the intermediate language (only valid if doing this alone or with a coding buddy). Ghidra and other tools can be quite useful here.
I like to follow that up by creating a limited simulated environment where I can implement parts/subsystems in my preferred programming language, and compare its outputs to the original outputs from the same inputs. I do this for individual modules. After I have sufficient modules, I start looking at their interactions, and trying to understand how the developer approached the overall problem.
When you have verified you have a full understanding of the operation of the system at hand, you
reimplement it. You don't
convert the code; you write new code that fulfills the same requirements. You use the old code to inform you of the possible approaches, but as long as you note all the side effects and interactions, you don't need to do it the exact same way; it suffices that all intentional side effects and results match.
For myself, I don't mind if the intermediate form of the new code contains
gotos, because I know from experience that when the rewrite/reimplementation behaves like the original, I'll still want to refactor the key parts of the code to be maintainable. I do not have the brainpower to think about long-term maintenance when I first rewrite code from one language to another; I need to sleep in between to see the code with fresh eyes and shift focus. It is at least 99% likely that I'd replace those
goto structures with subfunctions, switch statements, do..while or while "loops", et cetera.
Splitting even this process into sub-steps makes it much more manageable. Even by rereading this thread we can see that starting with an intermediate representation of the machine code (including clunky constructs like gotos and labels scattered everywhere), and then rewriting these using easier to maintain patterns, is the way to go. However, do not forget that you
will forget the intention/purpose of each function and chunk of code in a few weeks. You will definitely want to write comments or descriptions of your understanding of the purpose or developer intent of each function. What the functions do is easy to see in the code, but that intent is what makes it possible to consider whether what the code does is correct or not. If you don't have or remember that intent, you'll have to try and rediscover it, as otherwise you can only fix obvious typos (like off by one errors), and not any misunderstandings of what the purpose of the intent of the original code was.