diff --git a/CHANGELOG.md b/CHANGELOG.md index 00ab961..60e0919 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 3.0 - 2025-10-17 + +### Added + +- Support for the TK2000 VDP board + + ## 2.1 - 2025-10-07 ### Changed diff --git a/Makefile b/Makefile index 53d346b..68f225b 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,10 @@ INTRO_PRG=intro DLOG_PRG=dlog GAME_PRG=game DEMO_PRG=demo +VDPIN_PRG=vdpin +VDDLG_PRG=vddlg +VDGAM_PRG=vdgam +VDDEM_PRG=vddem # Libraries LIBS=clib-6502.a @@ -19,17 +23,29 @@ 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_C_SRCS = intro_main.c utility.c +INTRO_ASM_SRCS = tk2k_startup_module.s preserve_zero_pages.s vdp.s vdp_utils.s +INTRO_C_SRCS = intro_main.c utility.c input.c DLOG_ASM_SRCS = tk2k_startup_module.s preserve_zero_pages.s sound.s -DLOG_C_SRCS = dlog_main.c input.c utility.c game_graphics.c line_data.c +DLOG_C_SRCS = dlog_main.c input.c utility.c game_hgr_graphics.c hgr_line_data.c GAME_ASM_SRCS = tk2k_startup_module.s preserve_zero_pages.s input_asm.s sound.s -GAME_C_SRCS = game_main.c input.c utility.c game_graphics.c line_data.c game_logic.c arrows_pic.c tiles.c graph_misc_data.c +GAME_C_SRCS = game_main.c input.c utility.c game_hgr_graphics.c hgr_line_data.c game_logic.c arrows_pic.c tiles.c hgr_graph_misc_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_graphics_demo.c line_data.c game_logic.c arrows_pic.c tiles.c graph_misc_data.c +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 + +VDDLG_ASM_SRCS = tk2k_startup_module.s preserve_zero_pages.s sound.s vdp.s vdp_utils.s game_vdp_graphics.s +VDDLG_C_SRCS = vddlg_main.c input.c utility.c + +VDGAM_ASM_SRCS = tk2k_startup_module.s preserve_zero_pages.s input_asm.s sound.s vdp.s vdp_utils.s game_vdp_graphics.s +VDGAM_C_SRCS = vdgam_main.c input.c utility.c game_logic.c + +VDDEM_ASM_SRCS = tk2k_startup_module.s preserve_zero_pages.s input_asm.s sound.s vdp.s vdp_utils.s game_vdp_graphics.s +VDDEM_C_SRCS = vddem_main.c input.c utility.c game_logic.c # Object files MASTER_OBJS = $(MASTER_ASM_SRCS:%.s=%.o) $(MASTER_C_SRCS:%.c=%.o) @@ -37,6 +53,10 @@ 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) +VDDLG_OBJS = $(VDDLG_ASM_SRCS:%.s=%.o) $(VDDLG_C_SRCS:%.c=%.o) +VDGAM_OBJS = $(VDGAM_ASM_SRCS:%.s=%.o) $(VDGAM_C_SRCS:%.c=%.o) +VDDEM_OBJS = $(VDDEM_ASM_SRCS:%.s=%.o) $(VDDEM_C_SRCS:%.c=%.o) all: $(SW_NAME).woz @@ -61,6 +81,18 @@ $(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) + +$(VDDLG_PRG).hex: $(VDDLG_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) + +$(VDGAM_PRG).hex: $(VDGAM_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) + +$(VDDEM_PRG).hex: $(VDDEM_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) + $(MASTER_PRG).bin: $(MASTER_PRG).hex (cd out ; objcopy -I ihex -O binary $(MASTER_PRG).hex $(MASTER_PRG).bin) @@ -75,14 +107,30 @@ $(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) + +$(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 +$(VDDLG_PRG).bin: $(VDDLG_PRG).hex + (cd out ; objcopy -I ihex -O binary $(VDDLG_PRG).hex $(VDDLG_PRG).bin) + +$(VDGAM_PRG).bin: $(VDGAM_PRG).hex + (cd out ; objcopy -I ihex -O binary $(VDGAM_PRG).hex $(VDGAM_PRG).bin) + +$(VDDEM_PRG).bin: $(VDDEM_PRG).hex + (cd out ; objcopy -I ihex -O binary $(VDDEM_PRG).hex $(VDDEM_PRG).bin) + +$(SW_NAME).dsk: $(MASTER_PRG).bin $(INTRO_PRG).bin $(DLOG_PRG).bin $(GAME_PRG).bin $(DEMO_PRG).bin $(VDPIN_PRG).bin $(VDDLG_PRG).bin $(VDGAM_PRG).bin $(VDDEM_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 $(VDDLG_PRG).bin | $(JAVA) -jar $(ACMD) -p $(SW_NAME).dsk VDDLG b; \ + cat $(VDGAM_PRG).bin | $(JAVA) -jar $(ACMD) -p $(SW_NAME).dsk VDGAM b; \ + cat $(VDDEM_PRG).bin | $(JAVA) -jar $(ACMD) -p $(SW_NAME).dsk VDDEM 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/README.md b/README.md index 87359f6..6d24f63 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ If you wish to support me in building new hardware and software for old machines The game is playable, and the following features have been implemented: - B/W mode +- VDP board support - Single graphical tileset - Simple sound effects - Control via keyboard @@ -52,6 +53,17 @@ The game is playable, and the following features have been implemented: The game ends once you reach a tile with a value of 2048. +### VDP support + +The game supports the [TK2000 VDP board](https://codeberg.org/hkzlab/TK2000_VDPboard) configured at address `C0Cx`. + +![TK2048 VDP Game screen](pics/vdp_mode.jpg) + +The card gets automatically detected and used: once the game completes the initial load, everything will be displayed on the screen +connected to the VDP board, and the main screen connected to the TK2000 will be left blank. + +If you want to override detection and force the use of the main screen, just keep a button pressed while the title screen image loads. + ### Floppy version Just put your floppy in the first drive and power on the TK2000. It will autoboot. diff --git a/data/vdp_charset.bin b/data/vdp_charset.bin new file mode 100644 index 0000000..658d813 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..efd4f0e Binary files /dev/null and b/data/vdp_colortable.bin differ diff --git a/data/vdp_nt_board.bin b/data/vdp_nt_board.bin new file mode 100644 index 0000000..92d3faa Binary files /dev/null and b/data/vdp_nt_board.bin differ diff --git a/data/vdp_nt_dialog.bin b/data/vdp_nt_dialog.bin new file mode 100644 index 0000000..937d253 Binary files /dev/null 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/module.scm b/linker-files/module.scm index 81c846b..fd7c619 100644 --- a/linker-files/module.scm +++ b/linker-files/module.scm @@ -1,19 +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 . #x93ff)) (type ram) (section (programStart #x4000) startup code switch idata cdata data_init_table)) - (memory upperData (address (#x9400 . #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)) +(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 . #x935f)) (type ram) (section (programStart #x4000) startup code switch idata cdata data_init_table)) + (memory upperData (address (#x9360 . #x99ff)) (type ram) (section cstack zdata data heap intrzp)) + (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/linker-files/vdpin_module.scm b/linker-files/vdpin_module.scm new file mode 100644 index 0000000..2a0f734 --- /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 intrzp)) + (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/pics/vdp_mode.jpg b/pics/vdp_mode.jpg new file mode 100644 index 0000000..68c65d9 Binary files /dev/null and b/pics/vdp_mode.jpg differ diff --git a/src/demo_main.c b/src/demo_main.c index 00625b2..5d9a243 100644 --- a/src/demo_main.c +++ b/src/demo_main.c @@ -12,9 +12,10 @@ #include "game_data.h" #include "input.h" #include "game_logic.h" -#include "game_graphics.h" +#include "game_hgr_graphics.h" #include "monitor_subroutines.h" #include "sound.h" +#include "module_list.h" // External initialization requirements #pragma require __preserve_zp @@ -44,7 +45,7 @@ void main(void) { // By default, once we return from this, return to the DLOG module and give the master no command to execute shared_page->master_command = MASTER_COMMAND_NONE; - shared_page->next_module_idx = 3; // Go to the DLOG module + shared_page->next_module_idx = MODULE_DLOG; // Go to the DLOG module dlog_data *dld = (dlog_data *)(shared_page->module_data); dlog_data *gad = (dlog_data *)(shared_page->module_data); diff --git a/src/dlog_main.c b/src/dlog_main.c index c371164..5c32d8f 100644 --- a/src/dlog_main.c +++ b/src/dlog_main.c @@ -3,7 +3,7 @@ #include -#include "game_graphics.h" +#include "game_hgr_graphics.h" #include "monitor_subroutines.h" #include "utility.h" #include "mem_map.h" @@ -13,6 +13,7 @@ #include "game_data.h" #include "input.h" #include "sound.h" +#include "module_list.h" // External initialization requirements #pragma require __preserve_zp @@ -54,7 +55,7 @@ void main(void) { shared_page->master_command = MASTER_COMMAND_NONE; } - shared_page->next_module_idx = 4; // Go to the GAME module + shared_page->next_module_idx = MODULE_GAME; // Go to the GAME module gad->mode = GAME_MODE_NORMAL; // Set the proper start mode for the game while(!read_any_key() && (wait_counter != WAIT_COUNTER_END)) { @@ -63,7 +64,7 @@ void main(void) { } if (wait_counter == WAIT_COUNTER_END) { - shared_page->next_module_idx = 5; // Actually go to the DEMO module + shared_page->next_module_idx = MODULE_DEMO; // Actually go to the DEMO module return; } diff --git a/src/game_data.h b/src/game_data.h index 63f2e48..a8a400b 100644 --- a/src/game_data.h +++ b/src/game_data.h @@ -1,14 +1,19 @@ -#ifndef _GAME_DATA_HEADER_ -#define _GAME_DATA_HEADER_ - -#include - -#define GAME_MODE_NORMAL 0 -#define GAME_MODE_LOAD 1 - - -typedef struct { - uint8_t mode; -} game_data; - -#endif /* _GAME_DATA_HEADER_ */ +#ifndef _GAME_DATA_HEADER_ +#define _GAME_DATA_HEADER_ + +#include + +#define GAME_VER_CH0 '3' +#define GAME_VER_CH1 '.' +#define GAME_VER_CH2 '0' + + +#define GAME_MODE_NORMAL 0 +#define GAME_MODE_LOAD 1 + + +typedef struct { + uint8_t mode; +} game_data; + +#endif /* _GAME_DATA_HEADER_ */ diff --git a/src/game_graphics_demo.c b/src/game_graphics_demo.c deleted file mode 100644 index f33e2f1..0000000 --- a/src/game_graphics_demo.c +++ /dev/null @@ -1,3 +0,0 @@ -#define _DEMO_MODE_ 1 - -#include "game_graphics.c" diff --git a/src/game_graphics.c b/src/game_hgr_graphics.c similarity index 98% rename from src/game_graphics.c rename to src/game_hgr_graphics.c index bf1ed65..caac19b 100644 --- a/src/game_graphics.c +++ b/src/game_hgr_graphics.c @@ -1,16 +1,17 @@ -#include "game_graphics.h" +#include "game_hgr_graphics.h" #include +#include "game_data.h" #include "utility.h" #include "mem_map.h" #include "mem_registers.h" -#include "line_data.h" +#include "hgr_line_data.h" #include "game_logic.h" #include "tiles.h" #include "charset.h" #include "monitor_subroutines.h" -#include "graph_misc_data.h" +#include "hgr_graph_misc_data.h" #include "arrows_pic.h" #define SCREEN_WIDTH 280 @@ -78,7 +79,7 @@ static const uint8_t box_content_start[BOX_CONTENT_SIZE] = { 0, 0, 0, 0, 0, 0, 0,203, 77, 0, 0, 77, 0, 0, 77,192, 77, 0, 77, 77,211, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 77,211, 0, 0, 77, 0, 0, 77, 0, 77, 0, 77,198, 84, 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 20, 11, 50, 48, 52, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 50, 46, 49, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 20, 11, 50, 48, 52, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, GAME_VER_CH0, GAME_VER_CH1, GAME_VER_CH2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -106,7 +107,7 @@ static const uint8_t instruction_box[INSTR_BOX_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 5, 1, 3, 8, 0, 0, 50, 48, 52, 56, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 79, 0, 50, 48, 50, 53, 0, 50, 46, 49, 0, 79 + 7, 15, 15, 4, 0, 12, 21, 3, 11, 33, 33, 33 }; static const uint8_t demo_box[INSTR_BOX_SIZE] = { @@ -117,7 +118,7 @@ static const uint8_t demo_box[INSTR_BOX_SIZE] = { 4, 5, 0, 0, 4, 5, 13, 15, 0, 0, 13, 15, 15, 4, 5, 0, 0, 4, 5, 13, 15, 0, 0, 13, 13, 15, 4, 5, 0, 0, 4, 5, 13, 15, 0, 0, - 79, 0, 50, 48, 50, 53, 0, 50, 46, 49, 0, 79 + 0, 13, 15, 4, 5, 0, 0, 4, 5, 13, 15, 0 }; #ifdef _DEMO_MODE_ diff --git a/src/game_graphics.h b/src/game_hgr_graphics.h similarity index 87% rename from src/game_graphics.h rename to src/game_hgr_graphics.h index 72fcf99..e4234a7 100644 --- a/src/game_graphics.h +++ b/src/game_hgr_graphics.h @@ -1,34 +1,34 @@ -#ifndef _GAME_GRAPHICS_HEADER_ -#define _GAME_GRAPHICS_HEADER_ - -#include - -#define BRD_DOUBLING_UP(a) (a & 0x01) -#define BRD_DOUBLING_DOWN(a) (a & 0x02) -#define BRD_DOUBLING_LEFT(a) (a & 0x04) -#define BRD_DOUBLING_RIGHT(a) (a & 0x08) - -#define BRD_SKIP_UP(a) (a & 0x10) -#define BRD_SKIP_DOWN(a) (a & 0x20) -#define BRD_SKIP_LEFT(a) (a & 0x40) -#define BRD_SKIP_RIGHT(a) (a & 0x80) - -#define GRAPH_ARROW_UP 0 -#define GRAPH_ARROW_DOWN 1 -#define GRAPH_ARROW_LEFT 2 -#define GRAPH_ARROW_RIGHT 3 - -void initialize_display_buffers(void); -void ddraw_field_borders_on_buffer(uint8_t brd); -void draw_game_background(uint16_t hi_score); -void draw_number(uint16_t n, uint8_t len, uint8_t x, uint8_t y); -void draw_tiles(void); -void ddraw_single_tile(uint8_t offset); -void swap_display_buffers(void); -void clear_display_buffers(void); -void clear_box(uint8_t w, uint8_t h, uint8_t off_x, uint8_t off_y, uint8_t *disp_buf); -void ddraw_direction_arrows(uint8_t dir); -void ddraw_endgame_box(int8_t done, uint16_t score, uint16_t hi_score); -void sync_display1_buffer(void); - -#endif /* _GAME_GRAPHICS_HEADER_ */ +#ifndef _GAME_HGR_GRAPHICS_HEADER_ +#define _GAME_HGR_GRAPHICS_HEADER_ + +#include + +#define BRD_DOUBLING_UP(a) (a & 0x01) +#define BRD_DOUBLING_DOWN(a) (a & 0x02) +#define BRD_DOUBLING_LEFT(a) (a & 0x04) +#define BRD_DOUBLING_RIGHT(a) (a & 0x08) + +#define BRD_SKIP_UP(a) (a & 0x10) +#define BRD_SKIP_DOWN(a) (a & 0x20) +#define BRD_SKIP_LEFT(a) (a & 0x40) +#define BRD_SKIP_RIGHT(a) (a & 0x80) + +#define GRAPH_ARROW_UP 0 +#define GRAPH_ARROW_DOWN 1 +#define GRAPH_ARROW_LEFT 2 +#define GRAPH_ARROW_RIGHT 3 + +void initialize_display_buffers(void); +void ddraw_field_borders_on_buffer(uint8_t brd); +void draw_game_background(uint16_t hi_score); +void draw_number(uint16_t n, uint8_t len, uint8_t x, uint8_t y); +void draw_tiles(void); +void ddraw_single_tile(uint8_t offset); +void swap_display_buffers(void); +void clear_display_buffers(void); +void clear_box(uint8_t w, uint8_t h, uint8_t off_x, uint8_t off_y, uint8_t *disp_buf); +void ddraw_direction_arrows(uint8_t dir); +void ddraw_endgame_box(int8_t done, uint16_t score, uint16_t hi_score); +void sync_display1_buffer(void); + +#endif /* _GAME_HGR_GRAPHICS_HEADER_ */ diff --git a/src/game_hgr_graphics_demo.c b/src/game_hgr_graphics_demo.c new file mode 100644 index 0000000..337c19a --- /dev/null +++ b/src/game_hgr_graphics_demo.c @@ -0,0 +1,3 @@ +#define _DEMO_MODE_ 1 + +#include "game_hgr_graphics.c" diff --git a/src/game_logic.c b/src/game_logic.c index aa19674..22867b5 100644 --- a/src/game_logic.c +++ b/src/game_logic.c @@ -1,149 +1,149 @@ -#include "game_logic.h" - -#include "utility.h" - -#include - -static uint8_t game_grid_alpha[GRID_SIDE * GRID_SIDE]; -static uint8_t game_grid_beta[GRID_SIDE * GRID_SIDE]; - -static uint8_t *front_grid = game_grid_alpha; -static uint8_t *back_grid = game_grid_beta; - -void swap_grids(void); - -void swap_grids(void) { - uint8_t *temp = front_grid; - - front_grid = back_grid; - back_grid = temp; -} - -uint8_t reset_game(void) { - uint8_t score = 0; - - // Clear the back and front grid - memset(back_grid, 0, GRID_SIDE * GRID_SIDE); - memset(front_grid, 0, GRID_SIDE * GRID_SIDE); - - // Then add two random tiles - score += (1 << front_grid[add_random_tile() - 1]); - score += (1 << front_grid[add_random_tile() - 1]); - - return score; -} - -uint8_t *get_front_grid(void) { - return front_grid; -} - -uint8_t add_random_tile(void) { - uint16_t rand = lfsr_update(); - uint8_t tile_val = (rand & 0x000F) > 0x0D ? 2 : 1; // ~90% chance of a tile of type 1 (a "2"), 10% of a type 2 (a "4") - - uint8_t free_tiles = 0; - uint8_t chosen_tile; - - for (uint8_t offset = 0; offset < GRID_SIDE * GRID_SIDE; offset++) { - if (!front_grid[offset]) free_tiles++; - } - - if(!free_tiles) return 0; - chosen_tile = (rand >> 8) % free_tiles; - - for (uint8_t offset = 0; offset < GRID_SIDE * GRID_SIDE; offset++) { - if (!front_grid[offset] && !(chosen_tile--)) { - front_grid[offset] = tile_val; - return offset + 1; - } - } - - return 0; // Return 0 if we were not able to place the tile, else we return (offset + 1) to indicate where the tile was placed -} - -int8_t step_game(uint8_t dir) { - int8_t done = 0; - uint8_t start_offset; - int8_t column_step; - int8_t row_step; - - /* - * UP: scans TOP to BOTTOM, RIGHT to LEFT - * DOWN: scans BOTTOM to TOP, RIGHT to LEFT - * LEFT: scans LEFT to RIGHT, TOP to BOTTOM - * RIGHT: scans RIGHT to LEFT, TOP to BOTTOM - */ - - switch(dir) { - case GAME_STEP_UP: - start_offset = GRID_SIDE - 1; - column_step = GRID_SIDE; - row_step = -1; - break; - case GAME_STEP_DOWN: - start_offset = (GRID_SIDE * GRID_SIDE) - 1; - column_step = -GRID_SIDE; - row_step = -1; - break; - case GAME_STEP_LEFT: - start_offset = 0; - column_step = 1; - row_step = GRID_SIDE; - break; - case GAME_STEP_RIGHT: - start_offset = GRID_SIDE - 1; - column_step = -1; - row_step = GRID_SIDE; - break; - }; - - // Clear the back grid - memset(back_grid, 0, GRID_SIDE * GRID_SIDE); - - for (uint8_t row = 0; row < GRID_SIDE; row++) { - for(uint8_t col = 0; col < GRID_SIDE; col++) { - uint8_t current_offset = start_offset + (col * column_step) + (row * row_step); - uint8_t sub_col; - - // Search for the first non-zero value in the front grid and copy it in the current place of the back grid (zeroing it in the front) - for(sub_col = col; sub_col < GRID_SIDE; sub_col++) { - uint8_t sub_col_offset = start_offset + (sub_col * column_step) + (row * row_step); - if(front_grid[sub_col_offset]) { - back_grid[current_offset] = front_grid[sub_col_offset]; - front_grid[sub_col_offset] = 0; - - break; - } - } - - // The value is still 0, we found nothing. On to the next row! - if (!back_grid[current_offset]) break; - - // Now search if there is an identical value following this one, so we can merge them - for(; sub_col < GRID_SIDE; sub_col++) { - uint8_t sub_col_offset = start_offset + (sub_col * column_step) + (row * row_step); - if(front_grid[sub_col_offset] == back_grid[current_offset]) { - back_grid[current_offset]++; // Merge them (by increasing the value of the current square and removing the merged one) - front_grid[sub_col_offset] = 0; - - done += back_grid[current_offset] == 11 ? 1 : 0; - break; - } else if (front_grid[sub_col_offset]) break; - } - } - } - - swap_grids(); - - return done; -} - -uint16_t calculate_score(void) { - uint16_t score = 0; - - for(uint8_t offset = 0; offset < GRID_SIDE * GRID_SIDE; offset++) { - if(front_grid[offset]) score += ((uint16_t)1 << front_grid[offset]); - } - - return score; -} +#include "game_logic.h" + +#include "utility.h" + +#include + +static uint8_t game_grid_alpha[GRID_SIDE * GRID_SIDE]; +static uint8_t game_grid_beta[GRID_SIDE * GRID_SIDE]; + +static uint8_t *front_grid = game_grid_alpha; +static uint8_t *back_grid = game_grid_beta; + +void swap_grids(void); + +void swap_grids(void) { + uint8_t *temp = front_grid; + + front_grid = back_grid; + back_grid = temp; +} + +uint8_t reset_game(void) { + uint8_t score = 0; + + // Clear the back and front grid + memset(back_grid, 0, GRID_SIDE * GRID_SIDE); + memset(front_grid, 0, GRID_SIDE * GRID_SIDE); + + // Then add two random tiles + score += (1 << front_grid[add_random_tile() - 1]); + score += (1 << front_grid[add_random_tile() - 1]); + + return score; +} + +uint8_t *get_front_grid(void) { + return front_grid; +} + +uint8_t add_random_tile(void) { + uint16_t rand = lfsr_update(); + uint8_t tile_val = (rand & 0x000F) > 0x0D ? 2 : 1; // ~90% chance of a tile of type 1 (a "2"), 10% of a type 2 (a "4") + + uint8_t free_tiles = 0; + uint8_t chosen_tile; + + for (int8_t offset = 0; offset < GRID_SIDE * GRID_SIDE; offset++) { + if (!front_grid[offset]) free_tiles++; + } + + if(!free_tiles) return 0; + chosen_tile = (rand >> 8) % free_tiles; + + for (int8_t offset = 0; offset < GRID_SIDE * GRID_SIDE; offset++) { + if (!front_grid[offset] && !(chosen_tile--)) { + front_grid[offset] = tile_val; + return offset + 1; + } + } + + return 0; // Return 0 if we were not able to place the tile, else we return (offset + 1) to indicate where the tile was placed +} + +int8_t step_game(uint8_t dir) { + int8_t done = 0; + int8_t start_offset; + int8_t column_step; + int8_t row_step; + + /* + * UP: scans TOP to BOTTOM, RIGHT to LEFT + * DOWN: scans BOTTOM to TOP, RIGHT to LEFT + * LEFT: scans LEFT to RIGHT, TOP to BOTTOM + * RIGHT: scans RIGHT to LEFT, TOP to BOTTOM + */ + + switch(dir) { + case GAME_STEP_UP: + start_offset = GRID_SIDE - 1; + column_step = GRID_SIDE; + row_step = -1; + break; + case GAME_STEP_DOWN: + start_offset = (GRID_SIDE * GRID_SIDE) - 1; + column_step = -GRID_SIDE; + row_step = -1; + break; + case GAME_STEP_LEFT: + start_offset = 0; + column_step = 1; + row_step = GRID_SIDE; + break; + case GAME_STEP_RIGHT: + start_offset = GRID_SIDE - 1; + column_step = -1; + row_step = GRID_SIDE; + break; + }; + + // Clear the back grid + memset(back_grid, 0, GRID_SIDE * GRID_SIDE); + + for (int8_t row = 0; row < GRID_SIDE; row++) { + for(int8_t col = 0; col < GRID_SIDE; col++) { + uint8_t current_offset = start_offset + (col * column_step) + (row * row_step); + uint8_t sub_col; + + // Search for the first non-zero value in the front grid and copy it in the current place of the back grid (zeroing it in the front) + for(sub_col = col; sub_col < GRID_SIDE; sub_col++) { + uint8_t sub_col_offset = start_offset + (sub_col * column_step) + (row * row_step); + if(front_grid[sub_col_offset]) { + back_grid[current_offset] = front_grid[sub_col_offset]; + front_grid[sub_col_offset] = 0; + + break; + } + } + + // The value is still 0, we found nothing. On to the next row! + if (!back_grid[current_offset]) break; + + // Now search if there is an identical value following this one, so we can merge them + for(; sub_col < GRID_SIDE; sub_col++) { + uint8_t sub_col_offset = start_offset + (sub_col * column_step) + (row * row_step); + if(front_grid[sub_col_offset] == back_grid[current_offset]) { + back_grid[current_offset]++; // Merge them (by increasing the value of the current square and removing the merged one) + front_grid[sub_col_offset] = 0; + + done += back_grid[current_offset] == 11 ? 1 : 0; + break; + } else if (front_grid[sub_col_offset]) break; + } + } + } + + swap_grids(); + + return done; +} + +uint16_t calculate_score(void) { + uint16_t score = 0; + + for(uint8_t offset = 0; offset < GRID_SIDE * GRID_SIDE; offset++) { + if(front_grid[offset]) score += ((uint16_t)1 << front_grid[offset]); + } + + return score; +} diff --git a/src/game_main.c b/src/game_main.c index 1043aff..2ae6867 100644 --- a/src/game_main.c +++ b/src/game_main.c @@ -1,165 +1,166 @@ -#include -#include - -#include - -#include "monitor_subroutines.h" -#include "utility.h" -#include "mem_map.h" -#include "shared_page.h" -#include "state_page.h" -#include "dlog_data.h" -#include "game_data.h" -#include "input.h" -#include "game_logic.h" -#include "game_graphics.h" -#include "monitor_subroutines.h" -#include "sound.h" - -// External initialization requirements -#pragma require __preserve_zp -#pragma require __data_initialization_needed - -#define MOVES_TEXT_X 32 -#define MOVES_TEXT_Y 61 -#define MOVES_TEXT_WIDTH 5 - -#define SCORE_TEXT_X 32 -#define SCORE_TEXT_Y 29 -#define SCORE_TEXT_WIDTH 5 - -#define HIGH_TEXT_X 32 -#define HIGH_TEXT_Y 107 - -#define WIN_SCORE_BONUS 10000 - -static state_page_data* state_page = (state_page_data*)STATE_PAGE; -static shared_page_data *shared_page = (shared_page_data*)SHARED_PAGE; - -void main(void) { - uint16_t moves_count = 0; - uint16_t score = 0; - int8_t done = 0; - - // By default, once we return from this, return to the DLOG module and give the master no command to execute - shared_page->master_command = MASTER_COMMAND_NONE; - shared_page->next_module_idx = 3; // Go to the DLOG module - - dlog_data *dld = (dlog_data *)(shared_page->module_data); - dlog_data *gad = (dlog_data *)(shared_page->module_data); - - // Make sure the buffers are pointing to the correct memory and are clear - clear_display_buffers(); - - // Reset the game, calculate the initial score depending on which tiles we randomly get - score = reset_game(); - - // Load the game - if(gad->mode == GAME_MODE_LOAD) { - gad->mode = GAME_MODE_NORMAL; - - memcpy(get_front_grid(), (void*)(state_page->save_grid), GRID_SIDE * GRID_SIDE); - moves_count = state_page->saved_moves_count; - score = calculate_score(); - - // We loaded an empty save, just restart the game - if (!score) score = reset_game(); - } - - // Draw the initial state of the game - draw_game_background(state_page->hi_score); - draw_number(moves_count, MOVES_TEXT_WIDTH, MOVES_TEXT_X, MOVES_TEXT_Y); - draw_number(score, SCORE_TEXT_WIDTH, SCORE_TEXT_X, SCORE_TEXT_Y); - draw_tiles(); - - // Swap graphical buffers - swap_display_buffers(); - - while(1) { // Game loop - lfsr_update(); - - switch(read_kb()) { - case K_UP: - SND_TAP(); - done = step_game(GAME_STEP_UP); - ddraw_direction_arrows(GRAPH_ARROW_UP); - break; - case K_DOWN: - SND_TAP(); - done = step_game(GAME_STEP_DOWN); - ddraw_direction_arrows(GRAPH_ARROW_DOWN); - break; - case K_LEFT: - SND_TAP(); - done = step_game(GAME_STEP_LEFT); - ddraw_direction_arrows(GRAPH_ARROW_LEFT); - break; - case K_RIGHT: - SND_TAP(); - done = step_game(GAME_STEP_RIGHT); - ddraw_direction_arrows(GRAPH_ARROW_RIGHT); - break; - case K_CTRL_R: - snd_mod_button(); - score = 0; // We'll reset the score - done = -1; - break; - case K_CTRL_S: // The following two will return early - snd_mod_button(); - memcpy((void*)(state_page->save_grid), get_front_grid(), GRID_SIDE * GRID_SIDE); - state_page->saved_moves_count = moves_count; - shared_page->master_command = MASTER_COMMAND_SAVE; - case K_CTRL_L: - snd_mod_button(); - sync_display1_buffer(); - shared_page->next_module_idx = 4; - gad->mode = GAME_MODE_LOAD; - return; - default: - continue; // Do nothing, loop again - } - - // Increase the count of moves we made (unless we lost or reset the game) - if(done >= 0) moves_count++; - - // Draw the number of moves - draw_number(moves_count, MOVES_TEXT_WIDTH, MOVES_TEXT_X, MOVES_TEXT_Y); - - // Draw the moved tiles - draw_tiles(); - - // If we have won, or we got a reset request, break out of this loop - if(done) { - score += (done > 0) ? WIN_SCORE_BONUS : 0; - draw_number(score, SCORE_TEXT_WIDTH, SCORE_TEXT_X, SCORE_TEXT_Y); - swap_display_buffers(); // Make sure we show the latest changes - break; - } - - // Unable to add a tile: we ran out of space and lost!!! - uint8_t random_tile_off = add_random_tile(); - if(!random_tile_off) { - done = -1; // Lost the game - break; - } - - score = calculate_score(); - - // Draw the score - draw_number(score, SCORE_TEXT_WIDTH, SCORE_TEXT_X, SCORE_TEXT_Y); - - swap_display_buffers(); - - // Draw the new tile directly on the front buffer, this way we make it appear with an "animation" - ddraw_single_tile(random_tile_off - 1); - } - - // Sync the display buffers - sync_display1_buffer(); - - dld->mode = (done > 0) ? DLOG_MODE_WIN : DLOG_MODE_LOSE; - dld->score = score; - - - return; -} +#include +#include + +#include + +#include "monitor_subroutines.h" +#include "utility.h" +#include "mem_map.h" +#include "shared_page.h" +#include "state_page.h" +#include "dlog_data.h" +#include "game_data.h" +#include "input.h" +#include "game_logic.h" +#include "game_hgr_graphics.h" +#include "monitor_subroutines.h" +#include "sound.h" +#include "module_list.h" + +// External initialization requirements +#pragma require __preserve_zp +#pragma require __data_initialization_needed + +#define MOVES_TEXT_X 32 +#define MOVES_TEXT_Y 61 +#define MOVES_TEXT_WIDTH 5 + +#define SCORE_TEXT_X 32 +#define SCORE_TEXT_Y 29 +#define SCORE_TEXT_WIDTH 5 + +#define HIGH_TEXT_X 32 +#define HIGH_TEXT_Y 107 + +#define WIN_SCORE_BONUS 10000 + +static state_page_data* state_page = (state_page_data*)STATE_PAGE; +static shared_page_data *shared_page = (shared_page_data*)SHARED_PAGE; + +void main(void) { + uint16_t moves_count = 0; + uint16_t score = 0; + int8_t done = 0; + + // By default, once we return from this, return to the DLOG module and give the master no command to execute + shared_page->master_command = MASTER_COMMAND_NONE; + shared_page->next_module_idx = MODULE_DLOG; // Go to the DLOG module + + dlog_data *dld = (dlog_data *)(shared_page->module_data); + dlog_data *gad = (dlog_data *)(shared_page->module_data); + + // Make sure the buffers are pointing to the correct memory and are clear + clear_display_buffers(); + + // Reset the game, calculate the initial score depending on which tiles we randomly get + score = reset_game(); + + // Load the game + if(gad->mode == GAME_MODE_LOAD) { + gad->mode = GAME_MODE_NORMAL; + + memcpy(get_front_grid(), (void*)(state_page->save_grid), GRID_SIDE * GRID_SIDE); + moves_count = state_page->saved_moves_count; + score = calculate_score(); + + // We loaded an empty save, just restart the game + if (!score) score = reset_game(); + } + + // Draw the initial state of the game + draw_game_background(state_page->hi_score); + draw_number(moves_count, MOVES_TEXT_WIDTH, MOVES_TEXT_X, MOVES_TEXT_Y); + draw_number(score, SCORE_TEXT_WIDTH, SCORE_TEXT_X, SCORE_TEXT_Y); + draw_tiles(); + + // Swap graphical buffers + swap_display_buffers(); + + while(1) { // Game loop + lfsr_update(); + + switch(read_kb()) { + case K_UP: + SND_TAP(); + done = step_game(GAME_STEP_UP); + ddraw_direction_arrows(GRAPH_ARROW_UP); + break; + case K_DOWN: + SND_TAP(); + done = step_game(GAME_STEP_DOWN); + ddraw_direction_arrows(GRAPH_ARROW_DOWN); + break; + case K_LEFT: + SND_TAP(); + done = step_game(GAME_STEP_LEFT); + ddraw_direction_arrows(GRAPH_ARROW_LEFT); + break; + case K_RIGHT: + SND_TAP(); + done = step_game(GAME_STEP_RIGHT); + ddraw_direction_arrows(GRAPH_ARROW_RIGHT); + break; + case K_CTRL_R: + snd_mod_button(); + score = 0; // We'll reset the score + done = -1; + break; + case K_CTRL_S: // The following two will return early + snd_mod_button(); + memcpy((void*)(state_page->save_grid), get_front_grid(), GRID_SIDE * GRID_SIDE); + state_page->saved_moves_count = moves_count; + shared_page->master_command = MASTER_COMMAND_SAVE; + case K_CTRL_L: + snd_mod_button(); + sync_display1_buffer(); + shared_page->next_module_idx = MODULE_GAME; + gad->mode = GAME_MODE_LOAD; + return; + default: + continue; // Do nothing, loop again + } + + // Increase the count of moves we made (unless we lost or reset the game) + if(done >= 0) moves_count++; + + // Draw the number of moves + draw_number(moves_count, MOVES_TEXT_WIDTH, MOVES_TEXT_X, MOVES_TEXT_Y); + + // Draw the moved tiles + draw_tiles(); + + // If we have won, or we got a reset request, break out of this loop + if(done) { + score += (done > 0) ? WIN_SCORE_BONUS : 0; + draw_number(score, SCORE_TEXT_WIDTH, SCORE_TEXT_X, SCORE_TEXT_Y); + swap_display_buffers(); // Make sure we show the latest changes + break; + } + + // Unable to add a tile: we ran out of space and lost!!! + uint8_t random_tile_off = add_random_tile(); + if(!random_tile_off) { + done = -1; // Lost the game + break; + } + + score = calculate_score(); + + // Draw the score + draw_number(score, SCORE_TEXT_WIDTH, SCORE_TEXT_X, SCORE_TEXT_Y); + + swap_display_buffers(); + + // Draw the new tile directly on the front buffer, this way we make it appear with an "animation" + ddraw_single_tile(random_tile_off - 1); + } + + // Sync the display buffers + sync_display1_buffer(); + + dld->mode = (done > 0) ? DLOG_MODE_WIN : DLOG_MODE_LOSE; + dld->score = score; + + + return; +} diff --git a/src/game_vdp_graphics.h b/src/game_vdp_graphics.h new file mode 100644 index 0000000..a6d4c38 --- /dev/null +++ b/src/game_vdp_graphics.h @@ -0,0 +1,19 @@ +#ifndef _GAME_VDP_GRAPHICS_HEADER_ +#define _GAME_VDP_GRAPHICS_HEADER_ + +#include + +#define JS_POS_CENTER 0 +#define JS_POS_UP 1 +#define JS_POS_DOWN 2 +#define JS_POS_LEFT 3 +#define JS_POS_RIGHT 4 + + +void vdp_clear_gamegrid(void); +void vdp_clear_dialog(void); +uint8_t vdp_draw_numtile(uint8_t type, uint8_t x, uint8_t y); +void vdp_redraw_tiles(uint8_t *grid); +void vdp_draw_joystick(uint8_t position); + +#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..b09c2e9 --- /dev/null +++ b/src/game_vdp_graphics.s @@ -0,0 +1,323 @@ + .rtmodel version,"1" + .rtmodel core,"6502" + + .extern _Zp + .extern VDP_MEM, VDP_REG + + .extern vdp_point_to_vram_xy, vdp_hide_sprite, vdp_show_sprite, vdp_set_sprite_tile + +NUM_TILE_OFFSET: .equ 0x14 + + .section code,text + +vdp_redraw_tiles: +P_GRID_H$: .equ _Zp+9 +P_GRID_L$: .equ _Zp+8 + + lda zp:_Zp+0 + sta zp:P_GRID_L$ + lda zp:_Zp+1 + sta zp:P_GRID_H$ + + ldy #24 +SetSprtLoop$: + lda (zp:P_GRID_L$),y + bne SkipHide$ + pha + tya + pha + jsr vdp_hide_sprite + pla + tay + pla + jmp SkipSprite$ +SkipHide$: + sta zp:_Zp+0 + dec zp:_Zp+0 + tya + pha + jsr vdp_set_sprite_tile + pla + tay + pha + jsr vdp_show_sprite + pla + tay +SkipSprite$: + dey + bpl SetSprtLoop$ + + ldy #24 +SetTileLoop$: + tya + pha + lda TileNum_To_X_Map,y + sta zp:_Zp+0 + lda TileNum_To_Y_Map,y + sta zp:_Zp+1 + lda (zp:P_GRID_L$),y + + jsr vdp_draw_numtile + + pla + tay + dey + bpl SetTileLoop$ + + rts + +vdp_draw_joystick: +T_NT_IDX$: .equ _Zp+4 +P_POS$: .equ _Zp+2 +T_Y$: .equ _Zp+1 +T_X$: .equ _Zp+0 + + pha + + ldx #0 + stx zp:T_NT_IDX$ + + ; Clear the table + lda #29 + sta zp:T_X$ + lda #16 + sta zp:T_Y$ + jsr vdp_point_to_vram_xy + lda #0 + sta VDP_MEM + nop + + lda #29 + sta zp:T_X$ + lda #18 + sta zp:T_Y$ + jsr vdp_point_to_vram_xy + lda #0 + sta VDP_MEM + nop + + lda #28 + sta zp:T_X$ + lda #17 + sta zp:T_Y$ + jsr vdp_point_to_vram_xy + lda #0 + sta VDP_MEM + nop ; Slow down, we're using this while the IC is rendering + nop + sta VDP_MEM + nop + nop + sta VDP_MEM + nop + nop + + ; Jump to the correct code to handle joystick drawing + pla + asl a + tax + lda _draw_joystick_jumptable$,x + sta zp:_Zp+0 + lda _draw_joystick_jumptable$+1,x + sta zp:_Zp+1 + sec + jmp (_Zp+0) + +J_Center$: + ldx #29 + ldy #17 + bcs J_Done$ +J_Up$: + ldx #29 + ldy #16 + bcs J_Done$ +J_Down$: + ldx #29 + ldy #18 + bcs J_Done$ +J_Left$: + ldx #28 + ldy #17 + bcs J_Done$ +J_Right$: + ldx #30 + ldy #17 +J_Done$: + stx zp:T_X$ + sty zp:T_Y$ + jsr vdp_point_to_vram_xy + lda #120 + sta VDP_MEM + nop + nop + + rts +_draw_joystick_jumptable$: + .word J_Center$ + .word J_Up$ + .word J_Down$ + .word J_Left$ + .word J_Right$ + +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_clear_dialog: +T_NT_IDX$: .equ _Zp+4 +T_Y$: .equ _Zp+1 +T_X$: .equ _Zp+0 + + + lda #0x01 + sta zp:T_NT_IDX$ + lda #0x04 + sta zp:T_X$ + lda #15 + sta zp:T_Y$ + ldy #9 + +ClearLine$: + jsr vdp_point_to_vram_xy + lda #0x00 + ldx #24 +ClearTile$: + sta VDP_MEM + dex + bne ClearTile$ + + dec zp:T_Y$ + dey + 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 X_To_TileNum_Map,x + sta zp:P_X$ + + ldx zp:P_Y$ + lda Y_To_TileNum_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 + nop + 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 +X_To_TileNum_Map: + .byte 0x01, 0x06, 0x0B, 0x10, 0x15 +Y_To_TileNum_Map: + .byte 0x01, 0x05, 0x09, 0x0D, 0x11 + +; The following maps convert between the tile number and the corresponding X and Y coordinates +TileNum_To_X_Map: + .byte 0x00, 0x01, 0x02, 0x03, 0x04 + .byte 0x00, 0x01, 0x02, 0x03, 0x04 + .byte 0x00, 0x01, 0x02, 0x03, 0x04 + .byte 0x00, 0x01, 0x02, 0x03, 0x04 + .byte 0x00, 0x01, 0x02, 0x03, 0x04 + +TileNum_To_Y_Map: + .byte 0x00, 0x00, 0x00, 0x00, 0x00 + .byte 0x01, 0x01, 0x01, 0x01, 0x01 + .byte 0x02, 0x02, 0x02, 0x02, 0x02 + .byte 0x03, 0x03, 0x03, 0x03, 0x03 + .byte 0x04, 0x04, 0x04, 0x04, 0x04 + +;;;;;;;;;;;;;;;;;; + .public vdp_draw_numtile + .public vdp_clear_gamegrid + .public vdp_clear_dialog + .public vdp_redraw_tiles + .public vdp_draw_joystick + \ No newline at end of file diff --git a/src/graph_misc_data.c b/src/hgr_graph_misc_data.c similarity index 98% rename from src/graph_misc_data.c rename to src/hgr_graph_misc_data.c index 0652f60..447e809 100644 --- a/src/graph_misc_data.c +++ b/src/hgr_graph_misc_data.c @@ -1,31 +1,31 @@ -#include - -const uint8_t score_pic_data[] = { - 0x00, 0x2C, 0x40, 0x40, 0x03, 0x05, 0x30, 0x00, 0x00, 0x7B, 0x70, 0x63, 0x06, 0x7B, 0x70, 0x0F, 0x00, 0x0F, - 0x78, 0x57, 0x0B, 0x47, 0x39, 0x00, 0x40, 0x03, 0x3C, 0x2E, 0x4E, 0x63, 0x39, 0x00, 0x60, 0x00, 0x3C, 0x1B, - 0x2E, 0x61, 0x39, 0x00, 0x00, 0x07, 0x5E, 0x1F, 0x6E, 0x71, 0x18, 0x00, 0x40, 0x1D, 0x4E, 0x0C, 0x66, 0x1D, - 0x7E, 0x03, 0x00, 0x3A, 0x0E, 0x4E, 0x67, 0x07, 0x7C, 0x01, 0x00, 0x30, 0x07, 0x0E, 0x77, 0x02, 0x0C, 0x00, - 0x00, 0x3C, 0x07, 0x66, 0x33, 0x06, 0x06, 0x00, 0x40, 0x1B, 0x47, 0x46, 0x3B, 0x0D, 0x06, 0x00, 0x30, 0x0F, - 0x23, 0x6E, 0x19, 0x0E, 0x7F, 0x00, 0x78, 0x01, 0x3E, 0x78, 0x18, 0x1C, 0x3F, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 -}; - -const uint8_t moves_pic_data[] = { - 0x00, 0x06, 0x00, 0x1C, 0x06, 0x30, 0x00, 0x0B, 0x00, 0x3E, 0x0C, 0x36, 0x0E, 0x72, 0x6F, 0x1E, 0x00, 0x7F, - 0x0A, 0x5D, 0x06, 0x3A, 0x60, 0x03, 0x00, 0x57, 0x4D, 0x72, 0x0E, 0x3F, 0x70, 0x00, 0x40, 0x6F, 0x46, 0x71, - 0x26, 0x39, 0x18, 0x00, 0x50, 0x33, 0x67, 0x71, 0x47, 0x1B, 0x60, 0x01, 0x60, 0x19, 0x67, 0x30, 0x67, 0x7F, - 0x33, 0x07, 0x60, 0x15, 0x73, 0x3C, 0x67, 0x7C, 0x41, 0x0E, 0x70, 0x4C, 0x73, 0x38, 0x37, 0x0C, 0x00, 0x0C, - 0x68, 0x5C, 0x33, 0x1E, 0x1F, 0x06, 0x00, 0x0F, 0x30, 0x4C, 0x31, 0x1C, 0x1F, 0x06, 0x70, 0x06, 0x38, 0x26, - 0x71, 0x0E, 0x0E, 0x7F, 0x6C, 0x03, 0x38, 0x66, 0x41, 0x07, 0x07, 0x3F, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x00 -}; - -const uint8_t high_pic_data[] = { - 0x00, 0x00, 0x06, 0x60, 0x21, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x64, 0x78, 0x51, 0x20, 0x00, 0x00, 0x00, - 0x0E, 0x6E, 0x1D, 0x72, 0x70, 0x00, 0x00, 0x00, 0x06, 0x7E, 0x1E, 0x33, 0x30, 0x00, 0x00, 0x00, 0x0E, 0x33, - 0x0E, 0x71, 0x18, 0x00, 0x00, 0x00, 0x07, 0x7B, 0x05, 0x38, 0x18, 0x00, 0x00, 0x00, 0x5F, 0x1B, 0x07, 0x79, - 0x1D, 0x00, 0x00, 0x00, 0x7B, 0x59, 0x73, 0x59, 0x0F, 0x00, 0x00, 0x40, 0x27, 0x5D, 0x43, 0x3C, 0x0A, 0x00, - 0x00, 0x20, 0x51, 0x4C, 0x61, 0x0A, 0x05, 0x00, 0x00, 0x40, 0x71, 0x4C, 0x63, 0x0C, 0x07, 0x00, 0x00, 0x60, - 0x61, 0x06, 0x31, 0x0E, 0x06, 0x00, 0x00, 0x60, 0x30, 0x02, 0x3E, 0x06, 0x03, 0x00, 0x00, 0x20, 0x20, 0x00, - 0x00, 0x02, 0x02, 0x00 -}; +#include + +const uint8_t score_pic_data[] = { + 0x00, 0x2C, 0x40, 0x40, 0x03, 0x05, 0x30, 0x00, 0x00, 0x7B, 0x70, 0x63, 0x06, 0x7B, 0x70, 0x0F, 0x00, 0x0F, + 0x78, 0x57, 0x0B, 0x47, 0x39, 0x00, 0x40, 0x03, 0x3C, 0x2E, 0x4E, 0x63, 0x39, 0x00, 0x60, 0x00, 0x3C, 0x1B, + 0x2E, 0x61, 0x39, 0x00, 0x00, 0x07, 0x5E, 0x1F, 0x6E, 0x71, 0x18, 0x00, 0x40, 0x1D, 0x4E, 0x0C, 0x66, 0x1D, + 0x7E, 0x03, 0x00, 0x3A, 0x0E, 0x4E, 0x67, 0x07, 0x7C, 0x01, 0x00, 0x30, 0x07, 0x0E, 0x77, 0x02, 0x0C, 0x00, + 0x00, 0x3C, 0x07, 0x66, 0x33, 0x06, 0x06, 0x00, 0x40, 0x1B, 0x47, 0x46, 0x3B, 0x0D, 0x06, 0x00, 0x30, 0x0F, + 0x23, 0x6E, 0x19, 0x0E, 0x7F, 0x00, 0x78, 0x01, 0x3E, 0x78, 0x18, 0x1C, 0x3F, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +const uint8_t moves_pic_data[] = { + 0x00, 0x06, 0x00, 0x1C, 0x06, 0x30, 0x00, 0x0B, 0x00, 0x3E, 0x0C, 0x36, 0x0E, 0x72, 0x6F, 0x1E, 0x00, 0x7F, + 0x0A, 0x5D, 0x06, 0x3A, 0x60, 0x03, 0x00, 0x57, 0x4D, 0x72, 0x0E, 0x3F, 0x70, 0x00, 0x40, 0x6F, 0x46, 0x71, + 0x26, 0x39, 0x18, 0x00, 0x50, 0x33, 0x67, 0x71, 0x47, 0x1B, 0x60, 0x01, 0x60, 0x19, 0x67, 0x30, 0x67, 0x7F, + 0x33, 0x07, 0x60, 0x15, 0x73, 0x3C, 0x67, 0x7C, 0x41, 0x0E, 0x70, 0x4C, 0x73, 0x38, 0x37, 0x0C, 0x00, 0x0C, + 0x68, 0x5C, 0x33, 0x1E, 0x1F, 0x06, 0x00, 0x0F, 0x30, 0x4C, 0x31, 0x1C, 0x1F, 0x06, 0x70, 0x06, 0x38, 0x26, + 0x71, 0x0E, 0x0E, 0x7F, 0x6C, 0x03, 0x38, 0x66, 0x41, 0x07, 0x07, 0x3F, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00 +}; + +const uint8_t high_pic_data[] = { + 0x00, 0x00, 0x06, 0x60, 0x21, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x64, 0x78, 0x51, 0x20, 0x00, 0x00, 0x00, + 0x0E, 0x6E, 0x1D, 0x72, 0x70, 0x00, 0x00, 0x00, 0x06, 0x7E, 0x1E, 0x33, 0x30, 0x00, 0x00, 0x00, 0x0E, 0x33, + 0x0E, 0x71, 0x18, 0x00, 0x00, 0x00, 0x07, 0x7B, 0x05, 0x38, 0x18, 0x00, 0x00, 0x00, 0x5F, 0x1B, 0x07, 0x79, + 0x1D, 0x00, 0x00, 0x00, 0x7B, 0x59, 0x73, 0x59, 0x0F, 0x00, 0x00, 0x40, 0x27, 0x5D, 0x43, 0x3C, 0x0A, 0x00, + 0x00, 0x20, 0x51, 0x4C, 0x61, 0x0A, 0x05, 0x00, 0x00, 0x40, 0x71, 0x4C, 0x63, 0x0C, 0x07, 0x00, 0x00, 0x60, + 0x61, 0x06, 0x31, 0x0E, 0x06, 0x00, 0x00, 0x60, 0x30, 0x02, 0x3E, 0x06, 0x03, 0x00, 0x00, 0x20, 0x20, 0x00, + 0x00, 0x02, 0x02, 0x00 +}; diff --git a/src/graph_misc_data.h b/src/hgr_graph_misc_data.h similarity index 70% rename from src/graph_misc_data.h rename to src/hgr_graph_misc_data.h index e3f1724..c6fd0de 100644 --- a/src/graph_misc_data.h +++ b/src/hgr_graph_misc_data.h @@ -1,18 +1,18 @@ -#ifndef _GRAPH_MISC_DATA_HEADER_ -#define _GRAPH_MISC_DATA_HEADER_ - -#include - -#define SCORE_PIC_WIDTH_BYTES 8 -#define SCORE_PIC_HEIGHT 14 -extern const uint8_t score_pic_data[]; - -#define MOVES_PIC_WIDTH_BYTES 8 -#define MOVES_PIC_HEIGHT 14 -extern const uint8_t moves_pic_data[]; - -#define HIGH_PIC_WIDTH_BYTES 8 -#define HIGH_PIC_HEIGHT 14 -extern const uint8_t high_pic_data[]; - -#endif /* _GRAPH_MISC_DATA_HEADER_ */ +#ifndef _HGR_GRAPH_MISC_DATA_HEADER_ +#define _HGR_GRAPH_MISC_DATA_HEADER_ + +#include + +#define SCORE_PIC_WIDTH_BYTES 8 +#define SCORE_PIC_HEIGHT 14 +extern const uint8_t score_pic_data[]; + +#define MOVES_PIC_WIDTH_BYTES 8 +#define MOVES_PIC_HEIGHT 14 +extern const uint8_t moves_pic_data[]; + +#define HIGH_PIC_WIDTH_BYTES 8 +#define HIGH_PIC_HEIGHT 14 +extern const uint8_t high_pic_data[]; + +#endif /* _HGR_GRAPH_MISC_DATA_HEADER_ */ diff --git a/src/hgr_graphics.h b/src/hgr_graphics.h new file mode 100644 index 0000000..4851bd9 --- /dev/null +++ b/src/hgr_graphics.h @@ -0,0 +1,8 @@ +#ifndef _HGR_GRAPHICS_HEADER_ +#define _HGR_GRAPHICS_HEADER_ + +#define SCREEN_HEIGHT 192 +#define SCREEN_WIDTH_BYTES 128 +#define BYTES_PER_LINE 40 + +#endif /* _HGR_GRAPHICS_HEADER_ */ diff --git a/src/line_data.c b/src/hgr_line_data.c similarity index 97% rename from src/line_data.c rename to src/hgr_line_data.c index 992b832..95866e3 100644 --- a/src/line_data.c +++ b/src/hgr_line_data.c @@ -1,22 +1,22 @@ -#include - -#include "utility.h" - -const uint16_t line_offset_map[SCREEN_HEIGHT] = { - 0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x0080, 0x0480, 0x0880, 0x0C80, - 0x1080, 0x1480, 0x1880, 0x1C80, 0x0100, 0x0500, 0x0900, 0x0D00, 0x1100, 0x1500, 0x1900, 0x1D00, - 0x0180, 0x0580, 0x0980, 0x0D80, 0x1180, 0x1580, 0x1980, 0x1D80, 0x0200, 0x0600, 0x0A00, 0x0E00, - 0x1200, 0x1600, 0x1A00, 0x1E00, 0x0280, 0x0680, 0x0A80, 0x0E80, 0x1280, 0x1680, 0x1A80, 0x1E80, - 0x0300, 0x0700, 0x0B00, 0x0F00, 0x1300, 0x1700, 0x1B00, 0x1F00, 0x0380, 0x0780, 0x0B80, 0x0F80, - 0x1380, 0x1780, 0x1B80, 0x1F80, 0x0028, 0x0428, 0x0828, 0x0C28, 0x1028, 0x1428, 0x1828, 0x1C28, - 0x00A8, 0x04A8, 0x08A8, 0x0CA8, 0x10A8, 0x14A8, 0x18A8, 0x1CA8, 0x0128, 0x0528, 0x0928, 0x0D28, - 0x1128, 0x1528, 0x1928, 0x1D28, 0x01A8, 0x05A8, 0x09A8, 0x0DA8, 0x11A8, 0x15A8, 0x19A8, 0x1DA8, - 0x0228, 0x0628, 0x0A28, 0x0E28, 0x1228, 0x1628, 0x1A28, 0x1E28, 0x02A8, 0x06A8, 0x0AA8, 0x0EA8, - 0x12A8, 0x16A8, 0x1AA8, 0x1EA8, 0x0328, 0x0728, 0x0B28, 0x0F28, 0x1328, 0x1728, 0x1B28, 0x1F28, - 0x03A8, 0x07A8, 0x0BA8, 0x0FA8, 0x13A8, 0x17A8, 0x1BA8, 0x1FA8, 0x0050, 0x0450, 0x0850, 0x0C50, - 0x1050, 0x1450, 0x1850, 0x1C50, 0x00D0, 0x04D0, 0x08D0, 0x0CD0, 0x10D0, 0x14D0, 0x18D0, 0x1CD0, - 0x0150, 0x0550, 0x0950, 0x0D50, 0x1150, 0x1550, 0x1950, 0x1D50, 0x01D0, 0x05D0, 0x09D0, 0x0DD0, - 0x11D0, 0x15D0, 0x19D0, 0x1DD0, 0x0250, 0x0650, 0x0A50, 0x0E50, 0x1250, 0x1650, 0x1A50, 0x1E50, - 0x02D0, 0x06D0, 0x0AD0, 0x0ED0, 0x12D0, 0x16D0, 0x1AD0, 0x1ED0, 0x0350, 0x0750, 0x0B50, 0x0F50, - 0x1350, 0x1750, 0x1B50, 0x1F50, 0x03D0, 0x07D0, 0x0BD0, 0x0FD0, 0x13D0, 0x17D0, 0x1BD0, 0x1FD0 -}; +#include + +#include "hgr_graphics.h" + +const uint16_t line_offset_map[SCREEN_HEIGHT] = { + 0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x0080, 0x0480, 0x0880, 0x0C80, + 0x1080, 0x1480, 0x1880, 0x1C80, 0x0100, 0x0500, 0x0900, 0x0D00, 0x1100, 0x1500, 0x1900, 0x1D00, + 0x0180, 0x0580, 0x0980, 0x0D80, 0x1180, 0x1580, 0x1980, 0x1D80, 0x0200, 0x0600, 0x0A00, 0x0E00, + 0x1200, 0x1600, 0x1A00, 0x1E00, 0x0280, 0x0680, 0x0A80, 0x0E80, 0x1280, 0x1680, 0x1A80, 0x1E80, + 0x0300, 0x0700, 0x0B00, 0x0F00, 0x1300, 0x1700, 0x1B00, 0x1F00, 0x0380, 0x0780, 0x0B80, 0x0F80, + 0x1380, 0x1780, 0x1B80, 0x1F80, 0x0028, 0x0428, 0x0828, 0x0C28, 0x1028, 0x1428, 0x1828, 0x1C28, + 0x00A8, 0x04A8, 0x08A8, 0x0CA8, 0x10A8, 0x14A8, 0x18A8, 0x1CA8, 0x0128, 0x0528, 0x0928, 0x0D28, + 0x1128, 0x1528, 0x1928, 0x1D28, 0x01A8, 0x05A8, 0x09A8, 0x0DA8, 0x11A8, 0x15A8, 0x19A8, 0x1DA8, + 0x0228, 0x0628, 0x0A28, 0x0E28, 0x1228, 0x1628, 0x1A28, 0x1E28, 0x02A8, 0x06A8, 0x0AA8, 0x0EA8, + 0x12A8, 0x16A8, 0x1AA8, 0x1EA8, 0x0328, 0x0728, 0x0B28, 0x0F28, 0x1328, 0x1728, 0x1B28, 0x1F28, + 0x03A8, 0x07A8, 0x0BA8, 0x0FA8, 0x13A8, 0x17A8, 0x1BA8, 0x1FA8, 0x0050, 0x0450, 0x0850, 0x0C50, + 0x1050, 0x1450, 0x1850, 0x1C50, 0x00D0, 0x04D0, 0x08D0, 0x0CD0, 0x10D0, 0x14D0, 0x18D0, 0x1CD0, + 0x0150, 0x0550, 0x0950, 0x0D50, 0x1150, 0x1550, 0x1950, 0x1D50, 0x01D0, 0x05D0, 0x09D0, 0x0DD0, + 0x11D0, 0x15D0, 0x19D0, 0x1DD0, 0x0250, 0x0650, 0x0A50, 0x0E50, 0x1250, 0x1650, 0x1A50, 0x1E50, + 0x02D0, 0x06D0, 0x0AD0, 0x0ED0, 0x12D0, 0x16D0, 0x1AD0, 0x1ED0, 0x0350, 0x0750, 0x0B50, 0x0F50, + 0x1350, 0x1750, 0x1B50, 0x1F50, 0x03D0, 0x07D0, 0x0BD0, 0x0FD0, 0x13D0, 0x17D0, 0x1BD0, 0x1FD0 +}; diff --git a/src/hgr_line_data.h b/src/hgr_line_data.h new file mode 100644 index 0000000..9466fe5 --- /dev/null +++ b/src/hgr_line_data.h @@ -0,0 +1,10 @@ +#ifndef _HGR_LINE_DATA_HEADER_ +#define _HGR_LINE_DATA_HEADER_ + +#include + +#include "hgr_graphics.h" + +extern const uint16_t line_offset_map[SCREEN_HEIGHT]; + +#endif /* _HGR_LINE_DATA_HEADER_ */ diff --git a/src/intro_main.c b/src/intro_main.c index 4595ad0..f40cfe5 100644 --- a/src/intro_main.c +++ b/src/intro_main.c @@ -1,39 +1,46 @@ -#include -#include - -#include - -#include "monitor_subroutines.h" -#include "utility.h" -#include "mem_map.h" -#include "shared_page.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) { - uint8_t wait_count = 0x40; - - // Initialize the register for LFSR - lfsr_init(0xF00D); - - // 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 - - // Delay a bit - while(wait_count--) WAIT(0xFF); - - // Clear the display memory - memset((void*)DISPLAY_PAGE_2, 0, DISPLAY_PAGE_SIZE); - memset((void*)DISPLAY_PAGE_1, 0, DISPLAY_PAGE_SIZE); - - shared_page->master_command = MASTER_COMMAND_NONE; - shared_page->next_module_idx = 3; // DLOG module is next! - - return; -} +#include +#include + +#include + +#include "vdp_utils.h" + +#include "monitor_subroutines.h" +#include "utility.h" +#include "input.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) { + uint8_t wait_count = 0x40; + uint8_t vdp_detected; + + // Initialize the register for LFSR + lfsr_init(0xF00D); + + // Clear the memory used to pass parameters to the next module + memset((void*)(shared_page->module_data), 0, MODULE_DATA_SIZE); + + // Detect expansion hardware, and choose what to load according to that + vdp_detected = vdp_detect() && !read_any_key(); + + // Delay a bit + while(wait_count--) WAIT(0xFF); + + // Clear the display memory + memset((void*)DISPLAY_PAGE_2, 0, DISPLAY_PAGE_SIZE); + memset((void*)DISPLAY_PAGE_1, 0, DISPLAY_PAGE_SIZE); + + shared_page->master_command = MASTER_COMMAND_NONE; + 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/line_data.h b/src/line_data.h deleted file mode 100644 index 219744e..0000000 --- a/src/line_data.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _LINE_DATA_HEADER_ -#define _LINE_DATA_HEADER_ - -#include -#include "utility.h" - -extern const uint16_t line_offset_map[SCREEN_HEIGHT]; - -#endif /* _LINE_DATA_HEADER */ diff --git a/src/master_main.c b/src/master_main.c index 4c5b4c0..85e9880 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 10 #define FNAME_LEN 6 #define STATE_FILE_IDX 1 @@ -34,10 +34,14 @@ static shared_page_data * shared_page = (shared_page_data*)SHARED_PAGE; static const uint8_t file_table[FILE_LIST_LEN][FNAME_LEN] = { { 0x80 | 'L', 0x80 | 'O', 0x80 | 'A', 0x80 | 'D', 0x80 | 'S', 0x00}, // LOADS (this is not an executable, but will be used to show the loading screen). { 0x80 | 'S', 0x80 | 'T', 0x80 | 'A', 0x80 | 'T', 0x80 | 'E', 0x00}, // STATE (this is not an executable, but will be used to save/load the game and scores). - { 0x80 | 'I', 0x80 | 'N', 0x80 | 'T', 0x80 | 'R', 0x80 | 'O', 0x00}, // INTRO (this executable will show the initial presentation picture) + { 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), + { 0x80 | 'V', 0x80 | 'D', 0x80 | 'D', 0x80 | 'L', 0x80 | 'G', 0x00}, // VDDLG (VDP startup, win, lose dialogs), + { 0x80 | 'V', 0x80 | 'D', 0x80 | 'G', 0x80 | 'A', 0x80 | 'M', 0x00}, // VDGAM (Game, VDP version), + { 0x80 | 'V', 0x80 | 'D', 0x80 | 'D', 0x80 | 'E', 0x80 | 'M', 0x00}, // VDDEM (Demo, VDP version), }; 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 +52,10 @@ static uint16_t file_load_address[FILE_LIST_LEN] = { // This will hold the load MODULE_PAGE, MODULE_PAGE, MODULE_PAGE, + MODULE_PAGE, + MODULE_PAGE, + MODULE_PAGE, + MODULE_PAGE, }; static void init(void); @@ -123,17 +131,14 @@ __task int main(void) { uint8_t cur_file = 0; uint8_t keep_going = 1; - __disable_interrupts(); + __disable_interrupts(); // Keep the interrupts disabled in the master module init(); init_floppy_data(&cur_trk, &cur_file); - - __enable_interrupts(); do { if((cur_file != shared_page->next_module_idx) || (shared_page->master_command == MASTER_COMMAND_SAVE)) { - __disable_interrupts(); dii_power_on(DEFAULT_DRIVE_CONTROLLER_OFFSET, 0); // Check if we need to load another module @@ -151,7 +156,6 @@ __task int main(void) { } dii_power_off(DEFAULT_DRIVE_CONTROLLER_OFFSET); - __enable_interrupts(); } shared_page->master_command = MASTER_COMMAND_NONE; diff --git a/src/mem_registers.h b/src/mem_registers.h index 5ca2394..5c11921 100644 --- a/src/mem_registers.h +++ b/src/mem_registers.h @@ -1,48 +1,51 @@ -#ifndef _MEM_REGISTERS_HEADER_ -#define _MEM_REGISTERS_HEADER_ - -#include - -#define ROM_MONITOR 0xFF61 - -#define ZP_WNDLFT 0x0020 // 0, left column of scroll window -#define ZP_WNDWDTH 0x0021 // 40, width of scroll window -#define ZP_WNDTOP 0x0022 // 0, top line of the scroll window -#define ZP_WNDBTM 0x0023 // 24, bottom line of the scroll window - -#define ZP_CH 0x0024 // Displacement from window left for the cursor -#define ZP_CV 0x0025 // Displacement from top of screen (not window!) for the cursor - -#define ZP_INVFLAG 0x0032 // Either 0x00 or 0x7F, set text color inversion -#define ZP_PROMPT 0x0033 // Prompt character - -#define ZP_RND 0x004E // Note that this is a 16bit register incremented by the RDKEY func - -#define P3_PWRDUP_REF 0x03F3 -#define P3_PWRDUP 0x03F4 // Already-powered-up indicator. If it is set to the content of 0x03F3 XOR'd with 0xA5, the soft reset vector is considered valid - - -#define DATAIN_KB_MASK 0x3F -#define DATAIN_PRNT_MASK 0x40 -#define DATAIN_TAPEIN_MASK 0x80 - -#define IO_DATAOUT 0xC000 // (W) To keyboard and printer port -#define IO_DATAIN 0xC010 // (R) Data input from keyboard (0:5), printer (6) and tape (7) -#define IO_TAPEOUT 0xC020 // (R) Data output for tape, read from here to output bit on tape -#define IO_SPEAKER 0xC030 // (R) Speaker toggle -#define IO_DISPLAY_COLOR 0xC050 // (R / W) Access here to enable the colorburst -#define IO_DISPLAY_BW 0xC051 // (R / W) Access here to disable the colorburst -#define IO_MTA_OFF 0xC052 // (?) -#define IO_MTA_ON 0xC053 // (?) -#define IO_DISPLAY_PAGE1 0xC054 // (R / W) Access here to select the primary display page -#define IO_DISPLAY_PAGE2 0xC055 // (R / W) Access here to select the secondary display page -#define IO_MTB_OFF 0xC056 // (?) -#define IO_MTB_ON 0xC057 // (?) -#define IO_PRNT_STRB_LO 0xC058 // (R / W) Access LO/HI/LO or HI/LO/HI consecutively depending on the type of strobe pulse to create -#define IO_PRNT_STRB_HI 0xC059 // (R / W) -#define IO_ROMSEL 0xC05A // (R / W) Access here will make region C100-FFFF a ROM area -#define IO_RAMSEL 0xC05B // (R / W) Access here will make region C100-FFFF a RAM area -#define IO_KB_CTRL_LOW 0xC05E // (R / W) Set the CTRL line to 0, access is through DATAIN -#define IO_KB_CTRL_HI 0xC05F // (R / W) Set the CTRL line to 1 - -#endif /* _MEM_REGISTERS_HEADER_ */ +#ifndef _MEM_REGISTERS_HEADER_ +#define _MEM_REGISTERS_HEADER_ + +#include + +#define NMI_HANDLER_ADDRESS 0x03FB +#define IRQ_HANDLER_ADDRESS 0x03FE + +#define ROM_MONITOR 0xFF61 + +#define ZP_WNDLFT 0x0020 // 0, left column of scroll window +#define ZP_WNDWDTH 0x0021 // 40, width of scroll window +#define ZP_WNDTOP 0x0022 // 0, top line of the scroll window +#define ZP_WNDBTM 0x0023 // 24, bottom line of the scroll window + +#define ZP_CH 0x0024 // Displacement from window left for the cursor +#define ZP_CV 0x0025 // Displacement from top of screen (not window!) for the cursor + +#define ZP_INVFLAG 0x0032 // Either 0x00 or 0x7F, set text color inversion +#define ZP_PROMPT 0x0033 // Prompt character + +#define ZP_RND 0x004E // Note that this is a 16bit register incremented by the RDKEY func + +#define P3_PWRDUP_REF 0x03F3 +#define P3_PWRDUP 0x03F4 // Already-powered-up indicator. If it is set to the content of 0x03F3 XOR'd with 0xA5, the soft reset vector is considered valid + + +#define DATAIN_KB_MASK 0x3F +#define DATAIN_PRNT_MASK 0x40 +#define DATAIN_TAPEIN_MASK 0x80 + +#define IO_DATAOUT 0xC000 // (W) To keyboard and printer port +#define IO_DATAIN 0xC010 // (R) Data input from keyboard (0:5), printer (6) and tape (7) +#define IO_TAPEOUT 0xC020 // (R) Data output for tape, read from here to output bit on tape +#define IO_SPEAKER 0xC030 // (R) Speaker toggle +#define IO_DISPLAY_COLOR 0xC050 // (R / W) Access here to enable the colorburst +#define IO_DISPLAY_BW 0xC051 // (R / W) Access here to disable the colorburst +#define IO_MTA_OFF 0xC052 // (?) +#define IO_MTA_ON 0xC053 // (?) +#define IO_DISPLAY_PAGE1 0xC054 // (R / W) Access here to select the primary display page +#define IO_DISPLAY_PAGE2 0xC055 // (R / W) Access here to select the secondary display page +#define IO_MTB_OFF 0xC056 // (?) +#define IO_MTB_ON 0xC057 // (?) +#define IO_PRNT_STRB_LO 0xC058 // (R / W) Access LO/HI/LO or HI/LO/HI consecutively depending on the type of strobe pulse to create +#define IO_PRNT_STRB_HI 0xC059 // (R / W) +#define IO_ROMSEL 0xC05A // (R / W) Access here will make region C100-FFFF a ROM area +#define IO_RAMSEL 0xC05B // (R / W) Access here will make region C100-FFFF a RAM area +#define IO_KB_CTRL_LOW 0xC05E // (R / W) Set the CTRL line to 0, access is through DATAIN +#define IO_KB_CTRL_HI 0xC05F // (R / W) Set the CTRL line to 1 + +#endif /* _MEM_REGISTERS_HEADER_ */ diff --git a/src/module_list.h b/src/module_list.h new file mode 100644 index 0000000..fb9f821 --- /dev/null +++ b/src/module_list.h @@ -0,0 +1,13 @@ +#ifndef _MODULE_LIST_HEADER_ +#define _MODULE_LIST_HEADER_ + +#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/utility.c b/src/utility.c index e019be9..fb29948 100644 --- a/src/utility.c +++ b/src/utility.c @@ -1,65 +1,77 @@ -#include "utility.h" - -#include "mem_registers.h" -#include "monitor_subroutines.h" -#include "line_data.h" - -void num_to_decbuf(uint16_t n, uint8_t len, uint8_t *buf) { - - for(uint8_t idx = 0; idx < len; idx++) { - buf[idx] = n % 10; - n /= 10; - } -} - -// https://stackoverflow.com/questions/2602823/in-c-c-whats-the-simplest-way-to-reverse-the-order-of-bits-in-a-byte -uint8_t bit_reverse(uint8_t b) { - b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; - b = (b & 0xCC) >> 2 | (b & 0x33) << 2; - b = (b & 0xAA) >> 1 | (b & 0x55) << 1; - - return b; -} - -// https://stackoverflow.com/questions/14009765/fastest-way-to-count-bits -uint8_t bit_count(uint8_t b) { - b = (b & 0x55) + (b >> 1 & 0x55); - b = (b & 0x33) + (b >> 2 & 0x33); - b = (b & 0x0f) + (b >> 4 & 0x0f); - - return b; -} - -void lfsr_init(uint16_t reg) { - *((uint16_t*)LFSR_REGISTER_ADDRESS) = 0xF00D; -} - -uint16_t lfsr_update(void) { - uint16_t *lfsr = ((uint16_t*)LFSR_REGISTER_ADDRESS); - - *lfsr ^= (*lfsr) >> 7; - *lfsr ^= (*lfsr) << 9; - *lfsr ^= (*lfsr) >> 13; - - return *lfsr; -} - -#define CRC8RDALLAS_POLY 0x31 -uint8_t calculate_crc8(uint8_t* data, uint8_t len) { - uint8_t crc = 0; - - for(uint8_t data_idx = 0; data_idx < len; data_idx++) { - uint8_t carry; - uint8_t d = data[data_idx]; - - for (uint8_t i = 8; i > 0; i--) { - carry = (crc & 0x80); - crc <<= 1; - if (d & 1) crc |= 1; - d >>= 1; - if (carry) crc ^= CRC8RDALLAS_POLY; - } - } - - return crc; -} +#include "utility.h" + +#include "mem_registers.h" +#include "monitor_subroutines.h" + +void decbuf_to_ascii(uint8_t len, uint8_t *buf) { + for(uint8_t idx = 0; idx < len; idx++) { + buf[idx] += 0x30; + } + + // Swap the order in the array + uint8_t tmp; + for(uint8_t idx = 0; idx < len/2; idx++) { + tmp = buf[idx]; + buf[idx] = buf[(len - 1) - idx]; + buf[(len - 1) - idx] = tmp; + } +} + +void num_to_decbuf(uint16_t n, uint8_t len, uint8_t *buf) { + for(uint8_t idx = 0; idx < len; idx++) { + buf[idx] = n % 10; + n /= 10; + } +} + +// https://stackoverflow.com/questions/2602823/in-c-c-whats-the-simplest-way-to-reverse-the-order-of-bits-in-a-byte +uint8_t bit_reverse(uint8_t b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + + return b; +} + +// https://stackoverflow.com/questions/14009765/fastest-way-to-count-bits +uint8_t bit_count(uint8_t b) { + b = (b & 0x55) + (b >> 1 & 0x55); + b = (b & 0x33) + (b >> 2 & 0x33); + b = (b & 0x0f) + (b >> 4 & 0x0f); + + return b; +} + +void lfsr_init(uint16_t reg) { + *((uint16_t*)LFSR_REGISTER_ADDRESS) = 0xF00D; +} + +uint16_t lfsr_update(void) { + uint16_t *lfsr = ((uint16_t*)LFSR_REGISTER_ADDRESS); + + *lfsr ^= (*lfsr) >> 7; + *lfsr ^= (*lfsr) << 9; + *lfsr ^= (*lfsr) >> 13; + + return *lfsr; +} + +#define CRC8RDALLAS_POLY 0x31 +uint8_t calculate_crc8(uint8_t* data, uint8_t len) { + uint8_t crc = 0; + + for(uint8_t data_idx = 0; data_idx < len; data_idx++) { + uint8_t carry; + uint8_t d = data[data_idx]; + + for (uint8_t i = 8; i > 0; i--) { + carry = (crc & 0x80); + crc <<= 1; + if (d & 1) crc |= 1; + d >>= 1; + if (carry) crc ^= CRC8RDALLAS_POLY; + } + } + + return crc; +} diff --git a/src/utility.h b/src/utility.h index d30bb0a..96fc869 100644 --- a/src/utility.h +++ b/src/utility.h @@ -1,34 +1,31 @@ -#ifndef _UTILITY_HEADER_ -#define _UTILITY_HEADER_ - -#include - -#define SCREEN_HEIGHT 192 -#define SCREEN_WIDTH_BYTES 128 -#define BYTES_PER_LINE 40 - -#define PEEKZ(a) (*(volatile uint8_t* __attribute__((zpage)))(a)) -#define POKEZ(a, b) ((*(volatile uint8_t* __attribute__((zpage)))(a)) = b) - -#define PEEK(a) (*(volatile uint8_t*)(a)) -#define POKE(a, b) ((*(volatile uint8_t*)(a)) = b) - -#define PEEKW(a) (*(volatile uint16_t*)(a)) -#define POKEW(a, b) ((*(volatile uint16_t*)(a)) = b) - -#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) - -// 0x356 - 0x357 actually fall inside the -// decoding table for DISK II sector reading, -// but they're unused bytes! So we can use them -// to store the lfsr_update -#define LFSR_REGISTER_ADDRESS 0x0356 - -void num_to_decbuf(uint16_t n, uint8_t len, uint8_t *buf); -uint8_t bit_reverse(uint8_t b); -uint8_t bit_count(uint8_t b); -void lfsr_init(uint16_t reg); -uint16_t lfsr_update(void); -uint8_t calculate_crc8(uint8_t* data, uint8_t len); - -#endif /* _UTILITY_HEADER_ */ +#ifndef _UTILITY_HEADER_ +#define _UTILITY_HEADER_ + +#include + +#define PEEKZ(a) (*(volatile uint8_t* __attribute__((zpage)))(a)) +#define POKEZ(a, b) ((*(volatile uint8_t* __attribute__((zpage)))(a)) = b) + +#define PEEK(a) (*(volatile uint8_t*)(a)) +#define POKE(a, b) ((*(volatile uint8_t*)(a)) = b) + +#define PEEKW(a) (*(volatile uint16_t*)(a)) +#define POKEW(a, b) ((*(volatile uint16_t*)(a)) = b) + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + +// 0x356 - 0x357 actually fall inside the +// decoding table for DISK II sector reading, +// but they're unused bytes! So we can use them +// to store the lfsr_update +#define LFSR_REGISTER_ADDRESS 0x0356 + +void decbuf_to_ascii(uint8_t len, uint8_t *buf); +void num_to_decbuf(uint16_t n, uint8_t len, uint8_t *buf); +uint8_t bit_reverse(uint8_t b); +uint8_t bit_count(uint8_t b); +void lfsr_init(uint16_t reg); +uint16_t lfsr_update(void); +uint8_t calculate_crc8(uint8_t* data, uint8_t len); + +#endif /* _UTILITY_HEADER_ */ diff --git a/src/vddem_main.c b/src/vddem_main.c new file mode 100644 index 0000000..de44d1c --- /dev/null +++ b/src/vddem_main.c @@ -0,0 +1,197 @@ +#include +#include + +#include + +#include "vdp_utils.h" +#include "game_vdp_graphics.h" + +#include "monitor_subroutines.h" +#include "utility.h" +#include "mem_map.h" +#include "mem_registers.h" +#include "shared_page.h" +#include "state_page.h" +#include "dlog_data.h" +#include "game_data.h" +#include "input.h" +#include "game_logic.h" +#include "monitor_subroutines.h" +#include "sound.h" +#include "module_list.h" + +// External initialization requirements +#pragma require __preserve_zp +#pragma require __data_initialization_needed + +#define HSCORE_TEXT_X 27 +#define HSCORE_TEXT_Y 13 + +#define SCORE_TEXT_X 27 +#define SCORE_TEXT_Y 4 + +#define MOVES_TEXT_X 27 +#define MOVES_TEXT_Y 8 + +#define BOTTOM_TEXT_X 1 +#define BOTTOM_TEXT_Y 23 + +#define MAX_DEMO_MOVES 30 + +#define RUNS_TO_SKIP 4000 + +static state_page_data* state_page = (state_page_data*)STATE_PAGE; +static shared_page_data *shared_page = (shared_page_data*)SHARED_PAGE; +static uint8_t text_buf[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static volatile uint8_t key_pressed = 0; + +void main(void) { + uint16_t moves_count = 0; + uint16_t score = 0; + int8_t done = 0; + + __disable_interrupts(); // Make sure the interrupts are disabled + + // By default, once we return from this, return to the DLOG module and give the master no command to execute + shared_page->master_command = MASTER_COMMAND_NONE; + shared_page->next_module_idx = MODULE_DLOG_VDP; // Go to the DLOG module + + dlog_data *dld = (dlog_data *)(shared_page->module_data); + dlog_data *gad = (dlog_data *)(shared_page->module_data); + + // Make sure the buffers are pointing to the correct memory and are clear + vdp_hide_sprites(); + vdp_clear_gamegrid(); + vdp_switch_nt(0); // Make sure VDP shows the gamegrid + + // Setup the IRQ handler + POKEW(IRQ_HANDLER_ADDRESS, (uint16_t)vdp_irq_handler); + + // Reset the game, calculate the initial score depending on which tiles we randomly get + score = reset_game(); + + // Draw the initial state of the game + vdp_print_string(0, BOTTOM_TEXT_X, BOTTOM_TEXT_Y, "DEMO MODE Press any key to exit"); + + num_to_decbuf(state_page->hi_score, 5, text_buf); // High score + decbuf_to_ascii(5, text_buf); + vdp_print_string(0, HSCORE_TEXT_X, HSCORE_TEXT_Y, (char*)text_buf); + + num_to_decbuf(moves_count, 5, text_buf); // Moves count + decbuf_to_ascii(5, text_buf); + vdp_print_string(0, MOVES_TEXT_X, MOVES_TEXT_Y, (char*)text_buf); + + num_to_decbuf(score, 5, text_buf); // Score + decbuf_to_ascii(5, text_buf); + vdp_print_string(0, SCORE_TEXT_X, SCORE_TEXT_Y, (char*)text_buf); + + vdp_draw_joystick(JS_POS_CENTER); // Center the joystick + + vdp_redraw_tiles(get_front_grid()); + + uint16_t lfsr; + uint16_t current_run = 0; + + __enable_interrupts(); + while(1) { // Game loop + lfsr = lfsr_update(); + + // Any key will let us out of this, wait some time for a keypress + __disable_interrupts(); + if(read_any_key()) { + snd_mod_button(); + done = 1; + } + + if(!done && current_run <= RUNS_TO_SKIP) { + current_run++; + __enable_interrupts(); + continue; + } + + __disable_interrupts(); + current_run = 0; + + if(!done) { + switch((lfsr & 0x0003) + 1) { + case K_UP: + SND_TAP(); + vdp_draw_joystick(JS_POS_UP); + done = step_game(GAME_STEP_UP); + break; + case K_DOWN: + SND_TAP(); + vdp_draw_joystick(JS_POS_DOWN); + done = step_game(GAME_STEP_DOWN); + break; + case K_LEFT: + SND_TAP(); + vdp_draw_joystick(JS_POS_LEFT); + done = step_game(GAME_STEP_LEFT); + break; + case K_RIGHT: + SND_TAP(); + vdp_draw_joystick(JS_POS_RIGHT); + done = step_game(GAME_STEP_RIGHT); + break; + default: + __enable_interrupts(); + continue; // Do nothing, loop again + } + } + + + // Increase the count of moves we made (unless we lost or reset the game) + if(done >= 0) { + moves_count++; + done = done || (moves_count >= MAX_DEMO_MOVES); + } + + // Draw the number of moves + num_to_decbuf(moves_count, 5, text_buf); + decbuf_to_ascii(5, text_buf); + vdp_print_string(0, MOVES_TEXT_X, MOVES_TEXT_Y, (char*)text_buf); + + // Draw the moved tiles + vdp_redraw_tiles(get_front_grid()); + + // If we have won, or we got a reset request, break out of this loop + if(done) { + num_to_decbuf(score, 5, text_buf); // Score + decbuf_to_ascii(5, text_buf); + vdp_print_string(0, SCORE_TEXT_X, SCORE_TEXT_Y, (char*)text_buf); + + break; + } + + // Unable to add a tile: we ran out of space and lost!!! + uint8_t random_tile_off = add_random_tile(); + if(!random_tile_off) { + done = -1; // Lost the game + break; + } + + + score = calculate_score(); + + // Draw the score + num_to_decbuf(score, 5, text_buf); // Score + decbuf_to_ascii(5, text_buf); + vdp_print_string(0, SCORE_TEXT_X, SCORE_TEXT_Y, (char*)text_buf); + vdp_redraw_tiles(get_front_grid()); + vdp_draw_joystick(JS_POS_CENTER); + + __enable_interrupts(); + } + + __disable_interrupts(); + + // Always go back to the start dialog + dld->mode = DLOG_MODE_START; + dld->score = 0; + + vdp_redraw_tiles(get_front_grid()); + + return; +} diff --git a/src/vddlg_main.c b/src/vddlg_main.c new file mode 100644 index 0000000..905f997 --- /dev/null +++ b/src/vddlg_main.c @@ -0,0 +1,96 @@ +#include +#include + +#include + +#include "vdp_utils.h" +#include "game_vdp_graphics.h" +#include "monitor_subroutines.h" +#include "utility.h" +#include "mem_map.h" +#include "shared_page.h" +#include "state_page.h" +#include "dlog_data.h" +#include "game_data.h" +#include "input.h" +#include "sound.h" +#include "module_list.h" + +// External initialization requirements +#pragma require __preserve_zp +#pragma require __data_initialization_needed + +static state_page_data* state_page = (state_page_data*)STATE_PAGE; +static shared_page_data *shared_page = (shared_page_data*)SHARED_PAGE; +static uint8_t score_buf[7] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static char game_ver[5] = {'V', GAME_VER_CH0, GAME_VER_CH1, GAME_VER_CH2, '\0'}; + +#define WAIT_COUNTER_END 0xFFFF + +void main(void) { + uint16_t wait_counter = 0; + dlog_data *dld = (dlog_data *)(shared_page->module_data); + game_data *gad = (game_data *)(shared_page->module_data); + + __disable_interrupts(); // Make sure the interrupts are disabled + + num_to_decbuf(state_page->hi_score, 6, score_buf); + decbuf_to_ascii(6, score_buf); + + vdp_hide_sprites(); + vdp_clear_dialog(); + vdp_switch_nt(1); // Make sure VDP shows the correct dialog nametable + + vdp_print_string(1, 6, 10, "Current High-Score :"); + vdp_print_string(1, 13, 12, (char*)score_buf); + vdp_print_string(1, 5, 15, "Press any key to START"); + vdp_print_string(1, 1, 23, "hkz@social.chinwag.org"); + vdp_print_string(1, 28, 23, game_ver); + + switch(dld->mode) { + case DLOG_MODE_WIN: + snd_festive(); + vdp_print_string(1, 11, 8, "YOU WIN!!!"); + break; + case DLOG_MODE_LOSE: + snd_sad_scale(); + vdp_print_string(1, 11, 8, "GAME OVER!"); + break; + default: + case DLOG_MODE_START: + vdp_print_string(1, 7, 8, "WELCOME TO TK2048!"); + snd_start(); + break; + }; + + if(dld->score > state_page->hi_score) { // New high-score. We need to save it. + state_page->hi_score = dld->score; + + // Update the score! + num_to_decbuf(state_page->hi_score, 6, score_buf); + decbuf_to_ascii(6, score_buf); + vdp_print_string(1, 13, 12, (char*)score_buf); + vdp_print_string(1, 8, 13, "!NEW HIGH SCORE!"); + + shared_page->master_command = MASTER_COMMAND_SAVE; + } else { + shared_page->master_command = MASTER_COMMAND_NONE; + } + + shared_page->next_module_idx = MODULE_GAME_VDP; // Go to the GAME module + gad->mode = GAME_MODE_NORMAL; // Set the proper start mode for the game + + while(!read_any_key() && (wait_counter != WAIT_COUNTER_END)) { + wait_counter++; + lfsr_update(); + } + + if (wait_counter == WAIT_COUNTER_END) { + shared_page->next_module_idx = MODULE_DEMO_VDP; // Actually go to the DEMO module + return; + } + + snd_mod_button(); + + return; +} diff --git a/src/vdgam_main.c b/src/vdgam_main.c new file mode 100644 index 0000000..9ae1d1d --- /dev/null +++ b/src/vdgam_main.c @@ -0,0 +1,198 @@ +#include +#include + +#include + +#include "vdp_utils.h" +#include "game_vdp_graphics.h" + +#include "monitor_subroutines.h" +#include "utility.h" +#include "mem_map.h" +#include "mem_registers.h" +#include "shared_page.h" +#include "state_page.h" +#include "dlog_data.h" +#include "game_data.h" +#include "input.h" +#include "game_logic.h" +#include "monitor_subroutines.h" +#include "sound.h" +#include "module_list.h" + +// External initialization requirements +#pragma require __preserve_zp +#pragma require __data_initialization_needed + +#define HSCORE_TEXT_X 27 +#define HSCORE_TEXT_Y 13 + +#define SCORE_TEXT_X 27 +#define SCORE_TEXT_Y 4 + +#define MOVES_TEXT_X 27 +#define MOVES_TEXT_Y 8 + +#define BOTTOM_TEXT_X 1 +#define BOTTOM_TEXT_Y 23 + +#define WIN_SCORE_BONUS 10000 + +static state_page_data* state_page = (state_page_data*)STATE_PAGE; +static shared_page_data *shared_page = (shared_page_data*)SHARED_PAGE; +static uint8_t text_buf[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +void main(void) { + uint16_t moves_count = 0; + uint16_t score = 0; + int8_t done = 0; + + __disable_interrupts(); // Make sure the interrupts are disabled + + // By default, once we return from this, return to the DLOG module and give the master no command to execute + shared_page->master_command = MASTER_COMMAND_NONE; + shared_page->next_module_idx = MODULE_DLOG_VDP; // Go to the dialog module + + dlog_data *dld = (dlog_data *)(shared_page->module_data); + dlog_data *gad = (dlog_data *)(shared_page->module_data); + + // Make sure the buffers are pointing to the correct memory and are clear + vdp_hide_sprites(); + vdp_clear_gamegrid(); + vdp_switch_nt(0); // Make sure VDP shows the gamegrid + + // Setup the IRQ handler + POKEW(IRQ_HANDLER_ADDRESS, (uint16_t)vdp_irq_handler); + + // Reset the game, calculate the initial score depending on which tiles we randomly get + score = reset_game(); + + // Load the game + if(gad->mode == GAME_MODE_LOAD) { + gad->mode = GAME_MODE_NORMAL; + + memcpy(get_front_grid(), (void*)(state_page->save_grid), GRID_SIDE * GRID_SIDE); + moves_count = state_page->saved_moves_count; + score = calculate_score(); + + // We loaded an empty save, just restart the game + if (!score) score = reset_game(); + } + + // Draw the initial state of the game + vdp_print_string(0, BOTTOM_TEXT_X, BOTTOM_TEXT_Y, "hkz@social.chinwag.org 2025"); + + num_to_decbuf(state_page->hi_score, 5, text_buf); // High score + decbuf_to_ascii(5, text_buf); + vdp_print_string(0, HSCORE_TEXT_X, HSCORE_TEXT_Y, (char*)text_buf); + + num_to_decbuf(moves_count, 5, text_buf); // Moves count + decbuf_to_ascii(5, text_buf); + vdp_print_string(0, MOVES_TEXT_X, MOVES_TEXT_Y, (char*)text_buf); + + num_to_decbuf(score, 5, text_buf); // Score + decbuf_to_ascii(5, text_buf); + vdp_print_string(0, SCORE_TEXT_X, SCORE_TEXT_Y, (char*)text_buf); + + vdp_draw_joystick(JS_POS_CENTER); // Center the joystick + + vdp_redraw_tiles(get_front_grid()); + + __enable_interrupts(); + + while(1) { // Game loop + lfsr_update(); + + __disable_interrupts(); + switch(read_kb()) { + case K_UP: + SND_TAP(); + vdp_draw_joystick(JS_POS_UP); + done = step_game(GAME_STEP_UP); + break; + case K_DOWN: + SND_TAP(); + vdp_draw_joystick(JS_POS_DOWN); + done = step_game(GAME_STEP_DOWN); + break; + case K_LEFT: + SND_TAP(); + vdp_draw_joystick(JS_POS_LEFT); + done = step_game(GAME_STEP_LEFT); + break; + case K_RIGHT: + SND_TAP(); + vdp_draw_joystick(JS_POS_RIGHT); + done = step_game(GAME_STEP_RIGHT); + break; + case K_CTRL_R: + snd_mod_button(); + + score = 0; // We'll reset the score + done = -1; + break; + case K_CTRL_S: // The following two will return early + snd_mod_button(); + + memcpy((void*)(state_page->save_grid), get_front_grid(), GRID_SIDE * GRID_SIDE); + state_page->saved_moves_count = moves_count; + shared_page->master_command = MASTER_COMMAND_SAVE; + case K_CTRL_L: + snd_mod_button(); + + shared_page->next_module_idx = MODULE_GAME_VDP; + gad->mode = GAME_MODE_LOAD; + return; + default: + __enable_interrupts(); + continue; // Do nothing, loop again + } + + // Increase the count of moves we made (unless we lost or reset the game) + if(done >= 0) moves_count++; + + num_to_decbuf(moves_count, 5, text_buf); // Moves count + decbuf_to_ascii(5, text_buf); + vdp_print_string(0, MOVES_TEXT_X, MOVES_TEXT_Y, (char*)text_buf); + + // If we have won, or we got a reset request, break out of this loop + if(done) { + score += (done > 0) ? WIN_SCORE_BONUS : 0; + + num_to_decbuf(score, 5, text_buf); // Score + decbuf_to_ascii(5, text_buf); + vdp_print_string(0, SCORE_TEXT_X, SCORE_TEXT_Y, (char*)text_buf); + + break; + } + + // Unable to add a tile: we ran out of space and lost!!! + uint8_t random_tile_off = add_random_tile(); + if(!random_tile_off) { + done = -1; // Lost the game + break; + } + + score = calculate_score(); + + // Draw the score + num_to_decbuf(score, 5, text_buf); // Score + decbuf_to_ascii(5, text_buf); + vdp_print_string(0, SCORE_TEXT_X, SCORE_TEXT_Y, (char*)text_buf); + vdp_redraw_tiles(get_front_grid()); + vdp_draw_joystick(JS_POS_CENTER); + + __enable_interrupts(); + } + + dld->mode = (done > 0) ? DLOG_MODE_WIN : DLOG_MODE_LOSE; + dld->score = score; + + vdp_redraw_tiles(get_front_grid()); + + WAIT(0xFF); + + __disable_interrupts(); + + return; +} 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..a033dcc --- /dev/null +++ b/src/vdp_utils.h @@ -0,0 +1,20 @@ +#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_write_interleaved_sat(void); +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..69b37c4 --- /dev/null +++ b/src/vdp_utils.s @@ -0,0 +1,665 @@ + .rtmodel version,"1" + .rtmodel core,"6502" + + .extern _Zp + .extern VDP_MEM, VDP_REG + + ;;; Define a space + .section intrzp, noinit, root ; root, as we always require it + .space 0xA9 + + .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 + nop + lda zp:T_VADD_H$ + ora #0x40 + sta VDP_REG + nop + + 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 + nop + +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 + nop + nop + 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 + nop + 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 + nop + + 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 + nop + lda #0x40 + sta VDP_REG + nop + + lda #0x55 + sta VDP_MEM + nop + lda #0xAA + sta VDP_MEM + nop + + lda #0x00 + sta VDP_REG + nop + sta VDP_REG + nop + + eor VDP_MEM + nop + eor VDP_MEM + nop + + 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 + nop + lda #0x82 + sta VDP_REG + nop + 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 + nop + iny + cpy #16 + bne RegLoop$ + + rts + +;;; 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 + +_irq_save_zp: + ldx #0 + +SaveZp$: + lda zp:_Zp,x + sta .sectionStart intrzp,x + inx + ;cpx #.sectionSize intrzp + cpx #10 ; Save only part of the ZP, we're not using more + bne SaveZp$ + + rts + +_irq_restore_zp: + ldx #0 + +RestoreZp$: + lda .sectionStart intrzp,x + sta zp:_Zp,x + inx + ;cpx #.sectionSize intrzp + cpx #10 ; Restpre only part of the ZP, we're not using more + bne RestoreZp$ + + rts + +vdp_irq_handler: + ; Save the registers + pha + txa + pha + tya + pha + + ;;; Save ZP + jsr _irq_save_zp + + jsr vdp_write_interleaved_sat + + ;;; Restore ZP + jsr _irq_restore_zp + + ;;; Clear the IRQ + lda VDP_REG + + ; Restore the registers + pla + tay + pla + tax + pla + + rti + +;;;;;;;;;;;;;;;;;; + .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_write_interleaved_sat + .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..4d58aa7 --- /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_VDP; + + return; +}