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.
|
|
|