💾 Archived View for spam.works › mirrors › textfiles › programming › tricks65.txt captured on 2023-06-16 at 20:12:37.

View Raw

More Information

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

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

      Supplementary Notes                                   By: M.J.Malone

      			      6502 Tricks
			      ===========

      Did you know...
      ---------------

	  ...   that in the Atlanta Airport, facilities like the automatic
      doors are all controlled by a 6502 based  VIC  20  computer  system?
      The  6502 (or any 'older' CPU) is capable of executing about 300,000
      instructions per second.  If it requires only  two  instructions  to
      check  the  door  sensor  and another two to open a door then 30,000
      doors could be either opened or closed  in  one  second  by  such  a
      system.   More  realistically,  the  decision and command to open or
      shut 1000 doors could made 30 times per second or a maximum of  .033
      seconds  delay  between  a  person  activating a sensor and the door
      opening.  Systems that interact with people  seldom  have  to  react
      that fast.
 
	  ...   that  the  6502  instruction  set,  though limited, is not
      unlike  the  instruction  sets  of  the   RISC   systems   used   in
      workstations?   The  simple  design  of  the  6502 restricts it to a
      simple assembly language.  Each instruction however, is executed  in
      only  a  few  machine  cycles.   If  reproduced  today in HCMOS, the
      current microprocessor logic technology, the 6502 architecture would
      be capable of 20-30 MIPS  in  a  non-parallel  system  architecture.
      Special  versions  of  the 6502 have were used in a parallel-network
      machine that approached the speed of the supercomputers of the day.
 
	  ...   that  the  terminator  (CSM  -  101)  used  6502  assembly
      language?  In the scene at the motel, when the  terminator  is  just
      arriving,   shortly   after  seeing  the  barking  dog  through  the
      terminator's eyes, a subroutine is displayed on the screen  in  6502
      assembly language.
 
 
 
      0) Read the Program Counter: JSR and PulL(A) the Address
      --------------------------------------------------------
 
	   When the 6502 executes a JSR to a subroutine the return address
      is  pushed onto the stack.  When the RTS is executed, the address is
      pulled off of the stack and put back into the  program  counter  and
      the  program  continues  after the JSR instruction.  Sometimes in an
      assembled code it is convenient for  a  program  to  know  where  in
      memory it resides.  It is possible by executing a JSR instruction to
      the  next  instruction  to  discover  the  current  program  counter
      address.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
                                                            page 2 
 
      Example:
      ;
      ;  What is my address?
		  jsr Next_Inst
      Next_Inst   pla
		  clc
		  adc #$01
		  sta PCL
		  pla
		  adc #$00
		  sta PCH
      ;
      ; The address of Next_Inst is in PCH, PCL
      ;
 
 
      1) Set the Program Counter: PusH(A) the Address and RTS
      -------------------------------------------------------
 
	   When the 6502 executes a JSR to a subroutine the return address
      is pushed onto the stack.  When the RTS is executed, the address  is
      pulled  off  of  the stack and put back into the program counter and
      the program continues after the JSR instruction.  If  you  know  the
      address  of a point you wish to JMP to in the program then just push
      the address into the stack and execute an RTS command.   Two  things
      must  be  remembered.  The address pushed into the stack must be one
      less than the address you actually want to go to.  The high byte  of
      the  address  must be pushed into the stack then the low byte.  This
      trick is very useful in JMPing to a CALCULATED address simulating an
      ON-GOTO statement or a SWITCH/CASE statement.
 
      Example:
      --------
      ;
      ;   We wish to jump to $E080
      ;
		  LDA #$E0
		  PHA
		  LDA #$7F
		  PHA
		  RTS
 
 
      2) The Easy String Input
      ------------------------
	   When a JSR is called, the return address or the address of  the
      last  byte of the JSR operand is pushed into the stack.  This return
      address as in 0) is available to the  subroutine  that  was  called.
      This  is a convenient method of passing the address of a string to a
      routine for printing strings.  The following is a coding example for
      printing a string.
 
      ;
      ;  Print the Hello Message
      ;
		  jsr Print_Str
      .TEXT "Good morning. Ready...*"
 
      ;
 
 
 
 
 
                                                            page 3 
 
      ;  The Print String Subroutine
      ;
      Print_Str   pla        ;  Pull the RTS address off of the stack
		  clc
		  adc #$01   ;  Add one to the low byte of the address
		  sta ptr
		  pla
		  adc #$00   ;  Add zero to the high byte (add the carry)
		  sta ptr+1
      ; This is programmed for the 6502
      ; to simulate an indirect read
		  ldy #$00
      Next_Char   lda (ptr),Y
      ; For the 65C02
      ;Next_Char  lda (ptr)
		  cmp #'*'          ; '*' is the end of string character
		  beq End_String
      ;
      ; A character of the string is in the .ACC.  I assume you have a
      ; routine that drives your display that accepts ASC II codes
      ; called 'Print_Char'.
		  jsr Print_Char
      ;
      ; Increment the 'ptr' into the string ==> Do the next character
		  inc ptr
		  bne Next_Char
		  inc ptr+1
		  jmp Next_Char
      ;
      ; Put the address of the '*' back into the stack
      ; pretending it is the last byte of a JSR statement
      ; causing the processor to return to execution the
      ; the first byte after the '*'.
      ;
      End_String  lda ptr+1
		  pha
		  lda ptr
		  pha
		  rts
      ;
      ;
 
 
      3) The Reburnable EPROM
      -----------------------
 
	   Generalized induction states: if the first can be proven and  a
      (n+1)  can  be  proven  then  the  proposition is true.  In the case
      proposed, that of a reburnable EPROM, the first is the first version
      of the software programmed into the EPROM, there is no problem  with
      this  process.   To  have  later versions of the program in the same
      EPROM we must have an (n+1) or a way to link from one version to the
      next.
	   Erased EPROMs or unprogrammed areas  of  an  EPROM  are  filled
      with  $FF.   $FF  is  a valid data value that can be read, compared,
      acted upon.  As an opcode, $FF  is  the  very  rare  Rockwell  65C02
 
 
 
 
 
 
 
 
                                                            page 4 
 
      command BBS7 zpage,rel, a conditional branch that tests the if bit 7
      of  a  zero  page  address  is  set.  If we put a restriction on all
      versions of the software that none may begin with  this  instruction
      then we may proceed.
	   If  one  version  of  a program were executing in the EPROM, it
      could look into an area of memory reserved for a future version.  If
      the value of the memory  location  was  $FF,  then  the  superseding
      version  is  not  there yet and the program would continue executing
      since it is the most current version.  If superseding  version  were
      there,  the program currently executing would JMP to the new version
      since the version currently executing is obsolete.  The next version
      of the software would first test  if  yet  another  version  of  the
      software  was  in the EPROM before it went on to execute.  The (n+1)
      link between one version and the next has been established  provided
      each version first check to see if the (n+1) version is there before
      executing.
	   The  limit  of  the number of versions is the size of the EPROM
      divided by the size of a version of the software.  If  the  software
      (or  test  element)  is small then many versions are possible in one
      EPROM.  Hence one EPROM can be used for  several  revisions  of  the
      code.   Each  time  the  EPROM  is  reused it saves 20-25 minutes of
      erasing  time.   This  erasing  delay  is   VERY   irritating   when
      experimenting  with  time  delays  and  device dependent subroutines
      where many revisions may be  necessary  to  achieve  a  satisfactory
      final result.
	   There are several approaches to  reusing  EPROMS.   The  method
      hinted  above  is  a  chain  link  method where only the most recent
      version will run.  The advantages are that no limited is  preimposed
      on  the  number  of  versions and each version can be of a different
      length.  An alternative would be a preprogrammed  vector/jump  table
      (in  the  first  version  of the code) that jumps to only a specific
      number of preset addresses depending on whether code is  present  at
      those addresses or on some auxiliary condition.  By this method only
      a  preset  number of versions as determined by the number of vectors
      put in the table and a preset (quantum) size  for  each  version  is
      allowed.   The  advantage  is  of  course,  the user can decide just
      before resetting the processor which version of the code to run.  By
      setting switches on a VIA port for instance, these  user  determined
      values  could be read internally to select the jump point.  This may
      be very useful in comparing the operation of two  algorithms  or  in
      binary  searches  where  'which  is  better'  decisions are made and
      constants modified in smaller increments.
	   There  are  some  mechanical  details  that must be worked out.
      Your EPROM must contain the reset vector on the  first  version  and
      every  version  after.  Usually the first version of the code is put
      at the beginning of the EPROM and the reset vectors go at  the  end.
      To  avoid programming the spaces in between on the first version (to
      leave them blank), there are two alternatives.   The  first  version
      and  the  reset  vector could be programmed in two separate writings
      where the programming zone is carefully selected by the  user.   The
      second  alternative is to fill unused spaces in EPROM space with $FF
      in the binary file.  When the $FF are  programmed  into  the  EPROM,
      those  locations will be left blank.  This is possible because there
      is no difference between a programmed $FF and a blank EPROM location
      that reads $FF.  On  later  versions  of  the  code  the  user  must
      carefully  select  the  target zone of the EPROM to program only the
 
 
 
 
 
 
 
 
                                                            page 5 
 
      locations required for the version being entered.
	   There is no limit to the permutations on this idea.  If you try
      one of the methods suggested and think of a better one yourself then
      great, use it.  The chain and the jump table methods will  be  coded
      in  example code fragments.  Note that throughout the examples {UPV}
      stands for the user program version to be tested.
 
      Chain Method:
      -------------
      Assemble the Code with the following command line:
 
      tasm -65 -b -fff  reuse.asm reuse.bin reuse.lst
 
	  -fff stands for 'f' for fill the rest of memory with  'ff'  $FF.
      The  will  result in areas not specifically programmed to default to
      $FF which will not program the memory location.
 
      2) Determine the length of the code.   Determine  the  value  of  an
      address  that will be beyond the current version of the program.  We
      will call this address 'Beyond0'.  For the first  version  code  the
      following:
 
      .ORG $E000
		  SEI                  ; Initialize the Stack
		  LDX #$FF             ; You have to do it every time
		  TXS
      ;
		  LDX #$00             ; Wait for extra RST bounces
		  LDY #$00
      InitDelay   DEX
		  BNE InitDelay
		  DEY
		  BNE InitDelay
      ;
		  LDA Beyond0
		  CMP #$FF
		  BEQ ThisVersion
		  JMP Beyond0
 
      ThisVersion   {UPV}
      	      .
      	      .
      	      .
 
      Beyond0 = $E200     ; An address after the current version
      .ORG $FFFC
      .WORD $E000
      .END
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
                                                            page 6 
 
      On  subsequent  versions  code  that  start at address 'Beyond{n-1}'
      (referred to as beyond{n} in the previous version)  code  this:
 
      .ORG $E000
      .BYTE $FF
      ;
      .ORG Beyond{n-1}
		  LDA Beyond{n}
		  CMP #$FF
		  BEQ ThisVersion
		  JMP Beyond{n}
 
      ThisVersion    {UPV}
      		.
      		.
      		.
      Beyond{n} = $????  ; Something Beyond version n
      .ORG $FFFC
      .WORD $E000
      .END
 
      This  may look complex but look at that last fragment.  All that has
      to be changed to do  a  new  version  is  Beyond{n-1}=Beyond{n}  and
      assign  the  new value for Beyond{n}.  Once you get the hang of this
      it will take five seconds in a text editor as opposed to 25  minutes
      in the EPROM eraser.
	   The mechanical details of this are important.   The  .BYTE  $FF
      and  the  reset  vector  .ORG  $FFFC  \ .WORD $E000 are put in later
      versions of the code even though they will not be put in the  EPROM.
      The  .BYTE  $FF  is just a place holder to let TASM know that we are
      producing an object file starting at $E000.  The RST vector does not
      have to be burned into the EPROM on subsequent runs because  the  it
      is  already in the EPROM from the first burn.  The .BYTE $FF and the
      RST vector mark the beginning and end of the EPROM space and  forced
      TASM  to  produce  an binary file that is 8K long.  This binary file
      can then be loaded into the EPROM burner at buffer address 0000  and
      except for the $E000 offset all the addresses will match.
	   After  the  correct  Beyond  address  is calculated the program
      should be reassembled with the -fff option.
	   On the first version the whole EPROM is programmed.   The  only
      slightly tricky part comes later.  ONLY the part of the program that
      deals  with  new  version  of the code should be burned in after the
      first version is already in.  This requires the user to either use a
      selective copy option in the  EPROM  burner  menu  or  requires  the
      SOURCE and TARGET ZONES to be redefined (different EPROM burners are
      different).   This whole process is sure to take the average ENG SCI
      student less than 25 minutes to figure out  so  it  should  pay  for
      itself the first time that erasing is not necessary.
 
 
      Vector Jump Method
      ------------------
	   Assemble the code as before.  Determine how long a version will
      be  at its maximum and allow a margin of safety.  Allocate a spare 3
      or 4 bits on a VIA port to communicate to the CPU which  version  to
 
 
 
 
 
 
 
 
 
                                                            page 7 
 
      run.  Code this for the beginning of the EPROM:
 
      .ORG $E000
      ;
      PORT = $A001
      ;
		  SEI                  ; Initialize the Stack
		  LDX #$FF             ; You have to do it every time
		  TXS
      ;
		  LDX #$00             ; Wait for extra RST bounces
		  LDY #$00
      InitDelay   DEX
		  BNE InitDelay
		  DEY
		  BNE InitDelay
      ;
		  LDA PORT
		  AND #$07
		  TAX
		  BNE G0
		  JMP Version0
      G0          DEX
		  BNE G1
		  JMP $E400
      G1          DEX
		  BNE G2
		  JMP $E800
      G2          DEX
		  BNE G3
		  JMP $EC00
      G3          DEX
		  BNE G4
		  JMP $F000
      G4          DEX
		  BNE G5
		  JMP $F400
      G5          DEX
		  BNE G6
		  JMP $F800
      G6          JMP $FC00
      ;
      Version0      {UPV}
      ;
      .ORG $FFFC
      .WORD $E000
      .END
 
      On subsequent versions:
 
      .ORG $E000
      .BYTE $FF
      ;
      .ORG $F000    ;  Version 4
      ;
      	      {UPV4}
      ;
      .ORG $FFFC
      .WORD $E000
      .END
 
 
 
 
                                                            page 8 
 
      The  jump  vector  table  can be implemented more easily with a PUSH
      ADDRESS and RTS TRICK.  (see trick #1 in this file)
 
      .ORG $E000
      ;
      PORT = $A001
      ;
		  SEI                  ; Initialize the Stack
		  LDX #$FF             ; You have to do it every time
		  TXS
      ;
		  LDX #$00             ; Wait for extra RST bounces
		  LDY #$00
      InitDelay   DEX
		  BNE InitDelay
		  DEY
		  BNE InitDelay
      ;
		  LDA PORT
		  AND #$07
		  BEQ Version0
		  ASL A
		  ASL A
		  ASL A
		  ASL A
		  ASL A
		  ASL A
		  CLC
		  ADC #$DF
		  PHA
		  LDA #$FF
		  PHA
		  RTS
      ;
      Version0      {UPV0}
      ;
      .ORG $FFFC
      .WORD $E000
      .END