diff --git a/Makefile b/Makefile index 70c1604..1668b8a 100644 --- a/Makefile +++ b/Makefile @@ -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;) diff --git a/data/vdp_charset.bin b/data/vdp_charset.bin new file mode 100644 index 0000000..c98a21e Binary files /dev/null and b/data/vdp_charset.bin differ diff --git a/data/vdp_colortable.bin b/data/vdp_colortable.bin new file mode 100644 index 0000000..20da2c5 Binary files /dev/null and b/data/vdp_colortable.bin differ diff --git a/data/nt_board.bin b/data/vdp_nt_board.bin similarity index 100% rename from data/nt_board.bin rename to data/vdp_nt_board.bin diff --git a/data/nt_dialog.bin b/data/vdp_nt_dialog.bin similarity index 91% rename from data/nt_dialog.bin rename to data/vdp_nt_dialog.bin index b99788b..2102e69 100644 Binary files a/data/nt_dialog.bin and b/data/vdp_nt_dialog.bin differ diff --git a/data/vdp_sprite_tiles.bin b/data/vdp_sprite_tiles.bin new file mode 100644 index 0000000..20a9083 Binary files /dev/null and b/data/vdp_sprite_tiles.bin differ diff --git a/linker-files/vdpin_module.scm b/linker-files/vdpin_module.scm new file mode 100644 index 0000000..363b002 --- /dev/null +++ b/linker-files/vdpin_module.scm @@ -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)) + )) \ No newline at end of file diff --git a/src/game_vdp_graphics.h b/src/game_vdp_graphics.h new file mode 100644 index 0000000..54ee390 --- /dev/null +++ b/src/game_vdp_graphics.h @@ -0,0 +1,9 @@ +#ifndef _GAME_VDP_GRAPHICS_HEADER_ +#define _GAME_VDP_GRAPHICS_HEADER_ + +#include + +void vdp_clear_gamegrid(void); +uint8_t vdp_draw_numtile(uint8_t type, uint8_t x, uint8_t y); + +#endif /* _GAME_VDP_GRAPHICS_HEADER_ */ diff --git a/src/game_vdp_graphics.s b/src/game_vdp_graphics.s new file mode 100644 index 0000000..1a0b4f3 --- /dev/null +++ b/src/game_vdp_graphics.s @@ -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 + \ No newline at end of file diff --git a/src/intro_main.c b/src/intro_main.c index 4d094b9..89800c8 100644 --- a/src/intro_main.c +++ b/src/intro_main.c @@ -3,6 +3,8 @@ #include +#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; } diff --git a/src/master_main.c b/src/master_main.c index b9ed9c7..c088a8e 100644 --- a/src/master_main.c +++ b/src/master_main.c @@ -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); diff --git a/src/module_list.h b/src/module_list.h index 17df25e..fb9f821 100644 --- a/src/module_list.h +++ b/src/module_list.h @@ -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_ */ diff --git a/src/vdp.s b/src/vdp.s new file mode 100644 index 0000000..fa2a681 --- /dev/null +++ b/src/vdp.s @@ -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 \ No newline at end of file diff --git a/src/vdp_init.h b/src/vdp_init.h new file mode 100644 index 0000000..09f0e84 --- /dev/null +++ b/src/vdp_init.h @@ -0,0 +1,8 @@ +#ifndef _VDP_INIT_HEADER_ +#define _VDP_INIT_HEADER_ + +#include + +uint8_t vdp_init(void); + +#endif /* _VDP_INIT_HEADER_ */ diff --git a/src/vdp_init.s b/src/vdp_init.s new file mode 100644 index 0000000..f7dc3a1 --- /dev/null +++ b/src/vdp_init.s @@ -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 + \ No newline at end of file diff --git a/src/vdp_utils.h b/src/vdp_utils.h new file mode 100644 index 0000000..b253b6f --- /dev/null +++ b/src/vdp_utils.h @@ -0,0 +1,19 @@ +#ifndef _VDP_UTILS_HEADER_ +#define _VDP_UTILS_HEADER_ + +#include + +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_ */ diff --git a/src/vdp_utils.s b/src/vdp_utils.s new file mode 100644 index 0000000..1b1c8f5 --- /dev/null +++ b/src/vdp_utils.s @@ -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 \ No newline at end of file diff --git a/src/vdpin_main.c b/src/vdpin_main.c new file mode 100644 index 0000000..6bc0ba7 --- /dev/null +++ b/src/vdpin_main.c @@ -0,0 +1,34 @@ +#include +#include + +#include + +#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; +}