Starting to Program in 6502 Assembly Code

      ====================================================================
   DR  6502    AER 201S Engineering Design 6502 Execution Simulator
      ====================================================================

      Supplementary Notes                                   By: M.J.Malone

              Starting to Program in 6502 Assembly Code
              =========================================
      
      Quotes of Murphy's Kin:
      
    1) It is impossible to make anything fool-proof because fools are
        so ingenious.

    2)  If  you  explain  something so thoroughly and clearly so that
        everyone will understand, someone will not understand.

      
      Introduction to the 6502
      ------------------------
      
      Accumulator and Assembler Basics
      
      The 6502 has several 'registers'.  Registers are special memory
      locations that are  internal  to  the  processor  and  are  directly
      involved  in processor instruction codes.  The accumulator (.ACC) is
      one such register.  The code fragment that would transfer data  from
      one  memory  location  (A_loc) to another (B_loc) would be coded as:
      (Note that the numbers $0400 and $0800 are hexidecimal numbers which
      do have decimal equivalents.  The conversion  between  hex,  decimal
      and binary will be explained later.)
 
      ;
      A_loc = $0400
      B_loc = $0800
      ;
        LDA A_loc
        STA B_loc
      ;
 
      Where  LDA  is the mnemonic for LoaD Accumulator and STA is the
      mnemonic  for  STore  Accumulator.   In   this   example   the   two
      assignments  'A_loc='  and  'B_loc=' give values to the labels A_loc
      and B_loc.  Note that A_loc and B_loc are NOT variables  but  labels
      or constants used to replace numerical values and make the code more
      readable.   The  locations described by A_loc and B_loc will contain
      data important to the running of the program,  the  variables.   Now
      A_loc  and  B_loc  are  not  very  descriptive  but  'Abs_Value' and
      'Buffer_ptr' are also legal labels.  The assembler  sees  the  above
      code fragment as:
 
      ;
        LDA $0400
        STA $0800
      ;
 
      with  the labels substituted into the code.  This fragment loads the
      value from address $0400 into the accumulator and  then  stores  the
      value into memory address $0800.
 
 
 
 
 
 
 
 
                                                            page 2 
 
      You may think of labels 'Buffer_ptr' as a variable as in a high
      level  language when you go 'LDA Buffer_ptr' to get the value of the
      variable into the accumulator.  Remember  that  the  assembler  sees
      Buffer_ptr  as  an  address  of  a  location  in  memory,  it is the
      programmer who decides that that location has a special  meaning  or
      purpose and gives it a meaningful name.
 
 
      The .X and .Y Registers and Indexing
 
      The  .X  and  .Y  registers  are  similar  to  the .ACC in most
      respects, data can be loaded into them and stored into  memory  from
      them.   The above example could as easily be programmed with LDX and
      STX as it was with LDA and STA as follows:
 
      ;
      A_loc = $0400
      B_loc = $0800
      ;
        LDX A_loc
        STX B_loc
      ;
 
      The same code could also be written  for  the  .Y  but  there  is  a
      special  purpose  reserved  for  the  .X  and  .Y registers.  In the
      previous section we saw that labels  were  used  to  name  important
      memory  locations  and  simulate  the  function of variables for the
      programmer.  The assignment 'b=a' in a higher level language may  be
      coded  using  the example above.  If the programmer wanted to use an
      array and say assign 'b~[3]=a' it may be done as follows:
 
      ;
      A_loc         = $0400
      B_array_start = $0800
      ;
        LDA A_loc
        LDX #3
        STA B_array_start,X
      ;
 
      As before, the value of  memory  location  'A_loc'  is  loaded  into
      memory.   The  value  of  the  array  index '3' is loaded into the X
      register.  The statement 'STA B_array_start,X' takes  the  value  in
      the memory and stores it into the location (B_array_start+.X).  Here
      B_array_start  is the first address for the array and .X acts as the
      offset INDEX into the memory space that  follows.   This  is  called
      simple  indexing  and  can  be used to store and recall data that is
      organized into tables.
 
 
      The 6502 as an 8 bit Processor
 
      For programmers accustomed to high level  languages  often  the
      assembly  language  of  processors  seems very limiting.  So far the
      implementation of higher level language  assignment  statements  has
      seemed  to be as simple as a few substitutions, a few LDA and STA to
 
 
 
 
 
 
 
 
                                                            page 3 
 
      get the job done.  Unfortunately this  is  about  the  easiest  high
      level language concept to transfer to assembly and then only in very
      restricted cases.
      The 6502 is an 8 bit processor and all memory fetches (LDA etc)
      read  values from 8 bit memory locations.  All numbers stored in the
      accumulator are in the range of 0-255 or $00-$FF.   The  .X  and  .Y
      registers  are  also  8  bit as is the .SP the stack pointer for the
      6502.  This limits  the  stack  to  256  memory  locations  (located
      between  addresses  $0100-$01FF).   The  program counter is a 16 bit
      number which varies between $0000 and $FFFF.  The address  space  is
      defined  as  the range of memory locations that can be pointed to by
      the program counter.  In the case of the 6502, 16 bits, $0000-$FFFF,
      0-65535 represents an address space of 64K, where 1K is  defined  as
      1024 for binary applications.
 
      Number Conversions
      The  conversions  between hex and decimal representation can be
      done however students should be aware that since the 6502  is  an  8
      bit  machine,  everything  is  much  more  convenient  in  hex.  For
      instance the address $8000 is immediately recognizable as the  first
      address  in the upper half of memory (because it begins $80), at the
      beginning of a memory page (because it ends in 00).  The  equivalent
      number  in decimal 32768, unless memorized for what it is, cannot be
      recognized as easily.  Similarly it is necessary when  dealing  with
      I/O  ports to control individual bits of a memory location.  In this
      case  the  binary  representation   of   %1010000   is   immediately
      recognizable  as bits 7 and 5 of the byte set whereas the meaning of
      the equivalent decimal number 160 is not nearly so clear.
      It  is  advisable  to  use  hexidecimal  numbers for addresses,
      binary numbers where  bit  on/off  states  are  important.   Decimal
      numbers  should  be  used  only  when 'magic' numbers are such as 26
      letters in the alphabet, 24 hours in a day, 30 days in a  month  etc
      that  we  are accustomed to seeing as decimal are needed.  Note that
      numbers 9 or smaller are the same in hex as decimal so there  is  no
      problem for small constants.
      The conversion between hex, binary and decimal will most likely
      be  done  on  students  hand  calculators however recalling what you
      learned in grade 3: There are 10 symbols for digits in  the  decimal
      number  system  0-9.   All  numbers are stated with these 10 symbols
      (note the number 10 is expressed in decimal).  Numbers larger than 9
      are stated using ten's and unit's digits:
 
        98 = 9*10 + 8
 
      or more generally:
 
        34256 = 3*10^4 + 4*10^3 + 2*10^2 + 5*10^1 + 6*10^0
 
      Hexidecimal numbers are represented with 16 symbols  ($10  =  16  in
      hex),  the  symbols  0-9  and the letters A-F.  As units digits, 0-9
      correspond to the decimal numbers 0-9, ie: 5 =  $5  etc.   As  units
      digits,  A-F correspond to the decimal numbers 10-15.  A hexidecimal
      number can be expressed:
 
        $7A3F = $7*$10^3 + $A*$10^2 + $3*$10^1 + $F*$10^0
 
 
 
 
 
 
 
 
 
                                                            page 4 
 
 
      So far this is parallel to the representation of a decimal number as
      above.  To  perform  a  conversion  you  would  simply  convert  the
      hexidecimal numbers in the expansion to decimal.
 
   $7A3F = 7*16^3 + 10*16^2 + 3*16^1 + 15*16^0  =   31295 decimal
 
      A  similar  operation  can be done in binary but once the meaning of
      units, ten's and hundreds digits is understood then  the  expansions
      of  binary  numbers  are  as  easily  calculated  as for hexidecimal
      numbers.
      Often conversions between hexidecimal and binary or the reverse
      are necessary.  Similar expansions as above could be  performed  but
      there is a short cut.  Noting that 2^4 = 16 and 2 is the base of the
      binary  system  and  16 is base of the hexidecimal system, groups of
      four binary digits must somehow correspond  to  hexidecimal  digits.
      Observing that:
 
              %0000 = $0        %1000 = $8
              %0001 = $1        %1001 = $9
              %0010 = $2        %1010 = $A
              %0011 = $3        %1011 = $B
              %0100 = $4        %1100 = $C
              %0101 = $5        %1101 = $D
              %0110 = $6        %1110 = $E
        %0111 = $7        %1111 = $F
 
      The  conversion  of  multidigit  hexidecimal  numbers   becomes
      straight forward.
 
   $7A3F = $7,A,3,F = %0111,1010,0011,1111 = %0111101000111111
 
      This  is  very  useful when hardware tests have to be done.  If
      the program counter of the 6502 were pointing at address $7A3F  then
      the  binary  representation  would  be  useful for understanding the
      voltage values on the address pins of the  6502  chip.   In  digital
      circuits,  5  volts  corresponds  to  logic  1  or binary 1; 0 volts
      corresponds to logic 0 or binary 0.  The  binary  representation  of
      the  address  %0111101000111111  is  also  the  logic levels for the
      address lines Adr15-Adr0 and hence the voltages on the address lines
      would read (counting from Adr15 to 0): 0V, 5V, 5V, 5V, 5V,  0V,  5V,
      0V,  0V,  0V,  5V,  5V,  5V,  5V,  5V and 5V.  Summary of 6502 Basic
      Structure
 
      By now you  should  be  realizing  that  operation  of  digital
      computers  are  really  not so mysterious at all.  True the internal
      workings of a 6502 are very complex, it is not hard  to  imagine  it
      being  made  up  of  a great number of smaller blocks that interpret
      voltages as  logic  levels  and  perform  logical  operations.   The
      acculumator  is  in fact eight circuits that can each hold a voltage
      of 0 or 5 volts, organized into a unit that is  routed  out  of  the
      processor  to memory by the STA instruction for instance.  Since the
      actual  operations,  when  reduced   to   voltages   and   switching
      transistors  are  very simple, they are very fast.  Even the 6502, a
      relatively slow processor can perform a STA operation in  as  little
      as  3  microseconds  or  perform 333,333 such STA's in one second of
 
 
 
 
 
 
 
 
                                                            page 5 
 
      operation in the slowest 1 MHz implementation.
      We have been introduced to the accumulator and the  .X  and  .Y
      index  registers.   As  mentioned  before  the  system  8  bit stack
      pointer .SP points into an area of memory from $0100-$01FF.  For now
      suffice it to say that the stack is very important to the  operation
      of  the processor and the actual instructions that involve the stack
      will be discussed later.  The program counter is a 16  bit  register
      that  points  to  the  instruction in memory that is currently being
      executed.   The  processor  status  register  which  has  not   been
      discussed  until now, is a group of flags that represent the current
      state of the processor and  the  results  of  the  last  calculation
      performed.
 
 
      Classes of 6502 Instructions
      ----------------------------
 
      The  reader  will  be  introduced  to  several  classes of 6502
      instructions.  Some instructions require arguments and some  do  not
      and this is indicated where appropriate.
 
      Data Transfer Instructions
      There  are  several instructions in the family of data transfer
      instructions but basically they  fall  into  two  categories,  those
      which  move  data  between  memory and registers and those that move
      data between registers.
 
       LDA, LDX, LDY  arg    Load 'arg' into Accumulator, X or Y registers
       STA, STX, STY  arg    Store the Accumulator, X or Y registers to memory 'arg'
       STZ            arg   *Store the value zero into a memory location 'arg'
 
           TAX, TAY           Transfer .A to .X, .A to .Y
           TXA, TYA           Transfer .X to .A, .Y to .A
           TXS, TSX           Transfer .X to .SP, .SP to .X
 
           *65C02 Only
 
      These instructions are very straight forward, they  move  an  8
      bit  value  ($00-$FF)  from  the  one  place  to another.  After the
      instruction, the data is in two places, where it was  and  where  it
      was moved to.
 
 
      Go to Instructions
 
           The go to instructions are as follows:
 
           JMP  dest          Jump, Set the Program Counter = 'dest'
 
           JSR  dest          Jump to a Subroutine, saving the return address
           RTS                Return from subroutine
 
      The  JMP  statement  is  a  pure  'go to' which simply sets the
      program counter to a new address and continues the execution of  the
      code  at  that  point.   The  JSR statement first takes that current
      program counter position and pushes it into the stack and then jumps
 
 
 
 
 
 
 
 
                                                            page 6 
 
      to the new address.  When the RTS instruction is encountered at  the
      end  of the subroutine, the previous program location is pulled from
      the stack and the processor continues after the subroutine call.
 
 
      Compare Instructions
 
      So far we have examined ways of moving data and unconditionally
      changing the path of the program.  We  will  now  look  at  ways  to
      conditionally  change  the path of the program.  In machine language
      this is divided into two operations,  a  comparison  and  a  branch.
      There are three compare statements:
 
           CMP arg        Compare the .A
           CPX arg                    .X
           CPY arg                    .Y  to  'arg'
 
      The  results  of  these  comparisons are recorded in the system
      flags N-negative,  Z-zero  and  C-carry  for  later  use  in  branch
      instructions.
 
      Branch Instructions
     Branch  instructions  test  the  state  of  one of the flags and
      either branch or not.  Branches are relative jumps up to  128  bytes
      forward  or back in the code determined by the offset argument.  The
      branch instructions are:
 
   BNE offset    Branch on result not equal    , result not zero: Z=0
   BEQ offset    Branch on result equal        , result zero    : Z=1
   BMI offset    Branch on result greater      , result <0      : N=1
   BPL offset    Branch on result less or equal, result =>0     : N=0
   BCC offset    Branch on carry clear                          : C=0
   BCS offset    Branch on carry set                            : C=1
   BVC offset    Branch on overflow clear                       : V=0
   BVS offset    Branch on overflow set                         : V=1
   BRA offset   *Branch always
 
       *65C02 only
 
 
      Arithmetic and Logical Instructions
 
      We  have  examined  data  moving  and  comparing  instructions,
      conditional  branches and unconditional changes to the program flow.
      The last class of instructions we will examine  are  the  arithmetic
      and  logical  instructions  where actual computations occur.  All of
      these instructions involve the accumulator as one of the arguments.
 
       ADC arg   Add with carry        .A + 'arg' + C  ==> .A  (C,V,N,Z)
       SBC arg   Subtract with borrow  .A - 'arg' - !C ==> .A  (C,V,N,Z)
       AND arg   Logical bitwise And   .A and 'arg'    ==> .A    (N,Z)
       ORA arg   Logical bitwise Or    .A  or 'arg'    ==> .A    (N,Z)
       EOR arg   Logical bitwise XOr   .A xor 'arg'    ==> .A    (N,Z)
 
      The ADC and SBC use the carry to allow multibyte  additions  to
      be  done using the C flag to carry to or borrow from the next higher
 
 
 
 
 
 
 
 
                                                            page 7 
 
      byte of the operation.  The V flag is the overflow flag.  It is  set
      whenever a computation exceeds $FF or goes under $00.  Note that the
      overflow  flag  can also be set using the SO set overflow pin on the
      6502 allowing an additional input line to the processor.
 
      Instruction Summary
      -------------------
      There are other instructions for the 6502  but  they  are  less
      commonly  used then those introduced above.  It would be most useful
      now to attempt to  use  the  instructions  learned  in  a  few  code
      fragments to see how instructions interrelate.