Assembly (What if -- 3) -- C" compatible source file comments |
We have demonstrated the use of the HVZ memory operation notation for use as comments to the assembly language operations to explain what is occurring during the code. Another school of thought is to make use of a "high level language" line of code to document each assembly line's operation. You may prefer this comment approach if you are already familiar with programming in "C" or "C++".
In addition, later we will be linking "C" language subroutines with our assembly language files. It is therefore useful to get familiar with this operation by making use of "C" language code as comments.
As a reminder, this is file e1v2.s using the HVZ memory operation notation as comments
; Code from HVZ Computer Organization ; Chapter 2 -- Figure 2.26 -- page 67 ; A 68000 routine for C <- [A] + [B] ******************************************************* C EQU 0x202200 ; Specify location to store the result section const ; Start of CONSTANTs section A DC.W 639 ; Specify and initialize a constant value B DC.W -215 ; Specify and initialize a constant value section code ; Start of CODE (program) section .EXPORT mycode mycode MOVE A, D0 ; D0 <- [A] ADD B, D0 ; D0 <- [D0] + [B] MOVE D0, C ; C <- [D0] END
One of the advantages of using a high level language such as "C" is that the majority of the time the code developed by a compiler is efficient enough for the task we have in mind. On some occasion, assessing external devices or in tight loops, we may find that coding a subroutine in assembler is the approach to take. Being famliar with the relationship between "C" and assembly code is therefore a useful skill to develop. One approach to developing this skill is to use "C" syntax as the comments for assembly language code. You will often find that one line of "C" code will very effectively comment many lines of assembler in a way that "normal English" would not.
Using "C" language lines as comments for the memory operations performed during each instruction can look a bit strange. The strangeness occurs as we are not using "C" as it is currently used -- a "high level" language. Instead we are using "C" as it was originally designed -- a "portable assembly language" . Portable means the same notation used across many different processors.
File e1v2c.s shows Task 1 expressed with "C" language comments. The next section will explain why the syntax is this way.
; Code from HVZ Computer Organization ; Chapter 2 -- Figure 2.26 -- page 67 ; A 68000 routine for C <- [A] + [B] ; "C" comment version ******************************************************* C EQU 0x202200 ; Not exactly #define C 0x202200 ; (Actually this statement is more properly expressed as ; the following "casting" operation as "C" is a specific ; memory location. Accessing a user defined memory location is ; not a standard operation in the "C" language ; short int *C; ; #define CADDRESS 0x202200; ; C = (short int *) CADDRESS; section const ; Start of const section (linker operation) A DC.W 639 ; short int A = 639 B DC.W -215 ; short int B = -215 section code ; Start of CODE (program) section (linker operation) .EXPORT mycode ; void mycode(void) { mycode ; register short int D0; MOVE A, D0 ; D0 = A; ADD B, D0 ; D0 += B; MOVE D0, C ; *C = D0; RTS ; } END ; ("C" source EOF marker)
A DC.W 639 ; short int A = 639 B DC.W -215 ; short int B = -215
register short int D0
Normally it is inadvisable to "tell" the compiler to use a register for a variable. It may be a bad choice (slower code). Most modern compilers will make the decision for you if analysis of the code indicates that using a register would speed the code. Examine the large number of optimizations available with the SDS cc68000 compiler. There are several different optimizations specifically for processor register usage.
void mycode{void) { ........ }
This function block above has been given a return statement in the assembler code. The addition of this statement is part of the optimization associated with the simulation of Laboratory 1.
C EQU 0x202200
You might expect the EQU to translate as a #define statement. Normally this would be in the case if the label C represented a constant. However, in this case, the label C represents an ABSOLUTE memory location, something not normally allowed in the "C' language. To access an absolute memory location requires pointer operations. Pointer operations are "natural" in assembler with many instruction addressing modes on the 68K processor designed to allow very complex pointer operations.
We therefore define a short int pointer (C)
; short int *C;
A reminder, short int pointers point to a 16-bit memory location, but are the standard pointer size (32 bits). This distinction is automatically handled for you in "C" but can cause problems when doing pointer operations in assembler.
The next two steps cause the pointer C to point to the correct memory location.
; #define CADDRESS 0x202200; ; C = (short int *) CADDRESS;
An ANSI standard "C" compiler requires that you perform a cast between constants and pointers. The need for the casting is more obvious if you make use of "C++" language construct const and avoid the #define statement
; short int *C; ; const long int CADDRESS = 0x202200; ; C = (short int *) CADDRESS;
Actually if you are using a "good optimizing" C compiler, you are not finished yet.
Optimizing compilers can speed up code operation by taking into account where in your code you reuse variables. These variables are stored directly in registers for fast quick reuse. This can cause a problem if the pointer C points to a register in an I/O device, then the value can change (from some external device operation) in that device register without being changed from inside the "C" program code. This will confuse the optimizing compiler as it will re-use the old stale (out-of-date) value.
You can force the compiler not to store the "device register value" by using the "C" keyword volatile as in
; volatile short int *C; ; const long int CADDRESS = 0x202200; ; C = (volatile short int *) CADDRESS;
Using the volatile keyword is very confusing, especially if you are not sure of its use and whether the "C" compiler does or does not handle this new keyword volatile correctly.
A more reliable approach is to specifically code all functions that access I/O devices directly in assembler code. Then you will know what should happen. The mistakes become obvious rather than hidden by the compiler.This approach of using assembly language I/O functions is one that SDS recommends in their USER manuals, and is the approach that I will adopt for the "Laboratory Companion".
![]() |
|
Last modified: July 01, 1996 09:38 AM by M. Smith.
Copyright -- M. R. Smith