Quantcast
Channel: Blogs at All About Circuits
Viewing all articles
Browse latest Browse all 742

A Sieve of Eratosthene method on ATMEL AVR ATMEGA32

$
0
0
The Sieve of Eratosthene is an ancient algorithm, historically attributed to the Greek mathematician Eratosthene. Simply applying the definition of a prime number, the method allows to find all the prime numbers up to a given number (limit). It's an iterative method, starting with 2, the first prime number, and it can be easily described as it follows.
  1. Mark all the multiples of 2 and delete them.
  2. Move on the next number and look for all its multiples, than delete them again.
  3. Repeat the procedure for all the following numbers up to the √n, where 'n' is the limit number.
At this point we are totally sure that all the "survivors" of the list are prime numbers.

It's a very simple method for testing basic Assembly skills, yet it's not really useful for big numbers due to the limited size of the data Ram of our processor (2048 cells of 1 byte). Let's start with the code, we can divide our program in 4 different steps.

  • First of all we need to clear the data Ram that will keep storing values from the past
include "m32def.inc"
.equ n = 100 ;limit number 'n'
.org 0

rjmp main
.org 20

main:

ldi r16, low(RAMEND)
ldi r17, high(RAMEND)
out spl, r16
out sph, r17 ;initializing the stack pointer
clear:
push r0
push r26
push r27
push r28
push r29
push r30
push r31
eor r0, r0
ldi r26, 0x60
ldi r27, 0x00 ;start address 0x0060
ldi r30, 0x00
ldi r31, 0x08 ;counter 0x0800 (2048 addresses to clear)
loop:
st X+, r0 ;load 0 to the cells
sbiw r31:r30, 1 ;check the counter
brne loop
end:
pop r31
pop r30
pop r27
pop r26
pop r0
rjmp start
  • Than we can load all the numbers from 2 to 'n'.
start:
push r18
push r19
push r20
push r26
push r27
push r28
push r29
push r30
push r31

ldi r30, low(n-1)
ldi r31, high(n-1)
clr r18
clr r19
ldi r18, 2 ;start adding from 2
ldi r20, 1 ;inc register
ldi r26, 0x60
ldi r27, 0x00 ;start address 0x0060 (low address on 16 bit)
ldi r28, 0x61
ldi r29, 0x00 ;high address on 16 bit
loop1:
st X+, r18
adiw xh:xl , 1 ;moving 2 addresses next (16 bit), or on the low part of the next number
st Y+, r19
adiw yh:yl, 1 ;high part

add r18, r20
clr r21
adc r19, r21 ;inc r18 ;increasing the number to store

sbiw r31:r30, 1 ;repeat till n-1, we start from 2.
brne loop1
end1:
pop r31
pop r30
pop r29
pop r28
pop r27
pop r26
pop r20
pop r19
pop r18
  • Now it's the time for the real sieve algorithm, we can operate in different ways but since we wanna keep it simple yet optimized we don't want to use any other subroutine or complex operations. We will limit ourselves to the additions, and because of the fact that we don't know (actually don't wanna use) any square root subroutine, we will iterate the procedure till 'n', not till √n .
sieve:
ldi xl, 0x60 ;start from the first couple of addresses
ldi xh, 0x00
ldi yl, 0x61
ldi yh, 0x00
eor r0, r0

ldi r22, low(n-2)
ldi r23, high(n-2)
ldi r16, low(n-2)
ldi r17, high(n-2)
add r16, yl
adc r17, yh
add r22, r16
adc r23, r17 ;to determine the last valid address Y pointer

save:
push xl ;saving the address of the number I'm currently using for finding the multiples
push xh
push yl
push yh

loop2:
ld r24, x ;low part of the number
ld r25, y ;high part of the number

lsl r24
rol r25 ;times 2 for finding the multiples (2n on 16 bit)

delete:
add xl, r24
adc xh, r25
st x, r0 ;storing zeros to delete

add yl, r24
adc yh, r25
st y, r0

cp r22, yl
cpc r23, yh ;checking the last valid address to break
brcc delete

pop yh
pop yl
pop xh
pop xl

next:
adiw xh:xl, 2
adiw yh:yl, 2 ;pointing the next number

cp r22, yl
cpc r23, yh
brcs compatta ;checking the last address (if there are no other numbers)

check:
ld r14, x
ld r15, y ;checking if the next number stored is 0
tst r14
brne save
tst r15
breq next

  • After deleting all the non-prime numbers, we will have a bunch of holes (zeros) in our data Ram, so we will compact all the remaining values to store and show them as a list in our memory.
compact:
ldi xl, 0x62 ;starting from 3
ldi xh, 0x00
ldi yl, 0x63
ldi yh, 0x00

ldi r22, low(n-2)
ldi r23, high(n-2)
ldi r16, low(n-2)
ldi r17, high(n-2)
add r16, yl
adc r17, yh
add r22, r16
adc r23, r17 ;to determine the last valid address Y pointer

loopc:

ld r14, x
ld r15, y ;loading the value stored in X, Y
tst r14
brne increasing ;if it's not zero go on the next
tst r15

breq save2 ;if it's zero we have to save the address of the first zero we find

increasing:
adiw xl:xh, 2
adiw yl:yh, 2

cp r22, yl
cpc r23, yh
brcs fine ;checking the end

rjmp loopc

salva:
movw zl:zh, xl:xh ;copy the address of the first zero

checkzero:
adiw xl:xh, 2
adiw yl:yh, 2

cp r22, yl
cpc r23, yh
brcs stop

ld r14, x
ld r15, y
tst r14
brne copy ;check if there are other zeros following
tst r15
breq checkzero ;if there are go on and check for others

copy:
push zh
push zl
st z+, r14
st z, r15 ;save the value of the first number that it's not zero
pop zl
pop zh
st x, r0
st y, r0 ;store 0 in the address I just used for copying the number
movw xh:xl, zh:zl
adiw zh:zl, 1
movw yh:yl, zh:zl ;copy the last address with a value !=0
rjmp loopc

stop:
ret


Now we should be able to visualize all the prime numbers as a list in our memory. We simply implemented an algorithm using Assembly and the basic operations provided.

Viewing all articles
Browse latest Browse all 742

Trending Articles