mirror of https://gitlab.com/camelot/kickc.git
456 lines
9.9 KiB
NASM
456 lines
9.9 KiB
NASM
// Calculates the 1000 first primes
|
|
// From A Comparison of Language Speed, The Transactor, March 1987, Volume 7, Issue 5
|
|
// http://csbruce.com/cbm/transactor/pdfs/trans_v7_i05.pdf
|
|
// Commodore 64 PRG executable file
|
|
.file [name="primes-1000-2.prg", type="prg", segments="Program"]
|
|
.segmentdef Program [segments="Basic, Code, Data"]
|
|
.segmentdef Basic [start=$0801]
|
|
.segmentdef Code [start=$80d]
|
|
.segmentdef Data [startAfter="Code"]
|
|
.segment Basic
|
|
:BasicUpstart(main)
|
|
.const SIZEOF_UNSIGNED_INT = 2
|
|
.label print_screen = $400
|
|
.label print_char_cursor = $b
|
|
// The number currently being tested for whether it is a prime
|
|
.label potential = $10
|
|
// The last index to test. It is the smallest index where PRIMES[test_last] > sqr(potential)
|
|
.label test_last = $12
|
|
// The index into PRIMES[] used for prime testing. It runs from 2 to test_last for each number tested.
|
|
.label test_idx = $d
|
|
// The index of the last prime we put into the PRIME[] table
|
|
.label prime_idx = $13
|
|
.segment Code
|
|
main: {
|
|
.label __0 = $e
|
|
.label __14 = 2
|
|
.label __15 = 2
|
|
// PRIMES[1] = 2
|
|
lda #<2
|
|
sta PRIMES+1*SIZEOF_UNSIGNED_INT
|
|
lda #>2
|
|
sta PRIMES+1*SIZEOF_UNSIGNED_INT+1
|
|
// PRIMES[2] = 3
|
|
lda #<3
|
|
sta PRIMES+2*SIZEOF_UNSIGNED_INT
|
|
lda #>3
|
|
sta PRIMES+2*SIZEOF_UNSIGNED_INT+1
|
|
lda #<print_screen
|
|
sta.z print_char_cursor
|
|
lda #>print_screen
|
|
sta.z print_char_cursor+1
|
|
lda #<2
|
|
sta.z prime_idx
|
|
lda #>2
|
|
sta.z prime_idx+1
|
|
lda #<3
|
|
sta.z potential
|
|
lda #>3
|
|
sta.z potential+1
|
|
lda #2
|
|
sta.z test_last
|
|
__b1:
|
|
// char p = (char)PRIMES[test_last]
|
|
lda.z test_last
|
|
asl
|
|
tay
|
|
lda PRIMES,y
|
|
// mul8u(p, p)
|
|
tax
|
|
jsr mul8u
|
|
// if(potential > mul8u(p, p))
|
|
lda.z potential+1
|
|
cmp.z __0+1
|
|
bne !+
|
|
lda.z potential
|
|
cmp.z __0
|
|
beq __b2
|
|
!:
|
|
bcc __b2
|
|
// test_last++;
|
|
inc.z test_last
|
|
__b2:
|
|
// potential +=2
|
|
lda #2
|
|
clc
|
|
adc.z potential
|
|
sta.z potential
|
|
bcc !+
|
|
inc.z potential+1
|
|
!:
|
|
lda #2
|
|
sta.z test_idx
|
|
__b3:
|
|
// div16u8u(potential, (char)PRIMES[test_idx++])
|
|
lda.z test_idx
|
|
asl
|
|
tax
|
|
lda PRIMES,x
|
|
sta.z div16u8u.divisor
|
|
jsr div16u8u
|
|
// div16u8u(potential, (char)PRIMES[test_idx++]);
|
|
inc.z test_idx
|
|
// if(rem8u == 0)
|
|
cpy #0
|
|
bne __b4
|
|
// potential +=2
|
|
lda #2
|
|
clc
|
|
adc.z potential
|
|
sta.z potential
|
|
bcc !+
|
|
inc.z potential+1
|
|
!:
|
|
lda #2
|
|
sta.z test_idx
|
|
__b4:
|
|
// while (test_idx<=test_last)
|
|
lda.z test_last
|
|
cmp.z test_idx
|
|
bcs __b3
|
|
// PRIMES[++prime_idx] = potential;
|
|
inc.z prime_idx
|
|
bne !+
|
|
inc.z prime_idx+1
|
|
!:
|
|
// PRIMES[++prime_idx] = potential
|
|
lda.z prime_idx
|
|
asl
|
|
sta.z __14
|
|
lda.z prime_idx+1
|
|
rol
|
|
sta.z __14+1
|
|
lda.z __15
|
|
clc
|
|
adc #<PRIMES
|
|
sta.z __15
|
|
lda.z __15+1
|
|
adc #>PRIMES
|
|
sta.z __15+1
|
|
ldy #0
|
|
lda.z potential
|
|
sta (__15),y
|
|
iny
|
|
lda.z potential+1
|
|
sta (__15),y
|
|
// print_uint_decimal(potential)
|
|
jsr print_uint_decimal
|
|
// print_char(' ')
|
|
lda #' '
|
|
jsr print_char
|
|
// while(prime_idx<totalprimes)
|
|
lda.z prime_idx+1
|
|
cmp #>$3e8
|
|
bcs !__b1+
|
|
jmp __b1
|
|
!__b1:
|
|
bne !+
|
|
lda.z prime_idx
|
|
cmp #<$3e8
|
|
bcs !__b1+
|
|
jmp __b1
|
|
!__b1:
|
|
!:
|
|
// }
|
|
rts
|
|
}
|
|
// Perform binary multiplication of two unsigned 8-bit chars into a 16-bit unsigned int
|
|
// __zp($e) unsigned int mul8u(__register(X) char a, __register(A) char b)
|
|
mul8u: {
|
|
.label mb = 2
|
|
.label res = $e
|
|
.label return = $e
|
|
// unsigned int mb = b
|
|
sta.z mb
|
|
lda #0
|
|
sta.z mb+1
|
|
sta.z res
|
|
sta.z res+1
|
|
__b1:
|
|
// while(a!=0)
|
|
cpx #0
|
|
bne __b2
|
|
// }
|
|
rts
|
|
__b2:
|
|
// a&1
|
|
txa
|
|
and #1
|
|
// if( (a&1) != 0)
|
|
cmp #0
|
|
beq __b3
|
|
// res = res + mb
|
|
clc
|
|
lda.z res
|
|
adc.z mb
|
|
sta.z res
|
|
lda.z res+1
|
|
adc.z mb+1
|
|
sta.z res+1
|
|
__b3:
|
|
// a = a>>1
|
|
txa
|
|
lsr
|
|
tax
|
|
// mb = mb<<1
|
|
asl.z mb
|
|
rol.z mb+1
|
|
jmp __b1
|
|
}
|
|
// Divide unsigned 16-bit unsigned long dividend with a 8-bit unsigned char divisor
|
|
// The 8-bit unsigned char remainder can be found in rem8u after the division
|
|
// unsigned int div16u8u(__zp($10) unsigned int dividend, __zp(8) char divisor)
|
|
div16u8u: {
|
|
.label dividend = $10
|
|
.label divisor = 8
|
|
// unsigned char quotient_hi = divr8u(BYTE1(dividend), divisor, 0)
|
|
lda.z dividend+1
|
|
sta.z divr8u.dividend
|
|
ldy #0
|
|
jsr divr8u
|
|
// unsigned char quotient_lo = divr8u(BYTE0(dividend), divisor, rem8u)
|
|
lda.z dividend
|
|
sta.z divr8u.dividend
|
|
jsr divr8u
|
|
// }
|
|
rts
|
|
}
|
|
// Print a unsigned int as DECIMAL
|
|
// void print_uint_decimal(__zp($10) unsigned int w)
|
|
print_uint_decimal: {
|
|
.label w = $10
|
|
// utoa(w, decimal_digits, DECIMAL)
|
|
lda.z w
|
|
sta.z utoa.value
|
|
lda.z w+1
|
|
sta.z utoa.value+1
|
|
jsr utoa
|
|
// print_str(decimal_digits)
|
|
jsr print_str
|
|
// }
|
|
rts
|
|
}
|
|
// Print a single char
|
|
// void print_char(__register(A) char ch)
|
|
print_char: {
|
|
// *(print_char_cursor++) = ch
|
|
ldy #0
|
|
sta (print_char_cursor),y
|
|
// *(print_char_cursor++) = ch;
|
|
inc.z print_char_cursor
|
|
bne !+
|
|
inc.z print_char_cursor+1
|
|
!:
|
|
// }
|
|
rts
|
|
}
|
|
// Performs division on two 8 bit unsigned chars and an initial remainder
|
|
// Returns dividend/divisor.
|
|
// The final remainder will be set into the global variable rem8u
|
|
// Implemented using simple binary division
|
|
// __zp(6) char divr8u(__zp(7) char dividend, __zp(8) char divisor, __register(Y) char rem)
|
|
divr8u: {
|
|
.label dividend = 7
|
|
.label quotient = 6
|
|
.label return = 6
|
|
.label divisor = 8
|
|
ldx #0
|
|
txa
|
|
sta.z quotient
|
|
__b1:
|
|
// rem = rem << 1
|
|
tya
|
|
asl
|
|
tay
|
|
// dividend & 0x80
|
|
lda #$80
|
|
and.z dividend
|
|
// if( (dividend & 0x80) != 0 )
|
|
cmp #0
|
|
beq __b2
|
|
// rem = rem | 1
|
|
tya
|
|
ora #1
|
|
tay
|
|
__b2:
|
|
// dividend = dividend << 1
|
|
asl.z dividend
|
|
// quotient = quotient << 1
|
|
asl.z quotient
|
|
// if(rem>=divisor)
|
|
cpy.z divisor
|
|
bcc __b3
|
|
// quotient++;
|
|
inc.z quotient
|
|
// rem = rem - divisor
|
|
tya
|
|
sec
|
|
sbc.z divisor
|
|
tay
|
|
__b3:
|
|
// for( char i : 0..7)
|
|
inx
|
|
cpx #8
|
|
bne __b1
|
|
// rem8u = rem
|
|
// }
|
|
rts
|
|
}
|
|
// Converts unsigned number value to a string representing it in RADIX format.
|
|
// If the leading digits are zero they are not included in the string.
|
|
// - value : The number to be converted to RADIX
|
|
// - buffer : receives the string representing the number and zero-termination.
|
|
// - radix : The radix to convert the number to (from the enum RADIX)
|
|
// void utoa(__zp(2) unsigned int value, __zp(9) char *buffer, char radix)
|
|
utoa: {
|
|
.const max_digits = 5
|
|
.label value = 2
|
|
.label digit_value = 4
|
|
.label buffer = 9
|
|
.label digit = $d
|
|
lda #<decimal_digits
|
|
sta.z buffer
|
|
lda #>decimal_digits
|
|
sta.z buffer+1
|
|
ldx #0
|
|
txa
|
|
sta.z digit
|
|
__b1:
|
|
// for( char digit=0; digit<max_digits-1; digit++ )
|
|
lda.z digit
|
|
cmp #max_digits-1
|
|
bcc __b2
|
|
// *buffer++ = DIGITS[(char)value]
|
|
ldx.z value
|
|
lda DIGITS,x
|
|
ldy #0
|
|
sta (buffer),y
|
|
// *buffer++ = DIGITS[(char)value];
|
|
inc.z buffer
|
|
bne !+
|
|
inc.z buffer+1
|
|
!:
|
|
// *buffer = 0
|
|
lda #0
|
|
tay
|
|
sta (buffer),y
|
|
// }
|
|
rts
|
|
__b2:
|
|
// unsigned int digit_value = digit_values[digit]
|
|
lda.z digit
|
|
asl
|
|
tay
|
|
lda RADIX_DECIMAL_VALUES,y
|
|
sta.z digit_value
|
|
lda RADIX_DECIMAL_VALUES+1,y
|
|
sta.z digit_value+1
|
|
// if (started || value >= digit_value)
|
|
cpx #0
|
|
bne __b5
|
|
cmp.z value+1
|
|
bne !+
|
|
lda.z digit_value
|
|
cmp.z value
|
|
beq __b5
|
|
!:
|
|
bcc __b5
|
|
__b4:
|
|
// for( char digit=0; digit<max_digits-1; digit++ )
|
|
inc.z digit
|
|
jmp __b1
|
|
__b5:
|
|
// utoa_append(buffer++, value, digit_value)
|
|
jsr utoa_append
|
|
// utoa_append(buffer++, value, digit_value)
|
|
// value = utoa_append(buffer++, value, digit_value)
|
|
// value = utoa_append(buffer++, value, digit_value);
|
|
inc.z buffer
|
|
bne !+
|
|
inc.z buffer+1
|
|
!:
|
|
ldx #1
|
|
jmp __b4
|
|
}
|
|
// Print a zero-terminated string
|
|
// void print_str(__zp(9) char *str)
|
|
print_str: {
|
|
.label str = 9
|
|
lda #<decimal_digits
|
|
sta.z str
|
|
lda #>decimal_digits
|
|
sta.z str+1
|
|
__b1:
|
|
// while(*str)
|
|
ldy #0
|
|
lda (str),y
|
|
cmp #0
|
|
bne __b2
|
|
// }
|
|
rts
|
|
__b2:
|
|
// print_char(*(str++))
|
|
ldy #0
|
|
lda (str),y
|
|
jsr print_char
|
|
// print_char(*(str++));
|
|
inc.z str
|
|
bne !+
|
|
inc.z str+1
|
|
!:
|
|
jmp __b1
|
|
}
|
|
// Used to convert a single digit of an unsigned number value to a string representation
|
|
// Counts a single digit up from '0' as long as the value is larger than sub.
|
|
// Each time the digit is increased sub is subtracted from value.
|
|
// - buffer : pointer to the char that receives the digit
|
|
// - value : The value where the digit will be derived from
|
|
// - sub : the value of a '1' in the digit. Subtracted continually while the digit is increased.
|
|
// (For decimal the subs used are 10000, 1000, 100, 10, 1)
|
|
// returns : the value reduced by sub * digit so that it is less than sub.
|
|
// __zp(2) unsigned int utoa_append(__zp(9) char *buffer, __zp(2) unsigned int value, __zp(4) unsigned int sub)
|
|
utoa_append: {
|
|
.label buffer = 9
|
|
.label value = 2
|
|
.label sub = 4
|
|
.label return = 2
|
|
ldx #0
|
|
__b1:
|
|
// while (value >= sub)
|
|
lda.z sub+1
|
|
cmp.z value+1
|
|
bne !+
|
|
lda.z sub
|
|
cmp.z value
|
|
beq __b2
|
|
!:
|
|
bcc __b2
|
|
// *buffer = DIGITS[digit]
|
|
lda DIGITS,x
|
|
ldy #0
|
|
sta (buffer),y
|
|
// }
|
|
rts
|
|
__b2:
|
|
// digit++;
|
|
inx
|
|
// value -= sub
|
|
lda.z value
|
|
sec
|
|
sbc.z sub
|
|
sta.z value
|
|
lda.z value+1
|
|
sbc.z sub+1
|
|
sta.z value+1
|
|
jmp __b1
|
|
}
|
|
.segment Data
|
|
// The digits used for numbers
|
|
DIGITS: .text "0123456789abcdef"
|
|
// Values of decimal digits
|
|
RADIX_DECIMAL_VALUES: .word $2710, $3e8, $64, $a
|
|
// Digits used for storing the decimal unsigned int
|
|
decimal_digits: .fill 6, 0
|
|
// Table that is filled with the primes we are finding
|
|
PRIMES: .fill 2*$3e8, 0
|