479 lines
25 KiB
Plaintext
479 lines
25 KiB
Plaintext
====================================================================
|
||
DR 6502 AER 201S Engineering Design 6502 Execution Simulator
|
||
====================================================================
|
||
|
||
Supplementary Notes By: M.J.Malone
|
||
|
||
|
||
Suggested Programming Style and Debugging Methods Using DR6502
|
||
==============================================================
|
||
|
||
So far in the documentation, you have been introduced to the
|
||
6502, its assembly language, the project board and the 6502
|
||
simulator. Several times there have been references to 'proper' or
|
||
'suggested' programming style. These suggestions are for the
|
||
purpose of making debugging easier. Learning and writing assembly
|
||
language is not difficult since the commands are limited to a finite
|
||
set each performing very simple manipulations. Debugging assembly
|
||
language is THE hardest problem. Even with a software simulator
|
||
with extensive program monitoring, finding bugs in a code is very
|
||
difficult. By following certain programming methods, bugs are more
|
||
easily located.
|
||
|
||
|
||
'Bottom Up' vs 'Top Down'
|
||
-------------------------
|
||
|
||
Much discussion has been spent on the 'Bottom up' and 'Top
|
||
down' programming styles; each has its merits for different
|
||
applications. The 'Top down' strategy involves writing the highest
|
||
logic level of the program first and then the subroutines and then
|
||
the subroutines of subroutines etc until the task is complete. The
|
||
'Bottom up' strategy involves writing basic functions first and then
|
||
building on them to produce a the code from the bottom of the
|
||
pyramid.
|
||
|
||
The 'Top down' method has advantages especially if several
|
||
people are working on the same code. The main logic could be
|
||
decided and the task divided into modules which could be distributed
|
||
among the team members. The data structure and the calling
|
||
parameters for each module would have to be fixed to assure
|
||
compatibility. Problems can arise if the early definition of the
|
||
problem and its division into modules leads to some unanticipated
|
||
difficulties at the level of the elementary subroutines. For
|
||
example, duplication of tasks can occur when two modules are
|
||
designed that require (because of their definition) slightly
|
||
different subroutines that perform essentially the same function.
|
||
As a result of the duplication, the program is longer and has more
|
||
parts to debug. This is not a problem since there are more people
|
||
to debug the program and computer memories are usually not a
|
||
limiting factor. The entire task, with a team at work, when
|
||
programming 'Top down' can be completed quickly.
|
||
|
||
The 'Bottom up' method has advantages especially when starting
|
||
at machine level applications. The basic capabilities required to
|
||
manipulate data are identified and are written. From there the
|
||
program develops as ever more complex manipulations until the main
|
||
program can be written to perform the required task. Problems can
|
||
occur in 'Bottom up' style if a clear idea of the goal of the
|
||
program is not kept in mind. For example when advancing to a higher
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
page 2
|
||
|
||
level manipulation it may be realised that a poorly defined storage
|
||
method for some critical data element may have to be changed.
|
||
Reprogramming would have to begin at the lowest level of the
|
||
occurrence of the problem. 'Reaching the top' may seem impossible
|
||
when working on the manipulation of basic items. A 'Bottom up'
|
||
strategy does not duplicate low level functions and will lead to
|
||
smaller and probably faster code. 'Bottom up' strategy is not
|
||
easily allocated to a team and is more difficult to develop in
|
||
parallel. 'Bottom up' strategy allows early debugging of basic
|
||
functions before they are obscured by higher level manipulations.
|
||
|
||
There is a history of 'Bottom up' programming in computer
|
||
control applications especially with the FORTH computer language.
|
||
FORTH is a bottom up language where you MUST enter basic functions
|
||
first and these are compiled as entered. When higher level
|
||
functions are entered, their calls to the lower level functions are
|
||
converted to pointers to the already compiled block of code. As the
|
||
code is developed the higher level functions are appended to the end
|
||
of the code until the main program module is entered. The code
|
||
executes very fast like a compiled language but is entered as an
|
||
interpreted language. There are in fact even FORTH processors that
|
||
operate in the FORTH language instead of assembly language and
|
||
execute computation and control programs very quickly. At the FORTH
|
||
prompt, the user can type a function call with its arguments and
|
||
test the operation of that function.
|
||
|
||
|
||
|
||
Recommended Methods
|
||
-------------------
|
||
|
||
It is recommended that students use a 'Top Down' strategy when
|
||
planning the data structures and general program flow in the form of
|
||
a block diagram. From this diagram, the range of basic functions
|
||
required can be listed. The student should then set about
|
||
programming the software in a bottom up fashion, testing each
|
||
subroutine as it is coded. Strong use of subroutines should be made
|
||
to reduce the length of the code. The reason for this is not that
|
||
it is likely that a student will run out of memory with a maximum of
|
||
16K of program space on a project board. The reason is, the shorter
|
||
a program is, the less there is to debug. The more a subroutine is
|
||
used, the more is gained by proving it correct. Once the core of
|
||
basic subroutines are proven, the student can advance to the next
|
||
tier of nesting with confidence in what has been done thus far.
|
||
|
||
The first routines programmed will likely be the basic
|
||
multibyte mathematics subroutines. Since values sometimes cannot be
|
||
expressing in one byte and often these quantities must be added or
|
||
subtracted, there are few software subsystems that will not require
|
||
some math routines. Other routines that are likely to be required
|
||
are a delay loop of a controlled length and basic I/O interface
|
||
routines.
|
||
|
||
After each routine has been written, the student should use
|
||
DR6502 in the software mode to test the response of the routine to
|
||
inputs. For example if the routine adds, then make it add several
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
page 3
|
||
|
||
numbers and check to see if its answers are correct. Check I/O
|
||
routines in software mode first and then if the actual interface
|
||
circuitry is ready and tested, the integration of software and
|
||
interface should be attempted either with a test EPROM or using the
|
||
simulator in hardware mode.
|
||
|
||
Often students will give their code to DR6502 without providing
|
||
any input data. DR6502 executes the instructions, the screen flips
|
||
and the program reaches its end. The mistaken conclusion is that
|
||
the routine or program works. The simulator does not check to see
|
||
if the algorithm of the program conforms to any proper model
|
||
intended by the student. The simulator does not check to see if
|
||
data was processed properly. The only error conditions that the
|
||
simulator will flag and actually stop the execution over fall into
|
||
the category of dumb mistakes. If a program executes and DR6502
|
||
does not complain it just means there are no writes to EPROM, no
|
||
reads from a location that was never set and no reads or writes from
|
||
unallocated memory spaces. It is very important therefore to
|
||
provide input data, check intermediate values and check responses
|
||
and output values before any conclusions can be reached about a
|
||
particular piece of code.
|
||
|
||
After and only after the basic routines are tested, should the
|
||
higher level routines be tested. Because higher level routines may
|
||
not test as wide a range of input values to the lower level routines
|
||
under simulated conditions, it is never wise to test several levels
|
||
of the program at once. This is the most common problem for
|
||
students. Many students will very often write the entire program
|
||
and then test it as a whole, hoping it will all work. Often the
|
||
student will not input as great range of inputs into the program as
|
||
would be in the actual prototype and the tests will appear to
|
||
succeed. Since such full tests are difficult and time consuming to
|
||
stage without the hardware present there will be little motivation
|
||
to go on and do more. Toward the end of the term, students are
|
||
sometimes afraid to test their software more extensively in case
|
||
they do find a terrible bug that would take longer to fix then the
|
||
time available. Unfortunately, in the final design and testing
|
||
these undiscovered or ignored bugs will crop up leading to some
|
||
tough questions from instructors and group members. Make sure that
|
||
all tests are carefully controlled, testing the performance of one
|
||
routine. Any routines called in the testing of the routine under
|
||
investigation are thoroughly tested before hand.
|
||
|
||
Often students will test a routine and go on to test another.
|
||
When problems are found in the second routine and the reason is not
|
||
apparent, the student may begin to doubt the performance of the
|
||
first routine. At this point it becomes important for there to have
|
||
been notes on the tests performed on each routine. By consulting
|
||
these notes, the student will be sure whether the routine is
|
||
functioning or if some aspect of it being used that was in fact
|
||
never tested. To help in this matter, the DR6502 program outputs a
|
||
history file providing a record of all work done during a simulator
|
||
session. The log file is called DR_6502.HST. Though notes in a lab
|
||
book are still necessary, after a simulator session, the student may
|
||
wish to rename the history file to a name indicative of the test
|
||
performed and copy it onto a floppy or into a a different directory
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
page 4
|
||
|
||
where it can be consulted if necessary. In discussions with
|
||
instructors or group members, these records are good sources of
|
||
information on what problems were encountered and solved and how
|
||
they were solved.
|
||
|
||
In summary, the program should be planned from the highest
|
||
logic downward and then coded from the lowest level upward. Well
|
||
designed tests should be performed on each routine as it is
|
||
completed. Tests should provide a wide range of input values to the
|
||
routine, monitor intermediate values responses and outputs. It is
|
||
wise to keep extensive records of the tests performed.
|
||
|
||
|
||
Using DR 6502 to Test Software
|
||
------------------------------
|
||
|
||
DR 6502 is a tool that is versatile but has no options like
|
||
'test everything and see if it works.' To prepare for the software
|
||
testing stage of the design, the user of DR 6502 should read the
|
||
DR6502.DOC file and try hacking up and running the example program.
|
||
Performing a test on the software will also require a firm idea of
|
||
what the goals of the test are to be. Once this has been
|
||
established, the steps in a test should be:
|
||
|
||
1) Prepare the subroutine or segment of the program to be tested
|
||
(including the reset vector and a start up routine initializing the
|
||
stack), assemble with TASM and create a symbol file with DRSYM.EXE.
|
||
|
||
2) Start the simulator, provide all requested information and
|
||
proceed to the 'stopped' status screen.
|
||
|
||
3) Prepare any input data required for the program's execution.
|
||
|
||
4) Set any output options that are necessary to monitor the progress
|
||
of the program.
|
||
|
||
5) Begin execution of the program, monitoring its progress.
|
||
|
||
6) Upon completion of the test, examine the output data and save it
|
||
to disk if necessary.
|
||
|
||
7) Modify the program and repeat or exit the simulator and copy the
|
||
history file to your records disk.
|
||
|
||
In the case of testing a 2 byte addition subroutine the user
|
||
may do the following. As above, the program is prepared, assembled,
|
||
the symbol file is created and the simulator is started. The
|
||
specification of this routine includes that, as input variables, it
|
||
takes one 16 bit number passed into the subroutine call by the
|
||
values of the .X and .Y registers representing the high and low
|
||
bytes respectively. The other 16 bit number is present in memory
|
||
locations $00 and $01 as low byte, high byte respectively. The
|
||
routine is designed for doing a 'running total' type addition where
|
||
the answer is stored back into the memory location $00 and $01
|
||
before the routine executes an RTS. The output variables of this
|
||
subroutine are a copy of the contents of $00 and $01 in the .Y and
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
page 5
|
||
|
||
.X registers respectively, the answer. Also returned by this
|
||
routine is the value of the carry flag: set if there was an
|
||
overflow, clear otherwise. The overflow flag was not used because
|
||
the programmer is using it as a quick input port (with the SO pin of
|
||
the processor) in another routine.
|
||
The user selects the 'p' option to poke initial values into
|
||
the $00 and $01 memory locations, and inputs '$00 00' to set both
|
||
locations to zero. The user then selects the 'r' option to modify
|
||
the .X and .Y registers and sets each to zero as well. The first
|
||
test will be to see if the routine can add zero and zero and get
|
||
zero. This is essentially testing to see if any obvious errors
|
||
exist in the way the routine handles the carry flag. The user then
|
||
selects the output options: 'H' to select full history mode, 'm' to
|
||
monitor the address $00 as a two byte variable and 'b' to set a
|
||
break point after the addition of the least significant bytes of the
|
||
arguments. The user then executes the subroutine by pressing the
|
||
's' key for each program step or 'g' for continuous execution. The
|
||
user carefully observes the screen outputs and when the RTS is
|
||
reached if the code found 0+0=0 then another test would be
|
||
prepared. When the testing of this routine is complete the user
|
||
exits the simulator and enters the DOS command: 'copy dr_6502.hst
|
||
a:\tests\2byteadd.rec' to copy the history file into the test
|
||
records directory.
|
||
|
||
Testing I/O Routines
|
||
--------------------
|
||
In the above example, the subroutine processes input data and
|
||
produces output data all internal to the 6502 address space. In
|
||
such cases DR 6502 is sufficient, in its software form, for complete
|
||
debugging of routines. In the case of I/O subroutines, there is
|
||
information that originates outside the 6502 address space involved
|
||
in the manipulation. The problem can be divided into to classes of
|
||
subroutines: those that include hardware input and those that do
|
||
not.
|
||
If the subroutine is an output subroutine only, with monitoring
|
||
of the appropriate output port address, a software simulation with
|
||
DR6502 would be sufficient to check the algorithm. To check to see
|
||
if the routine would 'work' in the global sense, IE be sufficient to
|
||
drive the external device in the manner required, then the project
|
||
hardware must be brought into the loop, either through the hardware
|
||
simulator or by burning an EPROM and trying it on the target system.
|
||
If the subroutine contains hardware input then the simulation
|
||
becomes more difficult. Naturally, working in hardware simulation
|
||
mode, with the target system present or with an EPROM on the
|
||
completed target would test input subroutines. If the user would
|
||
like to test the routine with the simulator in software mode, the
|
||
task is more complex. The user must use the 'b' break point option
|
||
to halt the simulation and do inputs manually. The user may halt
|
||
the execution before the input read and put the data in the input
|
||
port with the 'p' poke memory location option. The user may wish to
|
||
use the 'b' break point option and set it to the port location.
|
||
When the simulator executes and reads the port, the simulation would
|
||
stop. The user could then put the NEXT value on the port. This
|
||
method is superior especially when the subroutine next reads the
|
||
port at several points in the code depending on the last value read
|
||
in.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
page 6
|
||
|
||
Hardware testing is essential to development of the software.
|
||
For example the control of a stepper motor requires a certain series
|
||
of bits or combinations to be sent to the output port. The
|
||
programmer may easily program these combinations and make sure they
|
||
are sent to the port in the correct order but the system may still
|
||
not work as a whole. The programmer may have neglected or chosen
|
||
the time constants, in this case the waiting periods before sending
|
||
the next bit code, incorrectly. A few tests with a function
|
||
generator and a few logic circuits may give the programmer an idea
|
||
of the time delays between sending each impulse. Unfortunately,
|
||
using a function generator is not always fool-proof either. The
|
||
maximum frequency, as read on the generator, that the motor will run
|
||
at and the frequency it will start at are two different values. An
|
||
acceleration routine would be required that starts the motor with
|
||
time constants corresponding to the lower frequency and slowly
|
||
shifts them to the values required for the higher frequency. The
|
||
motor would then appear to start and them accelerate.
|
||
Unfortunately, this is often not adequate either. Once the motor
|
||
has been mounted in its final configuration and attached to the
|
||
final mechanism with all the masses required to be moving, the
|
||
torque required from the motor and the momentum changes these
|
||
constants. Usually the motor must be run more slowly however in the
|
||
case of a high angular momentum and a small running torque like a
|
||
flywheel, the maximum frequency of the motor may actually increase.
|
||
Factors like mass and inertia that lead to time constants. Friction
|
||
alters those time constants and is found in all practical systems.
|
||
Such factors must be considered in the design of software and only
|
||
testing on the physical hardware can prove that the software is
|
||
adequate.
|
||
Unfortunately, the hardware simulator does execute instructions
|
||
as quickly as an actual 6502. As a result, the hardware simulator
|
||
is not well suited to determining time constants. Testing on the
|
||
actually hardware with a test EPROM would be required in this case.
|
||
For additional suggestions on how to determine time constants with a
|
||
test EPROM see file 'TRICKS65.DOC' Section 3) 'The Reburnable EPROM'
|
||
particularly the 'Vector Jump Method'.
|
||
|
||
|
||
|
||
More Advanced Testing
|
||
---------------------
|
||
After the basic subroutines have been tested, the student will
|
||
then move on to testing the more advanced routines that call the
|
||
lower level subroutines. After a while the purpose of the routines
|
||
moves away from the basic manipulation of data and on to the higher
|
||
level logic of which routines are called in what order to perform a
|
||
task. There are user selectable modes of operation where the
|
||
simulator does not produce as much extraneous output. The methods
|
||
involve the output to the screen and the history file records.
|
||
The history file can be set in one of three modes of output to
|
||
limit the quantity of unnecessary output not of interest to the
|
||
user. The total output mode, outputs instructions, arguments,
|
||
register values and monitored address values. When the flow of the
|
||
code is important but the register values are superfluous
|
||
information, there is a second mode (default) where the history file
|
||
can be made to contain only the instructions and arguments. In
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
page 7
|
||
|
||
addition, if only the call structure of the program is important
|
||
then the history file can be set to more brief output mode where
|
||
only program steps involving the JSR, RTS, IRQ, NMI, BRK and RTI
|
||
instructions are recorded.
|
||
Consider the following call tree typical of a mobile reactive
|
||
robotics system:
|
||
|
||
|
||
Main
|
||
|
|
||
-------------------------------------
|
||
/ | \
|
||
Sense Record React
|
||
| | |
|
||
|
|
||
|
|
||
----------------------------
|
||
/ \
|
||
Compute Execute
|
||
| |
|
||
|
|
||
|
|
||
-----------------------------------------------------------
|
||
/ | | | | \
|
||
Add Subtract Multiply Divide Square Root Arctan
|
||
|
||
|
||
Let it be assumed that the code has been designed from the top
|
||
down and is being programmed and tested from the bottom up (though
|
||
this is not necessary). After the basic math routines have been
|
||
programmed and tested and the 'Compute' algorithm has been written,
|
||
the user would like to next test the 'Compute' algorithm. When the
|
||
simulator executes, it would normally display all the program steps
|
||
(full output mode), none of the steps (silent mode) or none of the
|
||
steps up until a break point. If the user is confident of the math
|
||
routines and is really only interested in the behaviour of the
|
||
'Compute' subroutine then a mode that limits the screen output based
|
||
on the position of the execution in the call tree is required. Just
|
||
such a mode is available in the simulator as the subroutine trace
|
||
option. In the above example the user would execute the trace
|
||
option at any time that execution was in the 'Compute' subroutine.
|
||
From that point on, none of the steps of the math routines would be
|
||
displayed nor would any steps of subroutines called by 'Execute' be
|
||
displayed. If the entire program were present and execution
|
||
continued to other areas of the calling tree, all subroutines at or
|
||
above the level of 'Compute' would be displayed. Since limiting
|
||
screen output also limits history file output, the two methods above
|
||
can be used in combination. When this option is selected, only
|
||
program steps at the 'traced' level of nesting or higher are
|
||
displayed and hence are eligible to be recorded in the history file
|
||
depending on its output mode. The user may chose at any time to end
|
||
the trace option.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
page 8
|
||
|
||
Recommendations for Testing
|
||
---------------------------
|
||
|
||
When testing the subroutines of the code, make sure the purpose
|
||
of the test is clearly laid out, the progress of the execution well
|
||
monitored and the results of the test recorded in appropriate
|
||
detail. If the simulator is used, use all options available to
|
||
monitor and record the progress of the code. If the code is run on
|
||
an EPROM, make sure that adequate output is given to whatever output
|
||
device is available to follow the execution of the program. Use LCD
|
||
displays whenever possible, connect LEDs to any unused output bits
|
||
and program the code to give signals to indicate the course of
|
||
important decisions.
|
||
|