mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-08-07 06:30:04 +00:00
933 lines
20 KiB
NASM
933 lines
20 KiB
NASM
// Filling a simple 16x16 2D polygon using EOR-filling
|
|
// - Clearing canvas
|
|
// - Trivial 2D rotation using sine tables
|
|
// - Line-drawing polygon edges (fill-ready lines)
|
|
// - Up-to-down EOR filling
|
|
// - Double buffering
|
|
.pc = $801 "Basic"
|
|
:BasicUpstart(__bbegin)
|
|
.pc = $80d "Program"
|
|
// Value that disables all CIA interrupts when stored to the CIA Interrupt registers
|
|
.const CIA_INTERRUPT_CLEAR = $7f
|
|
// Timer Control - Start/stop timer (0:stop, 1: start)
|
|
.const CIA_TIMER_CONTROL_START = 1
|
|
// 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
|
|
.const BORDER_YPOS_BOTTOM = $fa
|
|
// Bits for the VICII IRQ Status/Enable Registers
|
|
.const IRQ_RASTER = 1
|
|
// The colors of the C64
|
|
.const BLACK = 0
|
|
.const WHITE = 1
|
|
.const RED = 2
|
|
.const DARK_GREY = $b
|
|
.const OFFSET_STRUCT_MOS6526_CIA_TIMER_A_CONTROL = $e
|
|
.const OFFSET_STRUCT_MOS6526_CIA_TIMER_B_CONTROL = $f
|
|
.const OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR = $20
|
|
.const OFFSET_STRUCT_MOS6569_VICII_BG_COLOR = $21
|
|
.const OFFSET_STRUCT_MOS6526_CIA_INTERRUPT = $d
|
|
.const OFFSET_STRUCT_MOS6569_VICII_CONTROL1 = $11
|
|
.const OFFSET_STRUCT_MOS6569_VICII_RASTER = $12
|
|
.const OFFSET_STRUCT_MOS6569_VICII_IRQ_ENABLE = $1a
|
|
.const OFFSET_STRUCT_MOS6569_VICII_MEMORY = $18
|
|
.const OFFSET_STRUCT_MOS6569_VICII_IRQ_STATUS = $19
|
|
.const toD0181_return = (>(SCREEN&$3fff)*4)|(>CANVAS2)/4&$f
|
|
// The VIC-II MOS 6567/6569
|
|
.label VICII = $d000
|
|
// Color Ram
|
|
.label COLS = $d800
|
|
// The CIA#1: keyboard matrix, joystick #1/#2
|
|
.label CIA1 = $dc00
|
|
// The CIA#2: Serial bus, RS-232, VIC memory bank
|
|
.label CIA2 = $dd00
|
|
// CIA#2 timer A&B as one single 32-bit value
|
|
.label CIA2_TIMER_AB = $dd04
|
|
// The vector used when the KERNAL serves IRQ interrupts
|
|
.label KERNEL_IRQ = $314
|
|
// CIA#1 Port A: keyboard matrix columns and joystick #2
|
|
.label CONIO_CIA1_PORT_A = $dc00
|
|
// CIA#1 Port B: keyboard matrix rows and joystick #1.
|
|
.label CONIO_CIA1_PORT_B = $dc01
|
|
// The line buffer
|
|
.label LINE_BUFFER = $2000
|
|
// The two charsets used as screen buffers
|
|
.label CANVAS1 = $3000
|
|
.label CANVAS2 = $3800
|
|
// The screen matrix
|
|
.label SCREEN = $2c00
|
|
// The screen console
|
|
.label CONSOLE = $400
|
|
// The default charset address
|
|
.label PETSCII = $1000
|
|
.label COSTAB = SINTAB+$40
|
|
.label canvas_show_memory = $11
|
|
.label canvas_show_flag = $12
|
|
__bbegin:
|
|
// canvas_show_memory = toD018(SCREEN, CANVAS2)
|
|
// The current canvas being rendered to the screen - in D018 format.
|
|
lda #toD0181_return
|
|
sta.z canvas_show_memory
|
|
// canvas_show_flag = 0
|
|
// Flag signalling that the canvas on screen needs to be updated.
|
|
// Set to 1 by the renderer when a new canvas is ready for showing, and to 0 by the raster when the canvas is shown on screen.
|
|
lda #0
|
|
sta.z canvas_show_flag
|
|
jsr main
|
|
rts
|
|
main: {
|
|
.const toD0181_return = (>(SCREEN&$3fff)*4)|(>CANVAS1)/4&$f
|
|
.const toD0182_return = (>(SCREEN&$3fff)*4)|(>CANVAS2)/4&$f
|
|
.label cols = 3
|
|
// Setup 16x16 canvas for rendering
|
|
.label screen = 5
|
|
.label y = 2
|
|
.label x0 = $13
|
|
.label y0 = $14
|
|
.label x1 = $c
|
|
.label y1 = $15
|
|
.label x2 = $c
|
|
.label y2 = $16
|
|
.label p0_idx = 7
|
|
.label p1_idx = 8
|
|
.label p2_idx = 9
|
|
// The current canvas being rendered to
|
|
.label canvas = $a
|
|
// memset(CONSOLE, ' ', 40*25)
|
|
// Clear the console
|
|
ldx #' '
|
|
lda #<CONSOLE
|
|
sta.z memset.str
|
|
lda #>CONSOLE
|
|
sta.z memset.str+1
|
|
lda #<$28*$19
|
|
sta.z memset.num
|
|
lda #>$28*$19
|
|
sta.z memset.num+1
|
|
jsr memset
|
|
// memset(SCREEN, 0, 40*25)
|
|
// Clear the screen
|
|
ldx #0
|
|
lda #<SCREEN
|
|
sta.z memset.str
|
|
lda #>SCREEN
|
|
sta.z memset.str+1
|
|
lda #<$28*$19
|
|
sta.z memset.num
|
|
lda #>$28*$19
|
|
sta.z memset.num+1
|
|
jsr memset
|
|
// memset(COLS, BLACK, 40*25)
|
|
ldx #BLACK
|
|
lda #<COLS
|
|
sta.z memset.str
|
|
lda #>COLS
|
|
sta.z memset.str+1
|
|
lda #<$28*$19
|
|
sta.z memset.num
|
|
lda #>$28*$19
|
|
sta.z memset.num+1
|
|
jsr memset
|
|
lda #<SCREEN+$c
|
|
sta.z screen
|
|
lda #>SCREEN+$c
|
|
sta.z screen+1
|
|
lda #<COLS+$c
|
|
sta.z cols
|
|
lda #>COLS+$c
|
|
sta.z cols+1
|
|
lda #0
|
|
sta.z y
|
|
__b1:
|
|
// for(char y=0;y<16;y++)
|
|
lda.z y
|
|
cmp #$10
|
|
bcs !__b2+
|
|
jmp __b2
|
|
!__b2:
|
|
// VICII->BORDER_COLOR = BLACK
|
|
lda #BLACK
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR
|
|
// VICII->BG_COLOR = BLACK
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_BG_COLOR
|
|
// setup_irq()
|
|
// Set-up the raster IRQ
|
|
jsr setup_irq
|
|
// textcolor(WHITE)
|
|
// Set text color
|
|
jsr textcolor
|
|
lda #<CANVAS1
|
|
sta.z canvas
|
|
lda #>CANVAS1
|
|
sta.z canvas+1
|
|
lda #$b5+$aa
|
|
sta.z p2_idx
|
|
lda #$b5+$f
|
|
sta.z p1_idx
|
|
lda #$b5
|
|
sta.z p0_idx
|
|
__b8:
|
|
// clock_start()
|
|
jsr clock_start
|
|
// memset(LINE_BUFFER, 0, 0x0800)
|
|
// Clear line buffer
|
|
ldx #0
|
|
lda #<LINE_BUFFER
|
|
sta.z memset.str
|
|
lda #>LINE_BUFFER
|
|
sta.z memset.str+1
|
|
lda #<$800
|
|
sta.z memset.num
|
|
lda #>$800
|
|
sta.z memset.num+1
|
|
jsr memset
|
|
// x0 = COSTAB[p0_idx]
|
|
// Plot in line buffer
|
|
ldy.z p0_idx
|
|
lda COSTAB,y
|
|
sta.z x0
|
|
// y0 = SINTAB[p0_idx]
|
|
lda SINTAB,y
|
|
sta.z y0
|
|
// x1 = COSTAB[p1_idx]
|
|
ldy.z p1_idx
|
|
lda COSTAB,y
|
|
sta.z x1
|
|
// y1 = SINTAB[p1_idx]
|
|
lda SINTAB,y
|
|
sta.z y1
|
|
// line(LINE_BUFFER, x0, y0, x1, y1)
|
|
lda.z x0
|
|
sta.z line.x1
|
|
lda.z y0
|
|
sta.z line.y1
|
|
lda.z y1
|
|
sta.z line.y2
|
|
jsr line
|
|
// x2 = COSTAB[p2_idx]
|
|
ldy.z p2_idx
|
|
lda COSTAB,y
|
|
sta.z x2
|
|
// y2 = SINTAB[p2_idx]
|
|
lda SINTAB,y
|
|
sta.z y2
|
|
// line(LINE_BUFFER, x1, y1, x2, y2)
|
|
lda.z x1
|
|
sta.z line.x1
|
|
lda.z y1
|
|
sta.z line.y1
|
|
lda.z y2
|
|
sta.z line.y2
|
|
jsr line
|
|
// line(LINE_BUFFER, x2, y2, x0, y0)
|
|
lda.z x2
|
|
sta.z line.x1
|
|
lda.z y2
|
|
sta.z line.y1
|
|
lda.z x0
|
|
sta.z line.x2
|
|
lda.z y0
|
|
sta.z line.y2
|
|
jsr line
|
|
// p0_idx++;
|
|
inc.z p0_idx
|
|
// p1_idx++;
|
|
inc.z p1_idx
|
|
// p2_idx++;
|
|
inc.z p2_idx
|
|
// VICII->BORDER_COLOR = RED
|
|
// Wait until the canvas on screen has been switched before starting work on the next frame
|
|
lda #RED
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR
|
|
__b9:
|
|
// while(canvas_show_flag)
|
|
lda #0
|
|
cmp.z canvas_show_flag
|
|
bne __b9
|
|
// VICII->BORDER_COLOR = BLACK
|
|
lda #BLACK
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR
|
|
// eorfill(LINE_BUFFER, canvas)
|
|
lda.z canvas
|
|
sta.z eorfill.canvas
|
|
lda.z canvas+1
|
|
sta.z eorfill.canvas+1
|
|
// Fill canvas
|
|
jsr eorfill
|
|
// canvas ^= (CANVAS1^CANVAS2)
|
|
// swap canvas being rendered to (using XOR)
|
|
lda #<CANVAS1^CANVAS2
|
|
eor.z canvas
|
|
sta.z canvas
|
|
lda #>CANVAS1^CANVAS2
|
|
eor.z canvas+1
|
|
sta.z canvas+1
|
|
// canvas_show_memory ^= toD018(SCREEN,CANVAS1)^toD018(SCREEN,CANVAS2)
|
|
// Swap canvas to show on screen (using XOR)
|
|
lda #toD0181_return^toD0182_return
|
|
eor.z canvas_show_memory
|
|
sta.z canvas_show_memory
|
|
// canvas_show_flag = 1
|
|
// Set flag used to signal when the canvas has been shown
|
|
lda #1
|
|
sta.z canvas_show_flag
|
|
// clock()
|
|
jsr clock
|
|
jmp __b8
|
|
__b2:
|
|
ldx.z y
|
|
ldy #0
|
|
__b4:
|
|
// for(char x=0;x<16;x++)
|
|
cpy #$10
|
|
bcc __b5
|
|
// cols += 40
|
|
lda #$28
|
|
clc
|
|
adc.z cols
|
|
sta.z cols
|
|
bcc !+
|
|
inc.z cols+1
|
|
!:
|
|
// screen += 40
|
|
lda #$28
|
|
clc
|
|
adc.z screen
|
|
sta.z screen
|
|
bcc !+
|
|
inc.z screen+1
|
|
!:
|
|
// for(char y=0;y<16;y++)
|
|
inc.z y
|
|
jmp __b1
|
|
__b5:
|
|
// cols[x] = WHITE
|
|
lda #WHITE
|
|
sta (cols),y
|
|
// screen[x] = c
|
|
txa
|
|
sta (screen),y
|
|
// c+=0x10
|
|
txa
|
|
axs #-[$10]
|
|
// for(char x=0;x<16;x++)
|
|
iny
|
|
jmp __b4
|
|
}
|
|
// 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: {
|
|
// }
|
|
rts
|
|
}
|
|
// EOR fill from the line buffer onto the canvas
|
|
// eorfill(byte* zp($1f) canvas)
|
|
eorfill: {
|
|
.label canvas = $1f
|
|
.label line_column = $1c
|
|
.label fill_column = $1f
|
|
lda #<LINE_BUFFER
|
|
sta.z line_column
|
|
lda #>LINE_BUFFER
|
|
sta.z line_column+1
|
|
ldx #0
|
|
__b1:
|
|
// for(char x=0;x<16;x++)
|
|
cpx #$10
|
|
bcc __b2
|
|
// }
|
|
rts
|
|
__b2:
|
|
// eor = line_column[0]
|
|
ldy #0
|
|
lda (line_column),y
|
|
// fill_column[0] = eor
|
|
sta (fill_column),y
|
|
ldy #1
|
|
__b3:
|
|
// for(char y=1;y<16*8;y++)
|
|
cpy #$10*8
|
|
bcc __b4
|
|
// line_column += 16*8
|
|
lda #$10*8
|
|
clc
|
|
adc.z line_column
|
|
sta.z line_column
|
|
bcc !+
|
|
inc.z line_column+1
|
|
!:
|
|
// fill_column += 16*8
|
|
lda #$10*8
|
|
clc
|
|
adc.z fill_column
|
|
sta.z fill_column
|
|
bcc !+
|
|
inc.z fill_column+1
|
|
!:
|
|
// for(char x=0;x<16;x++)
|
|
inx
|
|
jmp __b1
|
|
__b4:
|
|
// eor ^= line_column[y]
|
|
eor (line_column),y
|
|
// fill_column[y] = eor
|
|
sta (fill_column),y
|
|
// for(char y=1;y<16*8;y++)
|
|
iny
|
|
jmp __b3
|
|
}
|
|
// Draw a EOR friendly line between two points
|
|
// Uses bresenham line drawing routine
|
|
// line(byte zp($f) x1, byte zp($10) y1, byte zp($c) x2, byte zp($d) y2)
|
|
line: {
|
|
.label plot2___1 = $1e
|
|
.label plot5___1 = $27
|
|
.label x1 = $f
|
|
.label y1 = $10
|
|
.label x2 = $c
|
|
.label y2 = $d
|
|
.label x = $f
|
|
.label y = $10
|
|
.label dx = $17
|
|
.label dy = $18
|
|
.label sx = $19
|
|
.label sy = $1a
|
|
.label plot1_column = $21
|
|
.label plot2_y = $1b
|
|
.label plot2_column = $1c
|
|
.label plot3_column = $1f
|
|
.label e1 = $e
|
|
.label plot4_column = $23
|
|
.label plot5_column = $25
|
|
.label plot6_column = $28
|
|
// abs_u8(x2-x1)
|
|
lda.z x2
|
|
sec
|
|
sbc.z x
|
|
jsr abs_u8
|
|
// abs_u8(x2-x1)
|
|
// dx = abs_u8(x2-x1)
|
|
sta.z dx
|
|
// abs_u8(y2-y1)
|
|
lda.z y2
|
|
sec
|
|
sbc.z y
|
|
jsr abs_u8
|
|
// abs_u8(y2-y1)
|
|
// dy = abs_u8(y2-y1)
|
|
sta.z dy
|
|
// sgn_u8(x2-x1)
|
|
lda.z x2
|
|
sec
|
|
sbc.z x
|
|
jsr sgn_u8
|
|
// sgn_u8(x2-x1)
|
|
// sx = sgn_u8(x2-x1)
|
|
sta.z sx
|
|
// sgn_u8(y2-y1)
|
|
lda.z y2
|
|
sec
|
|
sbc.z y
|
|
jsr sgn_u8
|
|
// sgn_u8(y2-y1)
|
|
// sy = sgn_u8(y2-y1)
|
|
sta.z sy
|
|
// if(sx==0xff)
|
|
lda #$ff
|
|
cmp.z sx
|
|
bne __b1
|
|
// y++;
|
|
inc.z y
|
|
// y2++;
|
|
inc.z y2
|
|
__b1:
|
|
// if(dx > dy)
|
|
lda.z dy
|
|
cmp.z dx
|
|
bcs !__b2+
|
|
jmp __b2
|
|
!__b2:
|
|
// if(sx==sy)
|
|
// Steep slope - Y is the driver - only plot one plot per X
|
|
lda.z sx
|
|
cmp.z sy
|
|
beq plot1
|
|
// e = dy/2
|
|
lda.z dy
|
|
lsr
|
|
tax
|
|
__b6:
|
|
// y += sy
|
|
lda.z y
|
|
clc
|
|
adc.z sy
|
|
sta.z y
|
|
// e += dx
|
|
txa
|
|
clc
|
|
adc.z dx
|
|
tax
|
|
// if(e>dy)
|
|
lda.z dy
|
|
stx.z $ff
|
|
cmp.z $ff
|
|
bcs __b7
|
|
// plot(x, y-sy)
|
|
lda.z y
|
|
sec
|
|
sbc.z sy
|
|
sta.z plot2_y
|
|
// x/8
|
|
lda.z x
|
|
lsr
|
|
lsr
|
|
lsr
|
|
// column = plot_column[x/8]
|
|
asl
|
|
tay
|
|
lda plot_column,y
|
|
sta.z plot2_column
|
|
lda plot_column+1,y
|
|
sta.z plot2_column+1
|
|
// x&7
|
|
lda #7
|
|
and.z x
|
|
sta.z plot2___1
|
|
// column[y] |= plot_bit[x&7]
|
|
ldy.z plot2_y
|
|
lda (plot2_column),y
|
|
ldy.z plot2___1
|
|
ora plot_bit,y
|
|
ldy.z plot2_y
|
|
sta (plot2_column),y
|
|
// x += sx
|
|
lda.z x
|
|
clc
|
|
adc.z sx
|
|
sta.z x
|
|
// e -= dy
|
|
txa
|
|
sec
|
|
sbc.z dy
|
|
tax
|
|
__b7:
|
|
// while (y != y2)
|
|
lda.z y
|
|
cmp.z y2
|
|
bne __b6
|
|
// x/8
|
|
lda.z x
|
|
lsr
|
|
lsr
|
|
lsr
|
|
// column = plot_column[x/8]
|
|
asl
|
|
tay
|
|
lda plot_column,y
|
|
sta.z plot3_column
|
|
lda plot_column+1,y
|
|
sta.z plot3_column+1
|
|
// x&7
|
|
lda #7
|
|
and.z x
|
|
// column[y] |= plot_bit[x&7]
|
|
ldy.z y
|
|
tax
|
|
lda (plot3_column),y
|
|
ora plot_bit,x
|
|
sta (plot3_column),y
|
|
// }
|
|
rts
|
|
plot1:
|
|
// x/8
|
|
lda.z x
|
|
lsr
|
|
lsr
|
|
lsr
|
|
// column = plot_column[x/8]
|
|
asl
|
|
tay
|
|
lda plot_column,y
|
|
sta.z plot1_column
|
|
lda plot_column+1,y
|
|
sta.z plot1_column+1
|
|
// x&7
|
|
lda #7
|
|
and.z x
|
|
// column[y] |= plot_bit[x&7]
|
|
ldy.z y
|
|
tax
|
|
lda (plot1_column),y
|
|
ora plot_bit,x
|
|
sta (plot1_column),y
|
|
// if(dx==0)
|
|
lda.z dx
|
|
cmp #0
|
|
bne __b9
|
|
rts
|
|
__b9:
|
|
// e = dy/2
|
|
lda.z dy
|
|
lsr
|
|
sta.z e1
|
|
__b10:
|
|
// y += sy
|
|
lda.z y
|
|
clc
|
|
adc.z sy
|
|
sta.z y
|
|
// e += dx
|
|
lda.z e1
|
|
clc
|
|
adc.z dx
|
|
sta.z e1
|
|
// while(e<=dy)
|
|
lda.z dy
|
|
cmp.z e1
|
|
bcs __b10
|
|
// x += sx
|
|
lda.z x
|
|
clc
|
|
adc.z sx
|
|
sta.z x
|
|
// e -= dy
|
|
lda.z e1
|
|
sec
|
|
sbc.z dy
|
|
sta.z e1
|
|
// x/8
|
|
lda.z x
|
|
lsr
|
|
lsr
|
|
lsr
|
|
// column = plot_column[x/8]
|
|
asl
|
|
tay
|
|
lda plot_column,y
|
|
sta.z plot4_column
|
|
lda plot_column+1,y
|
|
sta.z plot4_column+1
|
|
// x&7
|
|
lda #7
|
|
and.z x
|
|
// column[y] |= plot_bit[x&7]
|
|
ldy.z y
|
|
tax
|
|
lda (plot4_column),y
|
|
ora plot_bit,x
|
|
sta (plot4_column),y
|
|
// while (x != x2)
|
|
lda.z x
|
|
cmp.z x2
|
|
bne __b10
|
|
rts
|
|
__b2:
|
|
// e = dx/2
|
|
lda.z dx
|
|
lsr
|
|
tax
|
|
plot5:
|
|
// x/8
|
|
lda.z x
|
|
lsr
|
|
lsr
|
|
lsr
|
|
// column = plot_column[x/8]
|
|
asl
|
|
tay
|
|
lda plot_column,y
|
|
sta.z plot5_column
|
|
lda plot_column+1,y
|
|
sta.z plot5_column+1
|
|
// x&7
|
|
lda #7
|
|
and.z x
|
|
sta.z plot5___1
|
|
// column[y] |= plot_bit[x&7]
|
|
ldy.z y
|
|
lda (plot5_column),y
|
|
ldy.z plot5___1
|
|
ora plot_bit,y
|
|
ldy.z y
|
|
sta (plot5_column),y
|
|
// x += sx
|
|
lda.z x
|
|
clc
|
|
adc.z sx
|
|
sta.z x
|
|
// e += dy
|
|
txa
|
|
clc
|
|
adc.z dy
|
|
tax
|
|
// if(e>dx)
|
|
lda.z dx
|
|
stx.z $ff
|
|
cmp.z $ff
|
|
bcs __b13
|
|
// y += sy
|
|
tya
|
|
clc
|
|
adc.z sy
|
|
sta.z y
|
|
// e -= dx
|
|
txa
|
|
sec
|
|
sbc.z dx
|
|
tax
|
|
__b13:
|
|
// while (x != x2)
|
|
lda.z x
|
|
cmp.z x2
|
|
bne plot5
|
|
// x/8
|
|
lsr
|
|
lsr
|
|
lsr
|
|
// column = plot_column[x/8]
|
|
asl
|
|
tay
|
|
lda plot_column,y
|
|
sta.z plot6_column
|
|
lda plot_column+1,y
|
|
sta.z plot6_column+1
|
|
// x&7
|
|
lda #7
|
|
and.z x
|
|
// column[y] |= plot_bit[x&7]
|
|
ldy.z y
|
|
tax
|
|
lda (plot6_column),y
|
|
ora plot_bit,x
|
|
sta (plot6_column),y
|
|
rts
|
|
}
|
|
// Get the sign of a 8-bit unsigned number treated as a signed number.
|
|
// Returns unsigned -1 if the number is negative
|
|
// sgn_u8(byte register(A) u)
|
|
sgn_u8: {
|
|
// u & 0x80
|
|
and #$80
|
|
// if(u & 0x80)
|
|
cmp #0
|
|
bne __b1
|
|
lda #1
|
|
rts
|
|
__b1:
|
|
lda #-1
|
|
// }
|
|
rts
|
|
}
|
|
// Get the absolute value of a u-bit unsigned number treated as a signed number.
|
|
// abs_u8(byte register(A) u)
|
|
abs_u8: {
|
|
// u & 0x80
|
|
ldx #$80
|
|
axs #0
|
|
// if(u & 0x80)
|
|
cpx #0
|
|
bne __b1
|
|
rts
|
|
__b1:
|
|
// return -u;
|
|
eor #$ff
|
|
clc
|
|
adc #1
|
|
// }
|
|
rts
|
|
}
|
|
// Copies the character c (an unsigned char) to the first num characters of the object pointed to by the argument str.
|
|
// memset(void* zp($1f) str, byte register(X) c, word zp($1c) num)
|
|
memset: {
|
|
.label end = $1c
|
|
.label dst = $1f
|
|
.label num = $1c
|
|
.label str = $1f
|
|
// if(num>0)
|
|
lda.z num
|
|
bne !+
|
|
lda.z num+1
|
|
beq __breturn
|
|
!:
|
|
// end = (char*)str + num
|
|
lda.z end
|
|
clc
|
|
adc.z str
|
|
sta.z end
|
|
lda.z end+1
|
|
adc.z str+1
|
|
sta.z end+1
|
|
__b2:
|
|
// for(char* dst = str; dst!=end; dst++)
|
|
lda.z dst+1
|
|
cmp.z end+1
|
|
bne __b3
|
|
lda.z dst
|
|
cmp.z end
|
|
bne __b3
|
|
__breturn:
|
|
// }
|
|
rts
|
|
__b3:
|
|
// *dst = c
|
|
txa
|
|
ldy #0
|
|
sta (dst),y
|
|
// for(char* dst = str; dst!=end; dst++)
|
|
inc.z dst
|
|
bne !+
|
|
inc.z dst+1
|
|
!:
|
|
jmp __b2
|
|
}
|
|
// 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: {
|
|
// CIA2->TIMER_A_CONTROL = CIA_TIMER_CONTROL_STOP | CIA_TIMER_CONTROL_CONTINUOUS | CIA_TIMER_CONTROL_A_COUNT_CYCLES
|
|
// Setup CIA#2 timer A to count (down) CPU cycles
|
|
lda #0
|
|
sta CIA2+OFFSET_STRUCT_MOS6526_CIA_TIMER_A_CONTROL
|
|
// CIA2->TIMER_B_CONTROL = CIA_TIMER_CONTROL_STOP | CIA_TIMER_CONTROL_CONTINUOUS | CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A
|
|
lda #CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A
|
|
sta CIA2+OFFSET_STRUCT_MOS6526_CIA_TIMER_B_CONTROL
|
|
// *CIA2_TIMER_AB = 0xffffffff
|
|
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
|
|
// CIA2->TIMER_B_CONTROL = CIA_TIMER_CONTROL_START | CIA_TIMER_CONTROL_CONTINUOUS | CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A
|
|
lda #CIA_TIMER_CONTROL_START|CIA_TIMER_CONTROL_B_COUNT_UNDERFLOW_A
|
|
sta CIA2+OFFSET_STRUCT_MOS6526_CIA_TIMER_B_CONTROL
|
|
// CIA2->TIMER_A_CONTROL = CIA_TIMER_CONTROL_START | CIA_TIMER_CONTROL_CONTINUOUS | CIA_TIMER_CONTROL_A_COUNT_CYCLES
|
|
lda #CIA_TIMER_CONTROL_START
|
|
sta CIA2+OFFSET_STRUCT_MOS6526_CIA_TIMER_A_CONTROL
|
|
// }
|
|
rts
|
|
}
|
|
// Set the color for text output. The old color setting is returned.
|
|
textcolor: {
|
|
rts
|
|
}
|
|
// Setup raster IRQ to change charset at different lines
|
|
setup_irq: {
|
|
// asm
|
|
sei
|
|
// CIA1->INTERRUPT = CIA_INTERRUPT_CLEAR
|
|
// Disable CIA 1 Timer IRQ
|
|
lda #CIA_INTERRUPT_CLEAR
|
|
sta CIA1+OFFSET_STRUCT_MOS6526_CIA_INTERRUPT
|
|
// VICII->CONTROL1 &= 0x7f
|
|
// Set raster line to 8 pixels before the border
|
|
lda #$7f
|
|
and VICII+OFFSET_STRUCT_MOS6569_VICII_CONTROL1
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_CONTROL1
|
|
// VICII->RASTER = BORDER_YPOS_BOTTOM-8
|
|
lda #BORDER_YPOS_BOTTOM-8
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_RASTER
|
|
// VICII->IRQ_ENABLE = IRQ_RASTER
|
|
// Enable Raster Interrupt
|
|
lda #IRQ_RASTER
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_IRQ_ENABLE
|
|
// *KERNEL_IRQ = &irq_bottom_1
|
|
// Set the IRQ routine
|
|
lda #<irq_bottom_1
|
|
sta KERNEL_IRQ
|
|
lda #>irq_bottom_1
|
|
sta KERNEL_IRQ+1
|
|
// asm
|
|
cli
|
|
// }
|
|
rts
|
|
}
|
|
// Interrupt Routine 2
|
|
irq_bottom_2: {
|
|
.const toD0181_return = (>(SCREEN&$3fff)*4)|(>LINE_BUFFER)/4&$f
|
|
// VICII->BORDER_COLOR = BLACK
|
|
// Change border color
|
|
lda #BLACK
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR
|
|
// kbhit()
|
|
jsr kbhit
|
|
// if(!kbhit())
|
|
// Show the current canvas (unless a key is being pressed)
|
|
cmp #0
|
|
beq __b1
|
|
// VICII->MEMORY = toD018(SCREEN, LINE_BUFFER)
|
|
lda #toD0181_return
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_MEMORY
|
|
__b2:
|
|
// canvas_show_flag = 0
|
|
lda #0
|
|
sta.z canvas_show_flag
|
|
// VICII->IRQ_STATUS = IRQ_RASTER
|
|
// Acknowledge the IRQ
|
|
lda #IRQ_RASTER
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_IRQ_STATUS
|
|
// VICII->RASTER = BORDER_YPOS_BOTTOM-8
|
|
// Trigger IRQ 1 at 8 pixels before the border
|
|
lda #BORDER_YPOS_BOTTOM-8
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_RASTER
|
|
// *KERNEL_IRQ = &irq_bottom_1
|
|
lda #<irq_bottom_1
|
|
sta KERNEL_IRQ
|
|
lda #>irq_bottom_1
|
|
sta KERNEL_IRQ+1
|
|
// }
|
|
jmp $ea31
|
|
__b1:
|
|
// VICII->MEMORY = canvas_show_memory
|
|
lda.z canvas_show_memory
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_MEMORY
|
|
jmp __b2
|
|
}
|
|
// Return true if there's a key waiting, return false if not
|
|
kbhit: {
|
|
// *CONIO_CIA1_PORT_A = 0
|
|
lda #0
|
|
sta CONIO_CIA1_PORT_A
|
|
// ~*CONIO_CIA1_PORT_B
|
|
lda CONIO_CIA1_PORT_B
|
|
eor #$ff
|
|
// }
|
|
rts
|
|
}
|
|
// Interrupt Routine 1: Just above last text line.
|
|
irq_bottom_1: {
|
|
.const toD0181_return = (>(CONSOLE&$3fff)*4)|(>PETSCII)/4&$f
|
|
// VICII->BORDER_COLOR = DARK_GREY
|
|
// Change border color
|
|
lda #DARK_GREY
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_BORDER_COLOR
|
|
// VICII->MEMORY = toD018(CONSOLE, PETSCII)
|
|
// Show the cycle counter
|
|
lda #toD0181_return
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_MEMORY
|
|
// VICII->IRQ_STATUS = IRQ_RASTER
|
|
// Acknowledge the IRQ
|
|
lda #IRQ_RASTER
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_IRQ_STATUS
|
|
// VICII->RASTER = BORDER_YPOS_BOTTOM
|
|
// Trigger IRQ 2 at bottom of text-line
|
|
lda #BORDER_YPOS_BOTTOM
|
|
sta VICII+OFFSET_STRUCT_MOS6569_VICII_RASTER
|
|
// *KERNEL_IRQ = &irq_bottom_2
|
|
lda #<irq_bottom_2
|
|
sta KERNEL_IRQ
|
|
lda #>irq_bottom_2
|
|
sta KERNEL_IRQ+1
|
|
// }
|
|
jmp $ea81
|
|
}
|
|
// SIN/COS tables
|
|
.align $100
|
|
SINTAB:
|
|
.fill $200, round(63 + 63*sin(i*2*PI/$100))
|
|
|
|
// Column offsets
|
|
plot_column: .word LINE_BUFFER, LINE_BUFFER+1*$80, LINE_BUFFER+2*$80, LINE_BUFFER+3*$80, LINE_BUFFER+4*$80, LINE_BUFFER+5*$80, LINE_BUFFER+6*$80, LINE_BUFFER+7*$80, LINE_BUFFER+8*$80, LINE_BUFFER+9*$80, LINE_BUFFER+$a*$80, LINE_BUFFER+$b*$80, LINE_BUFFER+$c*$80, LINE_BUFFER+$d*$80, LINE_BUFFER+$e*$80, LINE_BUFFER+$f*$80
|
|
// The bits used for plotting a pixel
|
|
plot_bit: .byte $80, $40, $20, $10, 8, 4, 2, 1
|