GENWiki

Premier IT Outsourcing and Support Services within the UK

User Tools

Site Tools


archive:programming:adv_ex65

Table of Contents

    ====================================================================
DR  6502    AER 201S Engineering Design 6502 Execution Simulator
    ====================================================================
    Supplementary Notes                                   By: M.J.Malone
	       Advanced 6502 Assembly Code Examples
	       ====================================
   The remainder of this file will be in a  format  acceptable  to
    TASM  for direct assembly.  Note there may be errors in the code, it
    is not intended that it be cut up and included  in  students  files.
    It   is   meant   only   as  an  example  of  addressing  modes  and
    instructions.  The first example is  a  prime  number  finder.   The
    second  example  is  a set of subroutines to maintain a multitasking
    system.

;

; Advanced Coding Examples for the Students of AER201S ;

; ; .ORG $E000

   SEI             ; INITIALIZING THE STACK POINTER
   LDX #$FF
   TXS

;

   LDX #$00
   LDY #$00

Delay DEX

   BNE Delay
   DEY
   BNE Delay

; ;

; Prime Number Finder ;

; This Prime Number Finder uses the sieve method to find the primes up to 255 ; and then uses those primes to find the primes up to 65535. Note that this ; is of course not THE most efficient way to find primes but it makes a good ; demonstration. ; It would be neat to stack this code up against a casually written/optimized ; compiled C prime number finder on a raging 386. I have a feeling there will ; be less than a factor of ten difference on execution speed. You may be ; surprised just how fast the 6502 is on simple problems. ; Test_num = $00 ; Test Number to Eliminate non-primes Array = $00 ; Base Address for the array of primes ; ;

   lda #$01
   sta $a003
   lda #$01
   sta $a001       ;  Turns on an LED on bit zero of port A of VIA 1
		   ;  to let you know it has started looking for primes

;

page 2

ldx #$01 ; Initialize the array of numbers Init_Loop txa

   sta Array,x
   inx
   bne Init_loop

;

   lda #$02        ; Initialize the Test_num = 2
   sta Test_num
   lda #$04        ; Put the square of 2 in the accumulator
		   ;   as the first non-prime

; ; Start Setting the Multiples of the Test_num to zero Start_num Got_Mult tax

   stz Array,x     ; Set multiples of Test_num to zero since they
   clc             ; are not prime.
   adc Test_num    ; Calculate the next multiple
   bcs Next_num    ; Until the Multiples are outside the array
   jmp Got_Mult

; Next_num inc Test_num ; Go on to the next Test_num

   ldx Test_num
   cpx #$10        ; Until Test_num => sqrt(largest number)
   beq More_Primes
   lda Array,x
   beq Next_num    ; Don't use Test_num if Test_num is not prime
   txa

; Got a valid new Test_num, now find its square because all non-primes ; multiples less than its square are eliminated already

   dex
   clc

Square adc Test_num

   dex
   bne Square

; OK Got the square of Test_num in the accumulator ; lets start checking

   jmp Start_num

; ; More_Primes ; ; Ok now we have all the primes up to 255 in the memory locations $01-$FF ; Lets repack them more neatly into an array with no spaces to make our ; life easier ;

   ldx #$00          ; .X is a pointer into the loose array
   ldy #$01          ; .Y is a pointer into the packed array

Repack inx

   beq Done_packing
   lda Array,x
   beq Repack
   sta Array,y
   iny
   jmp Repack

;

page 3

Prime_Ptr = $F0 ; This is a points into the list of primes greater

		     ;  than $FF and less that $10000

; Poss_Prime = $F2 ; Possible prime Temp = $F4 ; A Temporary Number used to find modulus Shift = $F6 ; Number of Places that .A is shifted TempArg = $F7 ; A temporary number; argument of modulus

; Done_packing

   lda #$00          ; Store a $00 at the end of the array of short
   sta Array,y       ; primes so we know when we have reached the end
   lda #$00
   sta Prime_ptr     ; Set the Prime Pointer (for primes >$FF)
   lda #$02          ; pointing into $0200. The found primes will be
   sta Prime_ptr+1   ; recorded sequentially from there on.

;

   lda #$01          ; Start with $0101 as the first possible prime
   sta Poss_Prime
   sta Poss_Prime+1

; Next_PP ldy #$02 Next_AP lda Array,y

   beq Prime
   jsr Mod
   beq Next_Poss_prime     ; it was a multiple of Array,y
			   ; and therefore not prime
   iny
   jmp Next_AP

; Prime ldx #$00

   lda Poss_prime       ; Store prime away in the array of primes
   sta (Prime_ptr,x)
   inx
   lda Poss_prime+1
   sta (Prime_ptr,x)
   clc
   lda Prime_ptr        ; Increment the pointer in the array of primes
   adc #$02
   sta Prime_ptr
   lda Prime_ptr+1
   adc #$00
   sta Prime_ptr+1

; Next_Poss_prime

   clc                  ; Increment Poss_Prime to look at the next
   lda Poss_Prime       ; number
   adc #$01
   sta Poss_Prime
   lda Poss_Prime+1
   adc #$00
   sta Poss_Prime+1
   bcc Next_PP          ; Carry will be set when we reach $10000

; ; Ends when it has found all the primes up to 65535 ; ;

page 4

lda #$00

   sta $a001       ; Turns off the LED after the code finishes

; DONE JMP DONE ; Endless loop at end to halt execution ; ; ; ; ————————————————————————– ; Find the Modulus Remainder of Poss_Prime and number in A ; ————————————————————————– ; Input Regs: .A Number being divided into the Possible Prime ; Poss_Prime contains the number being tested for primeness ; Output Regs: .A Modulo remainder ; Mod ldx Poss_Prime ; Transfer Poss_Prime to Temp

      stx Temp
      ldx Poss_Prime+1
      stx Temp+1
      ldx #$00            ; Set the bit shifting counter to #$00
      stx Shift

; ; Compare A to the upper byte of Temp ; Compare sec ; Compare to see if the .A is greater than

      cmp Temp+1          ; (equal to) the high byte of Temp
      bcs A_Bigger

; ; If the accumulator is smaller than the upper byte of Temp then shift it ; until it is bigger or it overflows the highest bit ;

      clc
      rol a
      bcc Not_off_end

; ; It has overflowed the highest bit, unroll it by one position ;

      ror a
      sta TempArg
      jmp Start_Mod

; ; Not overflowed yet, go and compare it to Temp+1 again ; Not_off_end inc Shift

      jmp Compare

; ; If the accumulator is bigger and it has been shifted then unshift by one ; bit ; A_Bigger ldx Shift

      cpx #$00
      sta TempArg
      beq Start_Mod
      clc
      ror a
      dec Shift
      sta TempArg

;

page 5

; If the accumulator was smaller than the highest byte of Temp it now ; has been shifted to strip off the high bit at least ; If the accumulator was larger than the highest byte then proceed with the ; regular modulus shift and subtracts ; Start_Mod lda Temp+1

      sec
      cmp TempArg
      bcc Dont_Subt

; ; Subtract as a stage of division ;

      sbc TempArg
      sta Temp+1

; Dont_Subt ; ; We would now like to shift the TempArg relative the Temp ; 1) Shift is greater than zero - accumulator was shifted - unshift it ; 2) Shift Temp - if shift reaches -8 then we are out of Temp and ; what we have left is the modulus –RTS ;

      lda Shift
      bmi Sh_Temp     ; Case 2
      beq Sh_Temp

; Case 1

      clc
      ror TempArg
      dec Shift
      jmp Start_Mod

; Sh_Temp cmp #$f8

      bne Continue
      lda Temp+1       ;  This is the Modulus
      rts

Continue dec Shift

      clc
      rol Temp
      rol Temp+1
      jmp Start_Mod

; .ORG $FFFC .WORD $E000 .END ; ; ; ;

;** ;

; ;

page 6

;

; The Multitasking 6502 - See you 6502 do several things at once ;

; This relies on the assumption that there is a source of IRQ's out there ; that is repetitive and each task is allotted time between each IRQ. ; Process 1 is started automatically by the RESET signal. ; Any process can extend its life for a while (if it is doing something ; important) by setting the SEI and then CLI after the important section. ; ; ; .ORG $E000

   SEI             ; INITIALIZING THE STACK POINTER
   LDX #$FF
   TXS

;

   LDX #$00
   LDY #$00

Delay DEX

   BNE Delay
   DEY
   BNE Delay

; ; Each Process has a reserved space in memory starting with process 1 at ; $0200-$03FF, process 2 at $0400-$05FF. With this model, an 8K RAM can ; support 15 such processes provided none of the RAM outside zero page and ; stack is used during the execution of a particular process. ; M_box = $F0 ; A Mailbox used to communicate between processes Com1 = $F8 ; User Communications Channel to other processes Com2 = $F9 Temp = $FA ; A temporary variable used during SWAPS and SPAWNS Proc_Ptr = $FB ; Pointer to the reserved space of the current process Proc = $FC ; Current process number Proc_N = $FE ; Actual Number for active Processes Proc_M = $FF ; Maximum Number of Processes that have been concurrent ; ; A Process Record Consists of: ; Offset Purpose ; —— ——- ; 00 Priority ; 01 Priority Counter ; 02 Accumulator ; 03 X Register ; 04 Y Register ; 05 Stack Pointer ; ; 10-FF Zero Page Memory from $00-$EF ; 100-1FF System Stack Space ;

   lda #$01           ; Initialize the start up process as 1
   sta Proc
   sta Proc_N         ; Set the number of processes to 1
   sta $0200          ; Set the priority of process 1 to 1
   lda #$00
   sta $0201          ; Set the priority counter of process 1 to 0

page 7

lda #$00

   sta Proc_Ptr       ; Initialize the process pointer to point to
   lda #$02           ; Process 1 reserved space $0200-$03FF
   sta Proc_Ptr+1
   JMP Start_Code

; ;

; IRQ Subroutine to Swap Tasks ;

; IRQ_VECT sta Temp ; Store .A Temporarily ; ; If there is only one active process currently then just return ;

   lda Proc_N
   cmp #$01
   bne Cont_Swap1
   lda Temp
   rti

; ; Continue there is more than one Process ; Cont_Swap1 tya

   pha

; ; Check process priority counter. If it equals the priority of the process ; then attempt to swap in another process ;

   ldy #$00
   lda (Proc_Ptr),y   ; Load Priority Number
   beq Swap_In        ; If 'killed' process then just swap in another
   iny
   inc (Proc_Ptr),y   ; Increment Priority Counter
   cmp (Proc_Ptr),y
   beq Cont_Swap2

; ; Not done this Process, Return ;

   pla
   tay
   lda Temp
   rti

; ; Other Processes available and this one is done: S W A P O U T ; Cont_Swap2 pla

   ldy #$04
   sta (Proc_Ptr),y   ; Save .Y
   dey
   txa
   sta (Proc_Ptr),y   ; Save .X
   dey
   lda Temp
   sta (Proc_Ptr),y   ; Save .A
   ldy #$05
   tsx
   txa
   sta (Proc_Ptr),y   ; Save .SP

page 8

; ; Swap Zero Page ($00-$EF) to (Proc_Ptr + $10-$FF) ;

   ldy #$00
   lda #$10
   sta Proc_Ptr

Out_Zero lda $00,y

   sta (Proc_Ptr),y
   iny
   cpy #$f0
   bne Out_Zero

; ; Swap System Stack ;

   lda #$00
   sta Proc_Ptr
   inc Proc_Ptr+1
   tsx
   txa
   tay

Out_Stack iny

   beq Swap_In
   lda $0100,y
   sta (Proc_Ptr),y
   jmp Out_Stack

; ; ; Look for the next process to swap in ; Swap_In Another lda Proc ; Looking for another process to Swap in

   cmp Proc_M
   bne Not_End

; ; Go back to Process #1 ;

   lda #$01
   sta Proc
   lda #$02
   sta Proc_Ptr+1
   jmp Check_Proc

; ; Go to the next Process ; Not_End clc

   lda Proc_Ptr+1
   adc #$02
   sta Proc_Ptr+1
   inc Proc

; ; Check this Process if Non-Active, go try another ;

   ldy #$00
   lda (Proc_Ptr),y
   beq Another

;

page 9

; Found an Acceptable Process: S W A P I N ; ; ; Get the Stack Pointer ;

   ldy #$05
   lda (Proc_Ptr),y  ; Restore .SP
   tax
   txs

; ; Swap In Zero Page ($00-$EF) to (Proc_Ptr + $10-$FF) ;

   ldy #$00
   lda #$10
   sta Proc_Ptr

In_Zero lda (Proc_Ptr),y

   sta $00,y
   iny
   cpy #$f0
   bne In_Zero

; ; Swap System Stack ;

   lda #$00
   sta Proc_Ptr
   inc Proc_Ptr+1
   tsx
   txa
   tay

In_Stack iny

   beq Restore_Regs
   lda (Proc_Ptr),y
   sta $0100,y
   jmp In_Stack

; ; Restore all of the system registers ; Restore_Regs

   lda #$00
   sta Proc_Ptr
   dec Proc_Ptr+1
   ldy #$01          ; Set Priority Counter to 0
   sta (Proc_Ptr),y
   iny
   lda (Proc_Ptr),y  ; Temporarily store .A
   sta Temp
   iny
   lda (Proc_Ptr),y  ; Restore .X
   tax
   iny
   lda (Proc_Ptr),y  ; Restore .Y
   tay
   lda Temp          ; Restore .A
   rti

;——————— Done the Swap ———————- ; ; ;

page 10

;

; Spawn a New Process ;

; PHA Process PCH ; PHA Process PCL ; PHA Process Priority ; JSR Spawn High ; Spawn Low ; ; Spawn lda Proc_Ptr+1 ; Store Current Process Pointer

 sta Temp
 lda Proc         ; Store Current Process Number
 pha
 lda #$01         ; Set Process Pointer and Number to 1
 sta Proc
 lda #$02
 sta Proc_Ptr+1

; Free_Check ; See if there is an old process number no longer

 ldy #$00          ;   in use
 lda (Proc_Ptr),y
 beq Got_Free
 inc Proc
 clc
 lda Proc_Ptr+1
 adc #$02
 sta Proc_Ptr+1
 lda Proc_M
 sec
 cmp Proc
 bcs Free_Check
 inc Proc_M       ; Have to create an extra Process
 inc Proc_N

; ; Ok we are clear, Create this Process ; Got_Free tsx ; Get the current stack pointer

 txa
 clc
 adc #$05
 tax              ; Set x to point at Priority

;

 ldy #$00
 lda $0100,x      ; Transfer Priority to Process Space
 sta (Proc_Ptr),y

;

 ldy #$05         ; Set .sp = #$FC
 lda #$FC
 sta (Proc_Ptr),y

;

 ldy #$02         ; Set the accumulator to 1 to indicate: START
 lda #$01         ; to the new process
 sta (Proc_Ptr),y

;

 inc Proc_Ptr+1   ; To point into stack swap space for this process

;

page 11

lda #$00 ; Processor Status Register, for this process

 ldy #$FD
 sta (Proc_Ptr),y

;

 inx
 lda $0100,x      ; Load PCL
 iny
 sta (Proc_Ptr),y ; Put into (swapped) Stack

;

 inx
 lda $0100,x      ; Load PCH
 iny
 sta (Proc_Ptr),y ; Put into (swapped) Stack

;

 lda Temp         ; Set Pointer back to original (Spawner) process
 sta Proc_Ptr+1

;

 lda Proc         ; Take Spawned Process number and put in Temp
 sta Temp

;

 pla              ; Restore Spawned Process number
 sta Proc

;

 pla              ; Pull 'Spawn' return address from stack
 tax
 pla
 tay

;

 pla              ; Pull Spawn data out of the stack
 pla
 pla

;

 tya              ; Push the Return Address back to the stack
 pha
 txa
 pha
 lda Temp         ; Return Spawned Process Number
 rts

;————– Done Spawn —————– ; ; ; ;

; Kill a Process ;

; ; Input Registers : NONE ; Output Registers: NEVER RETURNS IF KILL IS SUCCESSFUL ; Kill lda Proc_N

 cmp #$01          ; Can't Clear Last Process
 bne Ok_More
 rts

Ok_More ldy #$00 ; OK Kill the Process, put a 0 in Priority

 tya
 sta (Proc_Ptr)

;

page 12

dec Proc_N ; One Less Process ;

 lda Proc          ; If we are clearing 'Maximum' Process then
 cmp Proc_M        ; then reduce maximum
 beq Reduce_Max
 jmp Swap_In       ; Otherwise Go swap another in

; Reduce_Max

 dec Proc
 dec Proc_M
 dec Proc_Ptr+1
 dec Proc_Ptr+1
 lda (Proc_ptr),y
 beq Reduce_Max
 jmp Swap_In

;———————- Done Clear a Process ————————— ; ; ; ; ;

; An Example Spawnable Process ;

; Input Registers: .A = #$00 Means that we just want the address of ; (JSR Child) this process so that the process swapper ; will know where to start. ; ; (RTI to CHILD1) .A = #$01 Means that the process swapper has signalled ; this process to actually start ; Child jsr Child1 Child1 cmp #$00

 bne Go_For_It

; ; Process was called to get its start up address ;

 pla            ;  Grab Child1 start up address
 clc
 adc #$01       ;  Remember that an RTS return address points at the
 tax            ;  last byte of the JSR statement.
 pla            ;  RTI return addresses point to the first byte of the
 adc #$00       ;  next instruction to be executed
 tay

;

 pla            ;  Save Return Address to program calling Child
 sta Temp
 pla
 sta Proc_Ptr

;

 tya            ;  Push Child1  RTI  address
 pha
 txa
 pha

;

 lda Proc_Ptr   ;  This Pushes the calling program's return address
 pha            ;  back into the stack
 lda Temp
 pha

;

page 13

lda #$00 ; Returns Proc_Ptr(low) to #$00 after its use as a

 sta Proc_Ptr   ; Temporary variable
 rts

; ; Spawned Process actually starts: ; Note that PLA's are not required to get rid of the JSR Child1 start up ; address since the RTI address pushed in points to Child1 NOT Child Go_For_It

Body of the spawned process

; ; ;

; An Example of a Kill of the present Process ;

;

   { User Code  }

;

 sei
 jsr Kill     ;  This should kill the process unless it is the
	      ;  only process
 cli

; ; This is the only process ;

   { More user code }

; ; ; ;

; Start of User Code ;

Start_Code { Your first process goes here } ; ; ; Example Spawn of Process 'Child' ;

 sei             ;  Prevent swap attempts during process creation
 lda #$00
 jsr Child       ;  Request Address for Child1

;

 lda #Priority
 pha             ;  Push Priority into the stack

;

 jsr Spawn       ;  Ask the Process Swapper to set 'Child1' up in
		 ;  the swap schedule
 rol a
 sta Ptr+1       ;  Set pointer to the Child process zero page
 lda #$10        ;  reserved area
 sta Ptr

;

page 14

; The Spawn call returns the process number. If there is some initial data ; or a pointer that this process would like to pass to 'Child1' then the ; address of its ZERO PAGE reserved data space is pointed to by '(Ptr),y'. ; Once the data has been transferred: ;

 cli             ;  Re-enable swap attempts

; ; ; ;

; Example of Taking full control of execution temporarily ;

;

 sei             ; Disable swaps
{ User Code }
 cli             ; Re-enable swaps

; ; ; ;

; Example of taking full control by Killing all other processes ;

; Ptr = $00 K_Proc = $02 ;

 sei               ; Disable swaps

;

 lda #$00          ; Set Pointer to $0200
 sta Ptr
 lda #$02
 sta Ptr+1

;

 lda #$01          ; Set Kill Process counter to 1
 sta K_Proc

; Top lda Proc

 cmp K_Proc
 beq Don_t_Kill
 ldy #$00
 tya
 sta (Ptr),y

; Don_t_Kill

 cmp Proc_M
 beq Done_Kill
 inc Ptr+1
 inc Ptr+1
 inc K_Proc
 jmp Top

;

page 15

Done_Kill

 lda #$01
 sta Proc_N
 lda Proc
 sta Proc_M
 cli         ; Note that this is optional, if we know that there
	     ; are no other processes we could prevent swap decisions
	     ; by not clearing the IRQ mask.

;

 { More code that will not be swapped out }

; ; ; .ORG $FFFC .WORD $E000 .WORD IRQ_VECT .END ; ; ——————– Done Multitasking example ————————-

<eof> 

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

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki