TK2048/src/vdp_utils.s

595 lines
No EOL
11 KiB
ArmAsm

.rtmodel version,"1"
.rtmodel core,"6502"
.extern _Zp
.extern VDP_MEM, VDP_REG
.section code,text
;;; vdp_point_to_vram_xy:
;;; Readies the VDP registers to write on a tile at XY
;;; Parameters:
;;; - nametable index in vram [_Zp[4]]
;;; - X coordinate [_Zp[0]]
;;; - Y coordinate [_Zp[1]]
;;;
;;; Returns: nothing
;;;
;;; Clobbers:
;;; - A, Y, X
;;; - Zp 5, 6
;;;
vdp_point_to_vram_xy:
T_VADD_H$: .equ _Zp+6
T_VADD_L$: .equ _Zp+5
P_NT_IDX$: .equ _Zp+4
P_Y$: .equ _Zp+1
P_X$: .equ _Zp+0
lda #0x00
sta zp:T_VADD_H$
lda zp:P_Y$
sta zp:T_VADD_L$
;; Shift Y five times to the left to get the offset for the ROW in VRAM
ldx #5
ShiftOffset$:
asl zp:T_VADD_L$
rol zp:T_VADD_H$
dex
bne ShiftOffset$
clc
; Calculate the VRAM address
lda zp:P_X$
adc zp:T_VADD_L$
sta zp:T_VADD_L$
ldx zp:P_NT_IDX$
lda NameTablesList,x
adc zp:T_VADD_H$
sta zp:T_VADD_H$
; Setup the VDP to write into VRAM
lda zp:T_VADD_L$
sta VDP_REG
lda zp:T_VADD_H$
ora #0x40
sta VDP_REG
rts
;;; vdp_set_tile:
;;; Prints the provided ASCII string at the specified coordinates
;;; Parameters:
;;; - nametable index in vram [A]
;;; - X coordinate [_Zp[0]]
;;; - Y coordinate [_Zp[1]]
;;; - tile index [_Zp[2]]
;;;
;;; Returns: nothing
;;;
;;; Clobbers:
;;; - A, Y, X
;;; - Zp 5, 6
;;;
vdp_set_tile:
T_VADD_H$: .equ _Zp+6
T_VADD_L$: .equ _Zp+5
P_NT_IDX$: .equ _Zp+4
P_TILE$: .equ _Zp+2
P_Y$: .equ _Zp+1
P_X$: .equ _Zp+0
sta zp:P_NT_IDX$
jsr vdp_point_to_vram_xy
lda zp:P_TILE$
sta VDP_MEM
EOS$:
rts
;;; vdp_print_string:
;;; Prints the provided ASCII string at the specified coordinates (newlines and control chars are not supported)
;;; Parameters:
;;; - nametable index in vram [A]
;;; - X coordinate [_Zp[0]]
;;; - Y coordinate [_Zp[1]]
;;; - string address [_Zp[2], _Zp[3]]: Address of the beginning of data to write
;;;
;;; Returns: nothing
;;;
;;; Clobbers:
;;; - A, Y, X
;;; - Zp 5, 6
;;;
vdp_print_string:
T_VADD_H$: .equ _Zp+6
T_VADD_L$: .equ _Zp+5
P_NT_IDX$: .equ _Zp+4
P_DATA_H$: .equ _Zp+3
P_DATA_L$: .equ _Zp+2
P_Y$: .equ _Zp+1
P_X$: .equ _Zp+0
sta zp:P_NT_IDX$
jsr vdp_point_to_vram_xy
ldy #0x00
NextChar$:
lda (zp:P_DATA_L$),y
beq EOS$
sec
sbc #0x20
sta VDP_MEM
iny
bne NextChar$
EOS$:
rts
;;; vdp_write_vram:
;;; Write the provided data into VRAM at specified address
;;; Parameters:
;;; - data address [_Zp[0], _Zp[1]]: Address of the beginning of data to write
;;; - data length [_Zp[2], _Zp[3]]: Length of data to write
;;; - VRAM address [_Zp[4], _Zp[5]]: Start address of data in VRAM
;;;
;;; Returns: nothing
;;;
;;; Clobbers:
;;; - A, Y, X
;;; - Zp 1, 2, 3
;;;
vdp_write_vram:
P_VADD_H$: .equ _Zp+5
P_VADD_L$: .equ _Zp+4
P_DLEN_H$: .equ _Zp+3
P_DLEN_L$: .equ _Zp+2
P_DATA_H$: .equ _Zp+1
P_DATA_L$: .equ _Zp+0
; Decrement length by one, so or the loop would do an off-by-one write
sec
lda zp:P_DLEN_L$
sbc #1
sta zp:P_DLEN_L$
lda zp:P_DLEN_H$
sbc #0
sta zp:P_DLEN_H$
; Setup the VDP to write into VRAM
lda zp:P_VADD_L$
sta VDP_REG
lda zp:P_VADD_H$
ora #0x40
sta VDP_REG
; Actually write data into VRAM
ldx zp:P_DLEN_H$
ldy #0x00
CopyLoop$:
lda (zp:P_DATA_L$),y
sta VDP_MEM
iny
bne SkipHIncr$ ; Check if we did overflow. In case, increment the high byte of the address
inc zp:P_DATA_H$
SkipHIncr$:
dec zp:P_DLEN_L$
lda zp:P_DLEN_L$
cmp #0xFF
bne CopyLoop$
dex
bpl CopyLoop$
rts
;;; vdp_fill_vram:
;;; Fill VRAM with a specific byte
;;; Parameters:
;;; - fill value [A]
;;; - data length [_Zp[0], _Zp[1]]: Length of data to write
;;; - VRAM address [_Zp[2], _Zp[3]]: Start address of data in VRAM
;;;
;;; Returns: nothing
;;;
;;; Clobbers:
;;; - A, Y, X
;;; - Zp 0, 1, 2, 4
;;;
vdp_fill_vram:
T_FILLVAL$: .equ _Zp+4
P_VADD_H$: .equ _Zp+3
P_VADD_L$: .equ _Zp+2
P_DLEN_H$: .equ _Zp+1
P_DLEN_L$: .equ _Zp+0
sta zp:T_FILLVAL$
; Decrement length by one, so or the loop would do an off-by-one write
sec
lda zp:P_DLEN_L$
sbc #1
sta zp:P_DLEN_L$
lda zp:P_DLEN_H$
sbc #0
sta zp:P_DLEN_H$
; Setup the VDP to write into VRAM
lda zp:P_VADD_L$
sta VDP_REG
lda zp:P_VADD_H$
ora #0x40
sta VDP_REG
; Fill the VRAM
ldx zp:P_DLEN_H$
CopyLoop$:
lda zp:T_FILLVAL$
sta VDP_MEM
dec zp:P_DLEN_L$
lda zp:P_DLEN_L$
cmp #0xFF
bne CopyLoop$
dex
bpl CopyLoop$
rts
;;; vdp_detect:
;;; Check if a VDP is present
;;; Parameters: none
;;;
;;; Returns: 0xFF in A if VDP is present, 0 otherwise
;;;
;;; Clobbers:
;;; - A
;;;
vdp_detect:
lda #0x00
sta VDP_REG
lda #0x40
sta VDP_REG
lda #0x55
sta VDP_MEM
lda #0xAA
sta VDP_MEM
lda #0x00
sta VDP_REG
sta VDP_REG
eor VDP_MEM
eor VDP_MEM
cmp #0xFF
beq VdpFound$
lda #0x00
VdpFound$:
rts
;;; vdp_hide_sprites:
;;; Marks all sprites as hidden
;;; Parameters: none
;;;
;;; Returns: nothing
;;;
;;; Clobbers:
;;; - A, Y, X
;;; - Zp 1, 2, 3, 4, 5, 6, 7
;;;
vdp_hide_sprites:
ldy #0
HideLoop$:
tya
jsr vdp_hide_sprite
iny
cpy #25
bne HideLoop$
; Make sure the table gets updated in memory
jsr _vdp_write_interleaved_sat
rts
;;; vdp_hide_sprite:
;;; Mark a single sprite as hidden
;;; Parameters:
;;; - sprite number [A]
;;;
;;; Returns: nothing
;;;
;;; Clobbers:
;;; - A, Y, X
;;;
vdp_hide_sprite:
tay
ldx SAT_RowCol_Trans_Table,y
lda #0xC0
sta SpriteAttributeTable,x
rts
;;; vdp_show_sprite:
;;; Enable a single sprite in the game grid
;;; Parameters:
;;; - sprite number [A]
;;;
;;; Returns: nothing
;;;
;;; Clobbers:
;;; - A, Y, X
;;;
vdp_show_sprite:
tay
ldx SAT_RowCol_Trans_Table,y
lda SAT_Y_Map,y
sta SpriteAttributeTable,x
rts
;;; vdp_set_sprite_tile:
;;; Set the current tile index for a specific sprite
;;; Parameters:
;;; - sprite number [A]
;;; - tile index [_Zp [0]]
;;;
;;; Returns: nothing
;;;
;;; Clobbers:
;;; - A, Y, X
;;;
vdp_set_sprite_tile:
P_TILE_IDX$:.equ _Zp+0
asl zp:P_TILE_IDX$ ; We're using 2x2 tile sprites
asl zp:P_TILE_IDX$
tay
ldx SAT_RowCol_Trans_Table,y
lda zp:P_TILE_IDX$
sta SpriteAttributeTable+2,x
rts
;;; vdp_switch_nt:
;;; Switch shown nametable
;;; Parameters:
;;; - name table index [A]
;;;
;;; Returns: nothing
;;;
;;; Clobbers:
;;; - A, X
;;;
vdp_switch_nt:
tax
lda NameTablesList,x
lsr a
lsr a
sta VDP_REG
lda #0x82
sta VDP_REG
rts
;;; vdp_write_registers:
;;; Initialize the registers of the VDP, using the provided byte array
;;; Parameters:
;;; - data address [_Zp[0], _Zp[1]]: Address of the beginning of initialization data, a 16 byte array
;;;
;;; Returns: nothing
;;;
;;; Clobbers:
;;; - A, Y
;;;
vdp_write_registers:
P_REG_H$: .equ _Zp+1
P_REG_L$: .equ _Zp+0
; Write the register sequence
ldy #0x00
RegLoop$:
lda (zp:P_REG_L$),y
sta VDP_REG
iny
cpy #16
bne RegLoop$
rts
vdp_irq_handler:
lda VDP_REG ; Clear the interrupt
jsr _vdp_write_interleaved_sat
rti
;;; _vdp_write_interleaved_sat:
;;; Updates SAT at 0x300 with sprite multiplexing
;;; Parameters: none
;;;
;;; Returns: nothing
;;;
;;; Clobbers:
;;; - A, Y, X
;;; - Zp 1, 2, 3, 4, 5, 6, 7
;;;
_vdp_write_interleaved_sat:
T_COUNT$: .equ _Zp+7
T_SATB_IDX$: .equ _Zp+6
T_DEST_H$: .equ _Zp+5
T_DEST_L$: .equ _Zp+4
T_DLEN_H$: .equ _Zp+3
T_DLEN_L$: .equ _Zp+2
T_DATA_H$: .equ _Zp+1
T_DATA_L$: .equ _Zp+0
lda #0x00
sta zp:T_DEST_L$
lda #0x03
sta zp:T_DEST_H$
lda CurrentByteTableOffset
cmp #5
bne NoOverflow$
lda #0
sta CurrentByteTableOffset
NoOverflow$:
asl a
sta zp:T_SATB_IDX$
lda #4
sta zp:T_COUNT$
WriteLoop$:
lda #20
sta zp:T_DLEN_L$
lda #00
sta zp:T_DLEN_H$
lda zp:T_SATB_IDX$
tax
lda SAT_Block_Table,x
sta zp:T_DATA_L$
lda SAT_Block_Table+1,x
sta zp:T_DATA_H$
jsr vdp_write_vram
clc
lda zp:T_DEST_L$
adc #20
sta zp:T_DEST_L$
lda zp:T_SATB_IDX$
adc #0x02
sta zp:T_SATB_IDX$
dec zp:T_COUNT$
bpl WriteLoop$
inc CurrentByteTableOffset
rts
;;;;;;;;;;;;;;;;;;
.section data,data
SpriteAttributeTable:
; Organized in columns
SAT_Col1:
.byte 0xC0, 0x08, 0x30, 0x01 ; Col 1
.byte 0xC0, 0x08, 0x30, 0x01
.byte 0xC0, 0x08, 0x30, 0x01
.byte 0xC0, 0x08, 0x30, 0x01
.byte 0xC0, 0x08, 0x30, 0x01
SAT_Col2:
.byte 0xC0, 0x30, 0x30, 0x01 ; Col 2
.byte 0xC0, 0x30, 0x30, 0x01
.byte 0xC0, 0x30, 0x30, 0x01
.byte 0xC0, 0x30, 0x30, 0x01
.byte 0xC0, 0x30, 0x30, 0x01
SAT_Col3:
.byte 0xC0, 0x58, 0x30, 0x01 ; Col 3
.byte 0xC0, 0x58, 0x30, 0x01
.byte 0xC0, 0x58, 0x30, 0x01
.byte 0xC0, 0x58, 0x30, 0x01
.byte 0xC0, 0x58, 0x30, 0x01
SAT_Col4:
.byte 0xC0, 0x80, 0x30, 0x01 ; Col 4
.byte 0xC0, 0x80, 0x30, 0x01
.byte 0xC0, 0x80, 0x30, 0x01
.byte 0xC0, 0x80, 0x30, 0x01
.byte 0xC0, 0x80, 0x30, 0x01
SAT_Col5:
.byte 0xC0, 0xA8, 0x30, 0x01 ; Col 5
.byte 0xC0, 0xA8, 0x30, 0x01
.byte 0xC0, 0xA8, 0x30, 0x01
.byte 0xC0, 0xA8, 0x30, 0x01
.byte 0xC0, 0xA8, 0x30, 0x01
; Unused
.byte 0xD0, 0x00, 0x30, 0x00
.byte 0xD0, 0x00, 0x30, 0x00
.byte 0xD0, 0x00, 0x30, 0x00
.byte 0xD0, 0x00, 0x30, 0x00
.byte 0xD0, 0x00, 0x30, 0x00
.byte 0xD0, 0x00, 0x30, 0x00
.byte 0xD0, 0x00, 0x30, 0x00
SAT_RowCol_Trans_Table:
.byte 0, 20, 40, 60, 80
.byte 4, 24, 44, 64, 84
.byte 8, 28, 48, 68, 88
.byte 12, 32, 52, 72, 92
.byte 16, 36, 56, 76, 96
SAT_Y_Map:
.byte 0x07
.byte 0x07
.byte 0x07
.byte 0x07
.byte 0x07
.byte 0x27
.byte 0x27
.byte 0x27
.byte 0x27
.byte 0x27
.byte 0x47
.byte 0x47
.byte 0x47
.byte 0x47
.byte 0x47
.byte 0x67
.byte 0x67
.byte 0x67
.byte 0x67
.byte 0x67
.byte 0x87
.byte 0x87
.byte 0x87
.byte 0x87
.byte 0x87
SAT_Block_Table:
.word SAT_Col1
.word SAT_Col2
.word SAT_Col3
.word SAT_Col4
.word SAT_Col5
.word SAT_Col1
.word SAT_Col2
.word SAT_Col3
.word SAT_Col4
.word SAT_Col5
; Contains a list of VRAM addresses corresponding to every page
NameTablesList:
NT_P0: .byte 0x00
NT_P1: .byte 0x14
CurrentByteTableOffset:
.byte 0x00
;;;
;;;
;;; Exported symbols
.public vdp_write_vram
.public vdp_detect
.public vdp_write_registers
.public vdp_hide_sprites
.public vdp_hide_sprite
.public vdp_show_sprite
.public vdp_switch_nt
.public vdp_set_sprite_tile
.public vdp_print_string
.public vdp_set_tile
.public vdp_point_to_vram_xy
.public vdp_irq_handler
.public SpriteAttributeTable ; We'll need to set change visibility and values
.public NT_P0, NT_P1