Thursday, 21 March 2013

Stepping through the code


Stepping through the code

It is generally considered easier to control the debug process at the level of the program files rather than
through the disassembly. Although this is again a personal choice we will work from our C program file and
assume that users who prefer to work with the assembly language can extrapolate the principles involved.
You can view the calc.c file by selecting it from the Window menu. As we stand we can either step into the
initialise routine or step over the routine and on to the next part of the program. For our purposes assume
that we are not interested in this routine and so we want to run the simulation until we reach the entry point
of the main function. We can do this in one of two ways:
·Set another breakpoint at the point to which we want to advance and then select the Run command from
the Debug menu.
·Place the cursor on the line to which we wish to advance and select the Run to Cursor Line command
from the Debug menu.
Using one of these methods advance the simulation over the initialise and output functions coming to halt at
the line reading:
calc_evaluate(); (line 29)
Looking at ISIS we should now see some changes – the LCD display has initialized and the digit ‘0’ is
displayed. Now let’s examine the calculator in action. We will set a breakpoint at the point of receiving a valid
digit, i.e. on the line:
*bufferptr = key; (line 52)
We can set the simulation running and use the keypad in ISIS to enter a digit. Do this, entering the digit ‘7’
on the keypad, and then view the disassembly in the Keil window.
We can immediately see from the red rectangle in the disassembly window that we have stopped at a
breakpoint. This tells us that we have received a valid digit from the keypad. We will now take a slightly more
in depth look at how this digit is passed through the program.
Disassembly in Detail
The first thing that happens in the disassembly is that we move the contents of 0x0B and 0x0C into the Data
Pointer. Examination of the Internal Memory window in either ISIS or Keil reveals that this value is 0x0009.
Single step twice over these instructions and you should see that the value of the Data Pointer has indeed
changed correctly.
Next we move the contents of 0x08 into the Accumulator. Again, investigation reveals that this value is
0x37 which is consistent with the result after stepping past the instruction.
Finally we move the contents of the Accumulator into the address specified by the Data Pointer. A further
single step followed by an examination of the memory windows will confirm that this has happened. In
essence we have moved the ASCII value for the digit seven into a memory location (which happens to serve
as the first element of the specified array) which correctly reflects the current line in Calc.c.
The next line in our C file is a call to another routine taking a character pointer as an argument:

We can see from the disassembly that the next three instructions move values into registers R1-R3. These
instructions are followed by a ‘long call’ instruction which clearly corresponds to our function call in the C
program.
The Keil compiler by default passes generic pointers using three bytes, where the first byte specifies the
memory type and the second and third bytes specify the high and low order bytes of the offset respectively.
In our case this translates to be the external data memory space with an offset of nine. Again, after single
stepping, we can confirm from our memory windows that this location contains the value of our digit.
Note: The Keil C51.pdf detailing the operation of the compiler is available from the Books tab on the left
hand pane of the Keil IDE or from the website.
We now have the choice of entering the routine via the Step command or advancing beyond the routine via
the Step Over command. In order to follow our entered digit we should step into this routine.
We can see that the first thing that happens in our new routine is that the parameter passed in registers R1-
R3 is stored freeing up these registers for local use.
Next we clear the accumulator and store it’s value in memory locations 0x11 and 0x12. This corresponds
to the execution of the first line in the calc_display function:
INT data i = 0; (line 257)
Stepping through the disassembly we find ourselves at another ‘long call’ instruction. This corresponds to
the call to the clearscreen() function in our C source file and, as such is not relevant to the current
exercise. Use the Step Over instruction to advance the simulation beyond this instruction..
Note: Caution is required when stepping over functions during the debug of a real problem. Bear in mind that
not only are you stepping over the execution of the function in question but also of any functions called by
that function.
A quick check of the ISIS display at this point will confirm that the LCD display has indeed been cleared.
The next few instructions are quite involved and deal with the conditions specified in the C source file for
allowing a character to be outputted to the display. Given that we know that our character is valid and will
pass these checks we won’t concern ourselves with them but simply single step through them.
The by now rather familiar action of loading registers R1-R3 with our parameter comes next before yet
another function call – this time to the assembly language LCD controller routine.
At this point we will leave the trail and step over this function call (feel free to follow through the routines if
you want). This has the immediate and rather gratifying effect that the digit seven is now correctly shown in
the LCD display in ISIS.
Obviously there is a lot more to the debugging process than we have covered here but hopefully you should
have some idea of the possibilities and advantages of using Proteus VSM and the Keil IDE in partnership to
debug 8051 circuits. The next section of the document lists some other, less commonly used features of the
products.

No comments:

Post a Comment