1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-10-02 22:56:11 +00:00
kickc/src/test/ref/screen-center-distance.asm

551 lines
11 KiB
NASM

// Calculate the distance to the center of the screen - and show it using font-hex
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.const SIZEOF_WORD = 2
// Top of the heap used by malloc()
.label HEAP_TOP = $a000
.label D018 = $d018
// CIA #2 Timer A+B Value (32-bit)
.label CIA2_TIMER_AB = $dd04
// CIA #2 Timer A Control Register
.label CIA2_TIMER_A_CONTROL = $dd0e
// CIA #2 Timer B Control Register
.label CIA2_TIMER_B_CONTROL = $dd0f
// Timer Control - Start/stop timer (0:stop, 1: start)
.const CIA_TIMER_CONTROL_START = 1
// Timer Control - Time CONTINUOUS/ONE-SHOT (0:CONTINUOUS, 1: ONE-SHOT)
.const CIA_TIMER_CONTROL_CONTINUOUS = 0
// Timer B Control - Timer counts (00:system cycles, 01: CNT pulses, 10: timer A underflow, 11: time A underflow while CNT is high)
.const CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A = $40
// Clock cycles used to start & read the cycle clock by calling clock_start() and clock() once. Can be subtracted when calculating the number of cycles used by a routine.
// To make precise cycle measurements interrupts and the display must be disabled so neither steals any cycles from the code.
.const CLOCKS_PER_INIT = $12
.label CHARSET = $2000
.label SCREEN = $2800
.const NUM_SQUARES = $30
.label SQUARES = malloc.return
main: {
.label BASE_SCREEN = $400
.label BASE_CHARSET = $1000
.const toD0181_return = (>(SCREEN&$3fff)*4)|(>CHARSET)/4&$f
.const toD0182_return = (>(BASE_SCREEN&$3fff)*4)|(>BASE_CHARSET)/4&$f
.label __4 = $d
.label cyclecount = $d
jsr init_font_hex
lda #toD0181_return
sta D018
jsr clock_start
jsr init_dist_screen
jsr clock
lda.z cyclecount
sec
sbc #<CLOCKS_PER_INIT
sta.z cyclecount
lda.z cyclecount+1
sbc #>CLOCKS_PER_INIT
sta.z cyclecount+1
lda.z cyclecount+2
sbc #<CLOCKS_PER_INIT>>$10
sta.z cyclecount+2
lda.z cyclecount+3
sbc #>CLOCKS_PER_INIT>>$10
sta.z cyclecount+3
jsr print_dword_at
lda #toD0182_return
sta D018
rts
}
// Print a dword as HEX at a specific position
// print_dword_at(dword zeropage($d) dw)
print_dword_at: {
.label dw = $d
lda.z dw+2
sta.z print_word_at.w
lda.z dw+3
sta.z print_word_at.w+1
lda #<main.BASE_SCREEN
sta.z print_word_at.at
lda #>main.BASE_SCREEN
sta.z print_word_at.at+1
jsr print_word_at
lda.z dw
sta.z print_word_at.w
lda.z dw+1
sta.z print_word_at.w+1
lda #<main.BASE_SCREEN+4
sta.z print_word_at.at
lda #>main.BASE_SCREEN+4
sta.z print_word_at.at+1
jsr print_word_at
rts
}
// Print a word as HEX at a specific position
// print_word_at(word zeropage(4) w, byte* zeropage(9) at)
print_word_at: {
.label w = 4
.label at = 9
lda.z w+1
sta.z print_byte_at.b
jsr print_byte_at
lda.z w
sta.z print_byte_at.b
lda.z print_byte_at.at
clc
adc #2
sta.z print_byte_at.at
bcc !+
inc.z print_byte_at.at+1
!:
jsr print_byte_at
rts
}
// Print a byte as HEX at a specific position
// print_byte_at(byte zeropage($c) b, byte* zeropage(9) at)
print_byte_at: {
.label b = $c
.label at = 9
lda.z b
lsr
lsr
lsr
lsr
tay
ldx print_hextab,y
lda.z at
sta.z print_char_at.at
lda.z at+1
sta.z print_char_at.at+1
jsr print_char_at
lda #$f
and.z b
tay
lda.z at
clc
adc #1
sta.z print_char_at.at
lda.z at+1
adc #0
sta.z print_char_at.at+1
ldx print_hextab,y
jsr print_char_at
rts
}
// Print a single char
// print_char_at(byte register(X) ch, byte* zeropage(2) at)
print_char_at: {
.label at = 2
txa
ldy #0
sta (at),y
rts
}
// Returns the processor clock time used since the beginning of an implementation defined era (normally the beginning of the program).
// This uses CIA #2 Timer A+B on the C64, and must be initialized using clock_start()
clock: {
.label return = $d
lda #<$ffffffff
sec
sbc CIA2_TIMER_AB
sta.z return
lda #>$ffffffff
sbc CIA2_TIMER_AB+1
sta.z return+1
lda #<$ffffffff>>$10
sbc CIA2_TIMER_AB+2
sta.z return+2
lda #>$ffffffff>>$10
sbc CIA2_TIMER_AB+3
sta.z return+3
rts
}
// Populates 1000 bytes (a screen) with values representing the distance to the center.
// The actual value stored is distance*2 to increase precision
init_dist_screen: {
.label yds = $11
.label screen_topline = 4
.label screen_bottomline = 9
.label y = $c
.label xds = $13
.label ds = $13
.label x = 6
.label xb = $b
jsr init_squares
lda #<SCREEN+$28*$18
sta.z screen_bottomline
lda #>SCREEN+$28*$18
sta.z screen_bottomline+1
lda #<SCREEN
sta.z screen_topline
lda #>SCREEN
sta.z screen_topline+1
lda #0
sta.z y
__b1:
lda.z y
asl
cmp #$18
bcs __b2
eor #$ff
clc
adc #$18+1
__b4:
jsr sqr
lda.z sqr.return
sta.z sqr.return_2
lda.z sqr.return+1
sta.z sqr.return_2+1
lda #$27
sta.z xb
lda #0
sta.z x
__b5:
lda.z x
cmp #$13+1
bcc __b6
lda #$28
clc
adc.z screen_topline
sta.z screen_topline
bcc !+
inc.z screen_topline+1
!:
lda.z screen_bottomline
sec
sbc #<$28
sta.z screen_bottomline
lda.z screen_bottomline+1
sbc #>$28
sta.z screen_bottomline+1
inc.z y
lda #$d
cmp.z y
bne __b1
rts
__b6:
lda.z x
asl
cmp #$27
bcs __b8
eor #$ff
clc
adc #$27+1
__b10:
jsr sqr
lda.z ds
clc
adc.z yds
sta.z ds
lda.z ds+1
adc.z yds+1
sta.z ds+1
jsr sqrt
ldy.z x
sta (screen_topline),y
sta (screen_bottomline),y
ldy.z xb
sta (screen_topline),y
sta (screen_bottomline),y
inc.z x
dec.z xb
jmp __b5
__b8:
sec
sbc #$27
jmp __b10
__b2:
sec
sbc #$18
jmp __b4
}
// Find the (integer) square root of a word value
// If the square is not an integer then it returns the largest integer N where N*N <= val
// Uses a table of squares that must be initialized by calling init_squares()
// sqrt(word zeropage($13) val)
sqrt: {
.label __1 = 2
.label __3 = 2
.label found = 2
.label val = $13
jsr bsearch16u
lda.z __3
sec
sbc #<SQUARES
sta.z __3
lda.z __3+1
sbc #>SQUARES
sta.z __3+1
lsr.z __1+1
ror.z __1
lda.z __1
rts
}
// Searches an array of nitems unsigned words, the initial member of which is pointed to by base, for a member that matches the value key.
// - key - The value to look for
// - items - Pointer to the start of the array to search in
// - num - The number of items in the array
// Returns pointer to an entry in the array that matches the search key
// bsearch16u(word zeropage($13) key, word* zeropage(2) items, byte register(X) num)
bsearch16u: {
.label __2 = 2
.label pivot = $15
.label result = $17
.label return = 2
.label items = 2
.label key = $13
lda #<SQUARES
sta.z items
lda #>SQUARES
sta.z items+1
ldx #NUM_SQUARES
__b3:
cpx #0
bne __b4
ldy #1
lda (items),y
cmp.z key+1
bne !+
dey
lda (items),y
cmp.z key
beq __b2
!:
bcc __b2
lda.z __2
sec
sbc #<1*SIZEOF_WORD
sta.z __2
lda.z __2+1
sbc #>1*SIZEOF_WORD
sta.z __2+1
__b2:
rts
__b4:
txa
lsr
asl
clc
adc.z items
sta.z pivot
lda #0
adc.z items+1
sta.z pivot+1
sec
lda.z key
ldy #0
sbc (pivot),y
sta.z result
lda.z key+1
iny
sbc (pivot),y
sta.z result+1
bne __b6
lda.z result
bne __b6
lda.z pivot
sta.z return
lda.z pivot+1
sta.z return+1
rts
__b6:
lda.z result+1
bmi __b7
bne !+
lda.z result
beq __b7
!:
lda #1*SIZEOF_WORD
clc
adc.z pivot
sta.z items
lda #0
adc.z pivot+1
sta.z items+1
dex
__b7:
txa
lsr
tax
jmp __b3
}
// Find the square of a byte value
// Uses a table of squares that must be initialized by calling init_squares()
// sqr(byte register(A) val)
sqr: {
.label return = $13
.label return_2 = $11
asl
tay
lda SQUARES,y
sta.z return
lda SQUARES+1,y
sta.z return+1
rts
}
// Initialize squares table
// Uses iterative formula (x+1)^2 = x^2 + 2*x + 1
init_squares: {
.label squares = 9
.label sqr = 4
jsr malloc
ldx #0
lda #<SQUARES
sta.z squares
lda #>SQUARES
sta.z squares+1
txa
sta.z sqr
sta.z sqr+1
__b1:
ldy #0
lda.z sqr
sta (squares),y
iny
lda.z sqr+1
sta (squares),y
lda #SIZEOF_WORD
clc
adc.z squares
sta.z squares
bcc !+
inc.z squares+1
!:
txa
asl
clc
adc #1
clc
adc.z sqr
sta.z sqr
bcc !+
inc.z sqr+1
!:
inx
cpx #NUM_SQUARES-1+1
bne __b1
rts
}
// Allocates a block of size bytes of memory, returning a pointer to the beginning of the block.
// The content of the newly allocated block of memory is not initialized, remaining with indeterminate values.
malloc: {
.const size = NUM_SQUARES*SIZEOF_WORD
.label mem = HEAP_TOP-size
.label return = mem
rts
}
// Reset & start the processor clock time. The value can be read using clock().
// This uses CIA #2 Timer A+B on the C64
clock_start: {
// Setup CIA#2 timer A to count (down) CPU cycles
lda #CIA_TIMER_CONTROL_CONTINUOUS
sta CIA2_TIMER_A_CONTROL
lda #CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A
sta CIA2_TIMER_B_CONTROL
lda #<$ffffffff
sta CIA2_TIMER_AB
lda #>$ffffffff
sta CIA2_TIMER_AB+1
lda #<$ffffffff>>$10
sta CIA2_TIMER_AB+2
lda #>$ffffffff>>$10
sta CIA2_TIMER_AB+3
lda #CIA_TIMER_CONTROL_START|CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A
sta CIA2_TIMER_B_CONTROL
lda #CIA_TIMER_CONTROL_START
sta CIA2_TIMER_A_CONTROL
rts
}
// Make charset from proto chars
// init_font_hex(byte* zeropage(9) charset)
init_font_hex: {
.label __0 = $19
.label idx = $c
.label proto_lo = $11
.label charset = 9
.label c1 = $b
.label proto_hi = 4
.label c = 6
lda #0
sta.z c
lda #<FONT_HEX_PROTO
sta.z proto_hi
lda #>FONT_HEX_PROTO
sta.z proto_hi+1
lda #<CHARSET
sta.z charset
lda #>CHARSET
sta.z charset+1
__b1:
lda #0
sta.z c1
lda #<FONT_HEX_PROTO
sta.z proto_lo
lda #>FONT_HEX_PROTO
sta.z proto_lo+1
__b2:
lda #0
tay
sta (charset),y
lda #1
sta.z idx
ldx #0
__b3:
txa
tay
lda (proto_hi),y
asl
asl
asl
asl
sta.z __0
txa
tay
lda (proto_lo),y
asl
ora.z __0
ldy.z idx
sta (charset),y
inc.z idx
inx
cpx #5
bne __b3
lda #0
ldy.z idx
sta (charset),y
iny
sta (charset),y
lda #5
clc
adc.z proto_lo
sta.z proto_lo
bcc !+
inc.z proto_lo+1
!:
lda #8
clc
adc.z charset
sta.z charset
bcc !+
inc.z charset+1
!:
inc.z c1
lda #$10
cmp.z c1
bne __b2
lda #5
clc
adc.z proto_hi
sta.z proto_hi
bcc !+
inc.z proto_hi+1
!:
inc.z c
lda #$10
cmp.z c
bne __b1
rts
}
// Bit patterns for symbols 0-f (3x5 pixels) used in font hex
FONT_HEX_PROTO: .byte 2, 5, 5, 5, 2, 6, 2, 2, 2, 7, 6, 1, 2, 4, 7, 6, 1, 2, 1, 6, 5, 5, 7, 1, 1, 7, 4, 6, 1, 6, 3, 4, 6, 5, 2, 7, 1, 1, 1, 1, 2, 5, 2, 5, 2, 2, 5, 3, 1, 1, 2, 5, 7, 5, 5, 6, 5, 6, 5, 6, 2, 5, 4, 5, 2, 6, 5, 5, 5, 6, 7, 4, 6, 4, 7, 7, 4, 6, 4, 4
print_hextab: .text "0123456789abcdef"