Add initializer module for VDP

This commit is contained in:
hkz 2025-10-12 12:15:23 +02:00
commit d1dcdd1381
18 changed files with 1008 additions and 7 deletions

View file

@ -19,7 +19,7 @@ LIBS=clib-6502.a
MASTER_ASM_SRCS = tk2k_startup_master.s disk2.s master_func.s
MASTER_C_SRCS = master_main.c dos_floppy.c utility.c
INTRO_ASM_SRCS = tk2k_startup_module.s preserve_zero_pages.s
INTRO_ASM_SRCS = tk2k_startup_module.s preserve_zero_pages.s vdp.s vdp_utils.s
INTRO_C_SRCS = intro_main.c utility.c
DLOG_ASM_SRCS = tk2k_startup_module.s preserve_zero_pages.s sound.s
@ -31,12 +31,17 @@ GAME_C_SRCS = game_main.c input.c utility.c game_hgr_graphics.c hgr_line_data.c
DEMO_ASM_SRCS = tk2k_startup_module.s preserve_zero_pages.s input_asm.s sound.s
DEMO_C_SRCS = demo_main.c input.c utility.c game_hgr_graphics_demo.c hgr_line_data.c game_logic.c arrows_pic.c tiles.c hgr_graph_misc_data.c
VDPIN_ASM_SRCS = tk2k_startup_module.s preserve_zero_pages.s vdp.s vdp_utils.s vdp_init.s
VDPIN_C_SRCS = vdpin_main.c utility.c
# Object files
MASTER_OBJS = $(MASTER_ASM_SRCS:%.s=%.o) $(MASTER_C_SRCS:%.c=%.o)
INTRO_OBJS = $(INTRO_ASM_SRCS:%.s=%.o) $(INTRO_C_SRCS:%.c=%.o)
DLOG_OBJS = $(DLOG_ASM_SRCS:%.s=%.o) $(DLOG_C_SRCS:%.c=%.o)
GAME_OBJS = $(GAME_ASM_SRCS:%.s=%.o) $(GAME_C_SRCS:%.c=%.o)
DEMO_OBJS = $(DEMO_ASM_SRCS:%.s=%.o) $(DEMO_C_SRCS:%.c=%.o)
VDPIN_OBJS = $(VDPIN_ASM_SRCS:%.s=%.o) $(VDPIN_C_SRCS:%.c=%.o)
all: $(SW_NAME).woz
@ -61,6 +66,9 @@ $(GAME_PRG).hex: $(GAME_OBJS)
$(DEMO_PRG).hex: $(DEMO_OBJS)
(cd obj ; ln6502 -g ../linker-files/module.scm $^ -o ../out/$@ $(LIBS) -l --cross-reference --cstartup=tk2k --no-automatic-placement-rules --output-format intel-hex --rom-code)
$(VDPIN_PRG).hex: $(VDPIN_OBJS)
(cd obj ; ln6502 -g ../linker-files/vdpin_module.scm $^ -o ../out/$@ $(LIBS) -l --cross-reference --cstartup=tk2k --no-automatic-placement-rules --output-format intel-hex --rom-code)
$(MASTER_PRG).bin: $(MASTER_PRG).hex
(cd out ; objcopy -I ihex -O binary $(MASTER_PRG).hex $(MASTER_PRG).bin)
@ -75,14 +83,18 @@ $(GAME_PRG).bin: $(GAME_PRG).hex
$(DEMO_PRG).bin: $(DEMO_PRG).hex
(cd out ; objcopy -I ihex -O binary $(DEMO_PRG).hex $(DEMO_PRG).bin)
$(SW_NAME).dsk: $(MASTER_PRG).bin $(INTRO_PRG).bin $(DLOG_PRG).bin $(GAME_PRG).bin $(DEMO_PRG).bin
$(VDPIN_PRG).bin: $(VDPIN_PRG).hex
(cd out ; objcopy -I ihex -O binary $(VDPIN_PRG).hex $(VDPIN_PRG).bin)
$(SW_NAME).dsk: $(MASTER_PRG).bin $(INTRO_PRG).bin $(DLOG_PRG).bin $(GAME_PRG).bin $(DEMO_PRG).bin $(VDPIN_PRG).bin
(cd out ; cp ../dsk/TK2048_AUTO_BRUN.dsk ./$(SW_NAME).dsk; \
cat $(MASTER_PRG).bin | $(JAVA) -jar $(ACMD) -p $(SW_NAME).dsk HELLO B 0x800; \
cat $(INTRO_PRG).bin | $(JAVA) -jar $(ACMD) -p $(SW_NAME).dsk INTRO b; \
cat $(DLOG_PRG).bin | $(JAVA) -jar $(ACMD) -p $(SW_NAME).dsk DLOG b; \
cat $(GAME_PRG).bin | $(JAVA) -jar $(ACMD) -p $(SW_NAME).dsk GAME b; \
cat $(DEMO_PRG).bin | $(JAVA) -jar $(ACMD) -p $(SW_NAME).dsk DEMO b; \
cat $(VDPIN_PRG).bin | $(JAVA) -jar $(ACMD) -p $(SW_NAME).dsk VDPIN b; \
cat ../data/LOADS.bin | $(JAVA) -jar $(ACMD) -p $(SW_NAME).dsk LOADS b; \
cat ../data/STATE.bin | $(JAVA) -jar $(ACMD) -p $(SW_NAME).dsk STATE b;)

BIN
data/vdp_charset.bin Normal file

Binary file not shown.

BIN
data/vdp_colortable.bin Normal file

Binary file not shown.

BIN
data/vdp_sprite_tiles.bin Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
(define memories
'((memory zeroPage (address (#x56 . #xff)) (type ram)
(section registers zpage zzpage))
(memory firstPage (address (#x100 . #x1ff)) (section stack))
(memory reserved (address (#x200 . #x1fff)) (type ram))
(memory displayPage1 (address (#x2000 . #x3fff)) (type ram))
(memory upperProg (address (#x4000 . #x7fff)) (type ram) (section (programStart #x4000) startup code switch idata cdata data_init_table))
(memory upperData (address (#x8000 . #x99ff)) (type ram) (section cstack zdata data heap))
(memory sharedMem (address (#x9a00 . #x9bff)) (type ram)) ;;; This memory page will be used to pass parameters and data between the master and the modules, and to save the game state
(memory diskBuffer (address (#x9c00 . #x9eff)) (type ram))
(memory zeroPageBackup (address (#x9f00 . #x9fff)) (type ram) (section (zpsave #x9f00)))
(memory displayPage2 (address (#xa000 . #xbfff)) (type ram))
(memory io (address (#xc000 . #xc0ff)) (type ram))
(memory rombank (address (#xc100 . #xffff)) (type rom))
(block cstack (size #x400))
(block heap (size #x020))
(block stack (size #x100))
))

9
src/game_vdp_graphics.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef _GAME_VDP_GRAPHICS_HEADER_
#define _GAME_VDP_GRAPHICS_HEADER_
#include <stdint.h>
void vdp_clear_gamegrid(void);
uint8_t vdp_draw_numtile(uint8_t type, uint8_t x, uint8_t y);
#endif /* _GAME_VDP_GRAPHICS_HEADER_ */

127
src/game_vdp_graphics.s Normal file
View file

@ -0,0 +1,127 @@
.rtmodel version,"1"
.rtmodel core,"6502"
.extern _Zp
.extern VDP_MEM, VDP_REG
.extern vdp_point_to_vram_xy
NUM_TILE_OFFSET: .equ 0x14
.section code,text
vdp_clear_gamegrid:
T_NT_IDX$: .equ _Zp+4
T_Y$: .equ _Zp+1
T_X$: .equ _Zp+0
lda #0x00
sta zp:T_NT_IDX$
lda #0x01
sta zp:T_X$
lda #19
sta zp:T_Y$
ClearLine$:
jsr vdp_point_to_vram_xy
lda #0x00
ldx #24
ClearTile$:
sta VDP_MEM
dex
bne ClearTile$
dec zp:T_Y$
bne ClearLine$
rts
;;; vdp_draw_numtile:
;;; Draws the 2048 tile at specified coordinates
;;; Parameters:
;;; - Type of tile [A]
;;; - X in game grid [Zp[0]]
;;; - Y in game grid [Zp[1]]
;;;
;;; Returns: nothing
;;;
;;; Clobbers:
;;; - A, Y, X
;;; - Zp 0, 1, 2, 3, 4, 5, 6, 7
;;;
vdp_draw_numtile:
T_LINE_COUNT$:.equ _Zp+7
T_NT_IDX$: .equ _Zp+4
T_COUNTER$: .equ _Zp+3
P_TYPE$: .equ _Zp+2
P_Y$: .equ _Zp+1
P_X$: .equ _Zp+0
sta zp:P_TYPE$
lda #0
sta zp:T_NT_IDX$ ; the tiles here will be drawn only on game table
sta zp:T_COUNTER$
lda #3
sta zp:T_LINE_COUNT$
; Convert the coordinates
ldx zp:P_X$
lda TileNum_X_Map,x
sta zp:P_X$
ldx zp:P_Y$
lda TileNum_Y_Map,x
sta zp:P_Y$
; Calculate the beginning of the tile section we're interested in
clc
lda #NUM_TILE_OFFSET
adc zp:P_TYPE$
asl a
asl a
asl a
sta zp:P_TYPE$
; Draw the tile
DrawNextLine$:
jsr vdp_point_to_vram_xy
ldx zp:T_COUNTER$
ldy #4
SetTile$:
clc
lda TileNum_Offset_Map,x
adc zp:P_TYPE$
sta VDP_MEM
inx
dey
bne SetTile$
stx zp:T_COUNTER$
inc zp:P_Y$
dec zp:T_LINE_COUNT$
bne DrawNextLine$
rts
;;;;;;;;;;;;;;;;;;
.section data,data
TileNum_Offset_Map: ; Offset from the beginning of the selected tilemap section, of the tiles composing the numeric-2048 tile
.byte 0x00, 0x04, 0x04, 0x02 ; First line
.byte 0x04, 0x04, 0x04, 0x04 ; Second
.byte 0x01, 0x04, 0x04, 0x03 ; Third and last
; The following maps convert between X and Y in the game grid into the tile map grid
TileNum_X_Map:
.byte 0x01, 0x06, 0x0B, 0x10, 0x15
TileNum_Y_Map:
.byte 0x01, 0x05, 0x09, 0x0D, 0x11
;;;;;;;;;;;;;;;;;;
.public vdp_draw_numtile
.public vdp_clear_gamegrid

View file

@ -3,6 +3,8 @@
#include <calypsi/intrinsics6502.h>
#include "vdp_utils.h"
#include "monitor_subroutines.h"
#include "utility.h"
#include "mem_map.h"
@ -18,6 +20,7 @@ static shared_page_data *shared_page = (shared_page_data*)SHARED_PAGE;
void main(void) {
uint8_t wait_count = 0x40;
uint8_t vdp_detected;
// Initialize the register for LFSR
lfsr_init(0xF00D);
@ -25,7 +28,8 @@ void main(void) {
// Clear the memory used to pass parameters to the next module
memset((void*)(shared_page->module_data), 0, MODULE_DATA_SIZE);
// TODO: Detect expansion hardware, and choose what to load according to that
// Detect expansion hardware, and choose what to load according to that
vdp_detected = vdp_detect();
// Delay a bit
while(wait_count--) WAIT(0xFF);
@ -35,7 +39,7 @@ void main(void) {
memset((void*)DISPLAY_PAGE_1, 0, DISPLAY_PAGE_SIZE);
shared_page->master_command = MASTER_COMMAND_NONE;
shared_page->next_module_idx = MODULE_DLOG; // DLOG module is next!
shared_page->next_module_idx = vdp_detected ? MODULE_INIT_VDP : MODULE_DLOG; // If VDP is detected, load the module to initialize it, otherwise DLOG module is next!
return;
}

View file

@ -24,7 +24,7 @@
static uint8_t *module_page = (uint8_t*)MODULE_PAGE;
static shared_page_data * shared_page = (shared_page_data*)SHARED_PAGE;
#define FILE_LIST_LEN 6
#define FILE_LIST_LEN 7
#define FNAME_LEN 6
#define STATE_FILE_IDX 1
@ -37,7 +37,8 @@ static const uint8_t file_table[FILE_LIST_LEN][FNAME_LEN] = {
{ 0x80 | 'I', 0x80 | 'N', 0x80 | 'T', 0x80 | 'R', 0x80 | 'O', 0x00}, // INTRO (this executable will detect hardware and load the appropriate followup module)
{ 0x80 | 'D', 0x80 | 'L', 0x80 | 'O', 0x80 | 'G', 0xA0, 0x00}, // DLOG (startup, win, lose dialogs)
{ 0x80 | 'G', 0x80 | 'A', 0x80 | 'M', 0x80 | 'E', 0xA0, 0x00}, // GAME (the actual game)
{ 0x80 | 'D', 0x80 | 'E', 0x80 | 'M', 0x80 | 'O', 0xA0, 0x00}, // DEMO (automatic demo)
{ 0x80 | 'D', 0x80 | 'E', 0x80 | 'M', 0x80 | 'O', 0xA0, 0x00}, // DEMO (automatic demo),
{ 0x80 | 'V', 0x80 | 'D', 0x80 | 'P', 0x80 | 'I', 0x80 | 'N', 0x00}, // VDPIN (Loads VDP resources),
};
static uint8_t file_trksec[FILE_LIST_LEN][2]; // This will hold track/sector for initial ts list sector for every one of the listed files. Populated at startup.
@ -48,6 +49,7 @@ static uint16_t file_load_address[FILE_LIST_LEN] = { // This will hold the load
MODULE_PAGE,
MODULE_PAGE,
MODULE_PAGE,
MODULE_PAGE,
};
static void init(void);

View file

@ -4,5 +4,10 @@
#define MODULE_DLOG 3
#define MODULE_GAME 4
#define MODULE_DEMO 5
#define MODULE_INIT_VDP 6
#define MODULE_DLOG_VDP 7
#define MODULE_GAME_VDP 8
#define MODULE_DEMO_VDP 9
#endif /* _MODULE_LIST_HEADER_ */

10
src/vdp.s Normal file
View file

@ -0,0 +1,10 @@
.rtmodel version,"1"
.rtmodel core,"6502"
;;; IO Ports
SLOT_OFFSET:.equ 0x40
VDP_BASE: .equ 0xC080 + SLOT_OFFSET
VDP_MEM: .equ VDP_BASE + 0x00
VDP_REG: .equ VDP_BASE + 0x01
.public VDP_MEM, VDP_REG

8
src/vdp_init.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef _VDP_INIT_HEADER_
#define _VDP_INIT_HEADER_
#include <stdint.h>
uint8_t vdp_init(void);
#endif /* _VDP_INIT_HEADER_ */

161
src/vdp_init.s Normal file
View file

@ -0,0 +1,161 @@
.rtmodel version,"1"
.rtmodel core,"6502"
.extern _Zp
.extern vdp_write_vram, vdp_write_registers
.extern SpriteAttributeTable, NT_P0, NT_P1
.extern VDP_MEM, VDP_REG
.section code,text
;;; vdp_init:
;;; Initialize the VDP processor
;;; Parameters: none
;;;
;;; Returns: nothing
;;;
;;; Clobbers:
;;; - A, Y, X
;;; - Zp 1, 2, 3, 4, 5
;;;
vdp_init:
T_SCRATCH_L$:.equ _Zp+0
T_SCRATCH_H$:.equ _Zp+1
; Get the board ready to accept the rest of the commands
lda #0x80 ; Disable interrupts and video, set 16K mode
sta VDP_REG
lda #0x81
sta VDP_REG
; Load the pattern table int 0x0800
lda #.byte0 PatternTable_Charset
sta zp:_Zp+0
lda #.byte1 PatternTable_Charset
sta zp:_Zp+1
lda #0x00
sta zp:_Zp+2
lda #0x08
sta zp:_Zp+3
lda #0x00
sta zp:_Zp+4
lda #0x08
sta zp:_Zp+5
jsr vdp_write_vram
; Load the Color Table into 0x0380
lda #.byte0 ColorTable
sta zp:_Zp+0
lda #.byte1 ColorTable
sta zp:_Zp+1
lda #0x20
sta zp:_Zp+2
lda #0x00
sta zp:_Zp+3
lda #0x80
sta zp:_Zp+4
lda #0x03
sta zp:_Zp+5
jsr vdp_write_vram
; Load the Sprite attribute table at 0x0300
lda #.byte0 SpriteAttributeTable
sta zp:_Zp+0
lda #.byte1 SpriteAttributeTable
sta zp:_Zp+1
lda #0x80
sta zp:_Zp+2
lda #0x00
sta zp:_Zp+3
lda #0x00
sta zp:_Zp+4
lda #0x03
sta zp:_Zp+5
jsr vdp_write_vram
; Load the Name Table for the Game Board at 0x0000
lda #.byte0 NameTable_GameBoard
sta zp:_Zp+0
lda #.byte1 NameTable_GameBoard
sta zp:_Zp+1
lda #0x00
sta zp:_Zp+2
lda #0x03
sta zp:_Zp+3
lda #0x00
sta zp:_Zp+4
lda NT_P0
sta zp:_Zp+5
jsr vdp_write_vram
; Load the Name Table for the Dialog Screens at 0x1400
lda #.byte0 NameTable_Dialog
sta zp:_Zp+0
lda #.byte1 NameTable_Dialog
sta zp:_Zp+1
lda #0x00
sta zp:_Zp+2
lda #0x03
sta zp:_Zp+3
lda #0x00
sta zp:_Zp+4
lda NT_P1
sta zp:_Zp+5
jsr vdp_write_vram
; Load the Sprite Pattern table at 0x1000
lda #.byte0 SpritePatternTable
sta zp:_Zp+0
lda #.byte1 SpritePatternTable
sta zp:_Zp+1
lda #0x80
sta zp:_Zp+2
lda #0x01
sta zp:_Zp+3
lda #0x00
sta zp:_Zp+4
lda #0x10
sta zp:_Zp+5
jsr vdp_write_vram
; Write the register sequence
lda #.byte0 VDPRegs_M0
sta zp:_Zp+0
lda #.byte1 VDPRegs_M0
sta zp:_Zp+1
jsr vdp_write_registers
rts
.section data,data
PatternTable_Charset:
.incbin "..\\data\\vdp_charset.bin"
NameTable_GameBoard:
.incbin "..\\data\\vdp_nt_board.bin"
NameTable_Dialog:
.incbin "..\\data\\vdp_nt_dialog.bin"
SpritePatternTable:
.incbin "..\\data\\vdp_sprite_tiles.bin"
ColorTable:
.incbin "..\\data\\vdp_colortable.bin"
VDPRegs_M0:
.byte 0x00, 0x80 ; Reg. 0, Disable external video, set M3 to 0 (we'll use mode 0/Graphics I)
.byte 0xE3, 0x81 ; Reg. 1, Enable display and interrupts, 16x16 sprites and magnification
.byte 0x05, 0x82 ; Reg. 2, Place Name Table at 0x1400 (Load the Dialog)
.byte 0x0E, 0x83 ; Reg. 3, Place Color Table at 0x0380
.byte 0x01, 0x84 ; Reg. 4, Place Pattern Table at 0x0800
.byte 0x06, 0x85 ; Reg. 5, Place Sprite Attribute Table at 0x0300, the secondary will live at 0x1200
.byte 0x02, 0x86 ; Reg. 6, Place Sprite Pattern Table at 0x1000
.byte 0xF1, 0x87 ; Reg. 7, Set foreground and background color (white on black)
;;;
;;;
;;; Exported symbols
.public vdp_init

19
src/vdp_utils.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef _VDP_UTILS_HEADER_
#define _VDP_UTILS_HEADER_
#include <stdint.h>
void vdp_write_vram(uint8_t *src, uint16_t len, uint16_t vram_dest);
void vdp_fill_vram(uint8_t val, uint16_t len, uint16_t vram_dest);
uint8_t vdp_detect(void);
void vdp_init_registers(uint8_t *reg_array);
void vdp_hide_sprites(void);
void vdp_hide_sprite(uint8_t sprite_number);
void vdp_show_sprite(uint8_t sprite_number);
void vdp_set_sprite_tile(uint8_t sprite_number, uint8_t tile_idx);
void vdp_switch_nt(uint8_t nt_idx);
void vdp_print_string(uint8_t nt_idx, uint8_t x, uint8_t y, char *str);
void vdp_set_tile(uint8_t nt_idx, uint8_t x, uint8_t y, uint8_t tile_idx);
void vdp_irq_handler(void);
#endif /* _VDP_UTILS_HEADER_ */

591
src/vdp_utils.s Normal file
View file

@ -0,0 +1,591 @@
.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
;;;
vdp_hide_sprites:
ldy #0
HideLoop$:
tya
jsr vdp_hide_sprite
iny
cpy #25
bne HideLoop$
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

34
src/vdpin_main.c Normal file
View file

@ -0,0 +1,34 @@
#include <stubs.h>
#include <string.h>
#include <calypsi/intrinsics6502.h>
#include "vdp_init.h"
#include "vdp_utils.h"
#include "monitor_subroutines.h"
#include "utility.h"
#include "mem_map.h"
#include "shared_page.h"
#include "module_list.h"
// External initialization requirements
#pragma require __preserve_zp
#pragma require __data_initialization_needed
static shared_page_data *shared_page = (shared_page_data*)SHARED_PAGE;
void main(void) {
__disable_interrupts(); // Make sure the interrupts are disabled
vdp_init();
vdp_print_string(1, 6, 10, "Loading...");
shared_page->master_command = MASTER_COMMAND_NONE;
shared_page->next_module_idx = MODULE_DLOG;
return;
}