GENWiki

Premier IT Outsourcing and Support Services within the UK

User Tools

Site Tools


archive:programming:tricks65
    ====================================================================
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

  1. ——————————————————-

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

  1. ——————————————————

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:

  1. ——-

;

    ;   We wish to jump to $E080
    ;
	  LDA #$E0
	  PHA
	  LDA #$7F
	  PHA
	  RTS

2) The Easy String Input

  1. ———————–

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

  1. ———————-

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:

  1. ————

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

  1. —————–

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



/home/gen.uk/domains/wiki.gen.uk/public_html/data/pages/archive/programming/tricks65.txt · Last modified: 2000/12/16 02:37 by 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki