A0 01 84 FD 88 84 FE C4 02 F0 32 E6 FD A0 00 A5 FD C9 04 90 1F 85 64 B1 FB 85
65 A9 00 A2 08 06 64 2A C5 65 90 02 E5 65 CA D0 F4 C9 00 F0 DC C8 C4 FE D0 DB
A5 FD A4 FE 91 FB C8 D0 C8 A9 00 18 A8 C4 FE F0 05 71 FB C8 D0 F7 60
Expects a pointer to some temporary storage in $fb
/$fc
and the number of primes to sum up in $2
. Returns the sum in A
(the accu register).
Never did some prime checks in 6502 machine code, so here it finally comes ;)
Note this starts giving wrong results for inputs >= 14. This is because of overflow, the code works with the "natural" number range of the 8bit platform which is 0 - 255
for unsigned.
Commented disassembly
; function to sum the first n primes
;
; input:
; $fb/$fc: pointer to a buffer for temporary storage of primes
; $2: number of primes to sum (n)
; output:
; A: sum of the first n primes
; clobbers:
; $fd: current number under primality test
; $fe: number of primes currently found
; $64: temporary numerator for modulo check
; $65: temporary divisor for modulo check
; X, Y
.primesum:
A0 01 LDY #$01 ; init variable for ...
84 FD STY $FD ; next prime number to test
88 DEY ; init number of found primes
.mainloop:
84 FE STY $FE ; store current number of found primes
C4 02 CPY $02 ; compare with requested number
F0 32 BEQ .sum ; enough primes -> calculate their sum
.mainnext:
E6 FD INC $FD ; check next prime number
A0 00 LDY #$00 ; start check against first prime number
.primecheckloop:
A5 FD LDA $FD ; load current number to check
C9 04 CMP #$04 ; smaller than 4?
90 1F BCC .isprime ; is a prime (shortcut to get list started)
85 64 STA $64 ; store to temp as numerator
B1 FB LDA ($FB),Y ; load from prime number table
85 65 STA $65 ; store to temp as divisor
A9 00 LDA #$00 ; init modulo to 0
A2 08 LDX #$08 ; iterate over 8 bits
.bitloop:
06 64 ASL $64 ; shift left numerator
2A ROL A ; shift carry into modulo
C5 65 CMP $65 ; compare with divisor
90 02 BCC .bitnext ; smaller -> to next bit
E5 65 SBC $65 ; otherwise subtract divisor
.bitnext:
CA DEX ; next bit
D0 F4 BNE .bitloop
C9 00 CMP #$00 ; compare modulo with 0
F0 DC BEQ .mainnext ; equal? -> no prime number
C8 INY ; next index in prime number table
C4 FE CPY $FE ; checked against all prime numbers?
D0 DB BNE .primecheckloop ; no -> check next
.isprime:
A5 FD LDA $FD ; prime found
A4 FE LDY $FE ; then store in table
91 FB STA ($FB),Y
C8 INY ; increment number of primes found
D0 C8 BNE .mainloop ; and repeat whole process
.sum:
A9 00 LDA #$00 ; initialize sum to 0
18 CLC
A8 TAY ; start adding table from position 0
.sumloop:
C4 FE CPY $FE ; whole table added?
F0 05 BEQ .done ; yes -> we're done
71 FB ADC ($FB),Y ; add current entry
C8 INY ; increment index
D0 F7 BNE .sumloop ; and repeat
.done:
60 RTS
Example C64 assembler program using the routine:
Online demo
Code in ca65 syntax:
.import primesum ; link with routine above
.segment "BHDR" ; BASIC header
.word $0801 ; load address
.word $080b ; pointer next BASIC line
.word 2018 ; line number
.byte $9e ; BASIC token "SYS"
.byte "2061",$0,$0,$0 ; 2061 ($080d) and terminating 0 bytes
.bss
linebuf: .res 4 ; maximum length of a valid unsigned
; 8-bit number input
convbuf: .res 3 ; 3 BCD digits for unsigned 8-bit
; number conversion
primebuf: .res $100 ; buffer for primesum function
.data
prompt: .byte "> ", $0
errmsg: .byte "Error parsing number, try again.", $d, $0
.code
lda #$17 ; set upper/lower mode
sta $d018
input:
lda #<prompt ; display prompt
ldy #>prompt
jsr $ab1e
lda #<linebuf ; read string into buffer
ldy #>linebuf
ldx #4
jsr readline
lda linebuf ; empty line?
beq input ; try again
lda #<linebuf ; convert input to int8
ldy #>linebuf
jsr touint8
bcc numok ; successful -> start processing
lda #<errmsg ; else show error message and repeat
ldy #>errmsg
jsr $ab1e
bcs input
numok:
sta $2
lda #<primebuf
sta $fb
lda #>primebuf
sta $fc
jsr primesum ; call function to sum primes
tax ; and ...
lda #$0 ;
jmp $bdcd ; .. print result
; read a line of input from keyboard, terminate it with 0
; expects pointer to input buffer in A/Y, buffer length in X
.proc readline
dex
stx $fb
sta $fc
sty $fd
ldy #$0
sty $cc ; enable cursor blinking
sty $fe ; temporary for loop variable
getkey: jsr $f142 ; get character from keyboard
beq getkey
sta $2 ; save to temporary
and #$7f
cmp #$20 ; check for control character
bcs checkout ; no -> check buffer size
cmp #$d ; was it enter/return?
beq prepout ; -> normal flow
cmp #$14 ; was it backspace/delete?
bne getkey ; if not, get next char
lda $fe ; check current index
beq getkey ; zero -> backspace not possible
bne prepout ; skip checking buffer size for bs
checkout: lda $fe ; buffer index
cmp $fb ; check against buffer size
beq getkey ; if it would overflow, loop again
prepout: sei ; no interrupts
ldy $d3 ; get current screen column
lda ($d1),y ; and clear
and #$7f ; cursor in
sta ($d1),y ; current row
output: lda $2 ; load character
jsr $e716 ; and output
ldx $cf ; check cursor phase
beq store ; invisible -> to store
ldy $d3 ; get current screen column
lda ($d1),y ; and show
ora #$80 ; cursor in
sta ($d1),y ; current row
lda $2 ; load character
store: cli ; enable interrupts
cmp #$14 ; was it backspace/delete?
beq backspace ; to backspace handling code
cmp #$d ; was it enter/return?
beq done ; then we're done.
ldy $fe ; load buffer index
sta ($fc),y ; store character in buffer
iny ; advance buffer index
sty $fe
bne getkey ; not zero -> ok
done: lda #$0 ; terminate string in buffer with zero
ldy $fe ; get buffer index
sta ($fc),y ; store terminator in buffer
sei ; no interrupts
ldy $d3 ; get current screen column
lda ($d1),y ; and clear
and #$7f ; cursor in
sta ($d1),y ; current row
inc $cc ; disable cursor blinking
cli ; enable interrupts
rts ; return
backspace: dec $fe ; decrement buffer index
bcs getkey ; and get next key
.endproc
; parse / convert uint8 number using a BCD representation and double-dabble
.proc touint8
sta $fb
sty $fc
ldy #$0
sty convbuf
sty convbuf+1
sty convbuf+2
scanloop: lda ($fb),y
beq copy
iny
cmp #$20
beq scanloop
cmp #$30
bcc error
cmp #$3a
bcs error
bcc scanloop
error: sec
rts
copy: dey
bmi error
ldx #$2
copyloop: lda ($fb),y
cmp #$30
bcc copynext
cmp #$3a
bcs copynext
sec
sbc #$30
sta convbuf,x
dex
copynext: dey
bpl copyloop
lda #$0
sta $fb
ldx #$8
loop: lsr convbuf
lda convbuf+1
bcc skipbit1
ora #$10
skipbit1: lsr a
sta convbuf+1
lda convbuf+2
bcc skipbit2
ora #$10
skipbit2: lsr a
sta convbuf+2
ror $fb
dex
beq done
lda convbuf
cmp #$8
bmi nosub1
sbc #$3
sta convbuf
nosub1: lda convbuf+1
cmp #$8
bmi nosub2
sbc #$3
sta convbuf+1
nosub2: lda convbuf+2
cmp #$8
bmi loop
sbc #$3
sta convbuf+2
bcs loop
done: lda $fb
clc
rts
.endproc