💾 Archived View for gemini.spam.works › mirrors › textfiles › programming › desdebug.txt captured on 2020-10-31 at 14:47:38.

View Raw

More Information

-=-=-=-=-=-=-

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