When programming in assembly language, we have to specify operations in a much greater level of detail than we would have to do in a high level language. Furthermore, we get relatively little help from the assembler in finding errors. Assemblers can only check the syntax of each line, and check that every symbol or label used is defined somewhere. We are much less constrained by the structure of the assembly language than we are by the structure of a high level language. This lack of constraint makes the job much harder, not easier. It is much easier to write an incorrect program in assembly language that in C++ or Pascal. It is much more likely that mistakes, will not be found by the assembler, so that we get runtime errors, which are harder to find, rather than compile time errors which are easier to find. For these reasons, we need to be even more careful when programming in assembly language than we are when programming in a high level language. We need to apply all of the skills we have learned in relation to high level language programming to assembly language programming.
Planning our work carefully is equally important, building a design package, by following appropriate style conventions when writing the program, and by providing good documentation with appropriate commenting of the program text. After building the design, it is wise to write a high level language program, to help check out the program logic and to serve as a model for the assembly language program. The high level language program can then be translated little by little into assembly language, perhaps a procedure at a time, and can be used to document the logic of the assembly language program. Clearly, we should use a compiler to translate a high level language program into machine code if we have a compiler at hand. There are circumstances, however; when it is necessary to do the translation on our own. It is nevertheless wise to write out the high level language program, even if you cannot compile it for the machine in question.
It is not necessary or desirable for the programming of embedded microprocessors to be done in assembly language. Indeed, assembly language should be regarded as a last resort, to be used when compilers are not available, or in very special circumstances. It is, however, necessary for computing scientists and software engineers to have a good understanding of computer architecture, and some exposure to assembly language programming is an important part of achieving overall system programming knowledge.
The layout of the assembly language code should follow the high level language structure where possible and sections of code should be marked out clearly in relation to their function. They should be as self contained as possible, and could for example correspond to procedures in the high level language form. Each routine or function should have exactly one exit point. Branches and jumps should remain within a routine or function. They should never be used to jump out, except possibly in the case of errors which require special exception handling. A routine or function may, of course, call another routine or function. A call is generally made by storing parameters and executing a JSR or BSR instruction. The main entry point to a program should normally be to its first instruction, so that when the program is loaded, the entry point can be presumed to be at its lowest address in memory. Symbolic names (defined by labels and equate statements) should be chosen to convey meaning, in exactly the same way that we choose names in a high level language program. Equate statements should be used to define constants or to name registers where they would improve readability of the program.
Appropriate comments are even more important in assembly language programming than in high level language programming.