diff --git a/.gitignore b/.gitignore index fd68d6f..d5cb625 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,6 @@ to_compress/ *.exe .vscode/ .venv/ -tools/payload-generator/payload-generator +tools/rom-value-generator/payload-generator text_errors.txt terminal_output.txt diff --git a/Makefile b/Makefile index 6906a76..7e779c2 100644 --- a/Makefile +++ b/Makefile @@ -190,7 +190,7 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ -I$(CURDIR)/$(GENERATED_DIR) \ -I$(CURDIR)/$(BUILD) \ - -I$(CURDIR)/tools/payload-generator/include + -I$(CURDIR)/tools/rom-value-generator/include export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) @@ -199,7 +199,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) GENERATE_STAMP := $(BUILD)/.generate_data.$(BUILD_LANG).$(BUILD_TYPE).stamp BUILD_STAMP := $(BUILD)/.build.$(BUILD_LANG).$(BUILD_TYPE).stamp -PAYLOAD_GEN_INPUTS := $(shell find tools/payload-generator/src tools/payload-generator/include -type f \( -name "*.cpp" -o -name "*.h" -o -name "*.hpp" \)) +PAYLOAD_GEN_INPUTS := $(shell find tools/rom-value-generator/src tools/rom-value-generator/include -type f \( -name "*.cpp" -o -name "*.h" -o -name "*.hpp" \)) TEXT_HELPER_INPUTS := tools/text_helper/main.py $(wildcard tools/text_helper/fonts/*.png) $(wildcard tools/text_helper/text.xlsx) TEXT_GENERATED_OUTPUTS := \ $(GENERATED_DIR)/translated_text.h \ @@ -249,11 +249,13 @@ $(GENERATE_STAMP): $(TEXT_HELPER_INPUTS) $(PAYLOAD_GEN_INPUTS) compress_lz10.sh CXXFLAGS= \ LDFLAGS= \ AR=ar \ - $(MAKE) -C tools/payload-generator BUILD_LANG=$(BUILD_LANG) BUILD_TYPE=$(BUILD_TYPE) + $(MAKE) -C tools/rom-value-generator BUILD_LANG=$(BUILD_LANG) BUILD_TYPE=$(BUILD_TYPE) + $(MAKE) -C tools/gb-payload-generator @echo @echo "----------------------------------------------------------------" @echo - @tools/payload-generator/payload-generator to_compress + @tools/rom-value-generator/payload-generator to_compress + @cp -a tools/gb-payload-generator/build/. to_compress/ @echo "Compressing bin files!" @echo -n "[" @find to_compress -name "*.bin" -print0 | xargs -0 -n1 ./compress_lz10.sh @@ -285,7 +287,8 @@ $(BUILD): #--------------------------------------------------------------------------------- clean: @echo clean ... - @$(MAKE) -C tools/payload-generator clean + @$(MAKE) -C tools/rom-value-generator clean + @$(MAKE) -C tools/gb-payload-generator clean @$(MAKE) -C loader clean @$(MAKE) -C PCCS clean @rm -fr $(BUILD) $(TARGET).elf $(TARGET).gba $(LOADERNAME).gba data/ to_compress/ diff --git a/include/link_handler.h b/include/link_handler.h index 51857c1..bacbf1d 100644 --- a/include/link_handler.h +++ b/include/link_handler.h @@ -27,10 +27,10 @@ enum ConnectionState { - HS, - ACK, + CLOCK, MENU, - PRETRADE, + WAIT_FOR_TRADE, + TRADE_PREAMBLE, TRADE, PARTY_PREAMBLE, COLOSSEUM, @@ -47,7 +47,7 @@ enum ConnectionState struct LinkState { - ConnectionState conState = HS; + ConnectionState conState = CLOCK; uint8_t in_data; uint8_t out_data; diff --git a/source/dbg/debug_mode.cpp b/source/dbg/debug_mode.cpp index a9314eb..e4826c0 100644 --- a/source/dbg/debug_mode.cpp +++ b/source/dbg/debug_mode.cpp @@ -2,7 +2,7 @@ debug_options g_debug_options = { - .print_link_data = (false && DEBUG_MODE), + .print_link_data = (true && DEBUG_MODE), .instant_text_speed = (true && DEBUG_MODE), .ignore_game_pak = (true && DEBUG_MODE), .ignore_game_pak_sprites = (false && DEBUG_MODE), diff --git a/source/link_handler.cpp b/source/link_handler.cpp index 46274bb..fb88852 100644 --- a/source/link_handler.cpp +++ b/source/link_handler.cpp @@ -22,6 +22,9 @@ LinkSPI linkSPIInstance; LinkSPI *linkSPI = &linkSPIInstance; LinkState *currLinkState; +// TODO get rid of this because it should not exist and should be done with math +byte tradePreambleBytes[] = {0x00, 0x00, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD}; + // Here's a compilation check to ensure that the size of these structs match our expectations. // Just update it if you changed the struct members. The data-generator process prints their actual sizes. static_assert(sizeof(struct GB_ROM) == 136); @@ -99,67 +102,51 @@ void setup(byte *box_data_storage, byte *curr_payload, GB_ROM *curr_rom, PokeBox byte handleIncomingByte() { + // TODO this should be moved outside of this function. + currLinkState->section_data_counter += 1; switch (currLinkState->conState) { - case HS: + case CLOCK: currLinkState->mosi_delay = 4; - if (currLinkState->curr_gb_rom->generation == 2) + if (currLinkState->in_data == 0xFE) { - currLinkState->conState = ACK; + currLinkState->conState = MENU; return 0x00; } - if (currLinkState->in_data == 0x00) - { - currLinkState->conState = ACK; - return 0x01; - } + return 0x01; break; - - case ACK: - if (currLinkState->curr_gb_rom->generation == 2) + /* + case ACK: + if (currLinkState->in_data == 0x61) + { + currLinkState->conState = MENU; + return 0x61; + } + else if (currLinkState->in_data == 0x00) + { + currLinkState->conState = MENU; + return 0x00; + } + break; + */ + case MENU: + if (currLinkState->in_data == 0xD0 || currLinkState->in_data == 0x61) { + currLinkState->conState = WAIT_FOR_TRADE; + if (currLinkState->in_data == 0xD0) + { + currLinkState->gen = 1; + return 0xD4; + } if (currLinkState->in_data == 0x61) { - currLinkState->conState = MENU; + currLinkState->gen = 2; return 0x61; } - return 0x01; - } - else - { - if (currLinkState->in_data == 0x00) - { - currLinkState->conState = MENU; - return 0x00; - } - else if (currLinkState->in_data == 0x02) - { - currLinkState->conState = HS; - return 0x02; - } - } - break; - - case MENU: - if (currLinkState->in_data == 0x60 || currLinkState->in_data == 0x61) - { - { - u8 general_text_table_buffer[2048]; - text_data_table general_text(general_text_table_buffer); - - general_text.decompress(get_compressed_text_table(GENERAL_INDEX)); - ptgb_write_textbox(general_text.get_text_entry(GENERAL_link_success), true, false, - GENERAL_INDEX, GENERAL_link_success, false); - } - - link_animation_state(STATE_NO_ANIM); - currLinkState->conState = PRETRADE; - currLinkState->section_data_counter = 0; - return currLinkState->in_data; } else if (currLinkState->in_data == 0x02) { - currLinkState->conState = HS; + currLinkState->conState = CLOCK; return 0x02; } else @@ -168,111 +155,101 @@ byte handleIncomingByte() } break; - case PRETRADE: - if (currLinkState->section_data_counter == 16) + case WAIT_FOR_TRADE: + if (currLinkState->in_data == 0xFD) + { + REG_TM3D = -0x0040; + currLinkState->section_data_counter = 0; + currLinkState->conState = TRADE_PREAMBLE; + return 0x00; + } + return currLinkState->in_data; + break; + case TRADE_PREAMBLE: + if (currLinkState->section_data_counter == 10) { currLinkState->section_data_counter = 0; currLinkState->conState = TRADE; - } - currLinkState->section_data_counter++; - if (currLinkState->curr_gb_rom->generation == 2) - { - return 0x61; - } - else if (currLinkState->curr_gb_rom->generation == 1 && currLinkState->curr_gb_rom->version == YELLOW_ID) - { - return 0xD5; - } - else - { - return 0xD4; - } - break; + return 0xFD; + }; + return tradePreambleBytes[currLinkState->section_data_counter]; case TRADE: - if (currLinkState->in_data == 0xfd) - { - { - u8 general_text_table_buffer[2048]; - text_data_table general_text(general_text_table_buffer); - - general_text.decompress(get_compressed_text_table(GENERAL_INDEX)); - ptgb_write_textbox(general_text.get_text_entry(GENERAL_transferring), true, false, - GENERAL_INDEX, GENERAL_transferring, false); - } - - link_animation_state(STATE_TRANSFER); - // currLinkState->mosi_delay = 1; - REG_TM3D = -0x0040; - currLinkState->conState = PARTY_PREAMBLE; - } - return currLinkState->in_data; - break; - case PARTY_PREAMBLE: - if (currLinkState->in_data != 0xfd) - { - currLinkState->conState = TRADE_DATA; - return exchange_parties(currLinkState->in_data, currLinkState->curr_payload); - } - return currLinkState->in_data; - break; - case TRADE_DATA: - if (currLinkState->section_data_counter >= currLinkState->curr_gb_rom->payload_size) - { - if (currLinkState->in_data == 0xFD) - { - currLinkState->conState = BOX_PREAMBLE; - currLinkState->init_packet = true; - } - else - { - return 0x00; - } - } - return exchange_parties(currLinkState->in_data, currLinkState->curr_payload); - break; - case BOX_PREAMBLE: - if (currLinkState->in_data != 0xFD) - { - currLinkState->conState = BOX_DATA; - return exchange_boxes(currLinkState->in_data, currLinkState->box_data_storage, currLinkState->curr_gb_rom, currLinkState->debug_charset); - } - return currLinkState->in_data; - break; - case BOX_DATA: - return exchange_boxes(currLinkState->in_data, currLinkState->box_data_storage, currLinkState->curr_gb_rom, currLinkState->debug_charset); - break; - case REBOOT: - currLinkState->section_data_counter = 0; - currLinkState->conState = REMOVE_ARRAY_PREAMBLE; - return 0xFD; - break; - - case REMOVE_ARRAY_PREAMBLE: - if (currLinkState->in_data != 0xFD) - { - currLinkState->conState = SEND_REMOVE_ARRAY; - return exchange_remove_array(currLinkState->in_data, currLinkState->box, currLinkState->cancel_connection); - } - return currLinkState->in_data; - break; - - case SEND_REMOVE_ARRAY: - if (currLinkState->section_data_counter >= 29) // This assumes the preamble does not count towards the number of bytes sent over the cable - { - currLinkState->conState = END2; - } - currLinkState->section_data_counter++; - return exchange_remove_array(currLinkState->in_data, currLinkState->box, currLinkState->cancel_connection); + return currLinkState->curr_payload[currLinkState->section_data_counter]; break; default: return currLinkState->in_data; break; } - - return 0; // This should never hit but the compiler likes this being here + return 0; } /* +case PARTY_PREAMBLE: + if (currLinkState->in_data != 0xfd) + { + currLinkState->conState = TRADE_DATA; + return exchange_parties(currLinkState->in_data, currLinkState->curr_payload); + } + return currLinkState->in_data; + break; +case TRADE_DATA: + if (currLinkState->section_data_counter >= currLinkState->curr_gb_rom->payload_size) + { + if (currLinkState->in_data == 0xFD) + { + currLinkState->conState = BOX_PREAMBLE; + currLinkState->init_packet = true; + } + else + { + return 0x00; + } + } + return exchange_parties(currLinkState->in_data, currLinkState->curr_payload); + break; +case BOX_PREAMBLE: + if (currLinkState->in_data != 0xFD) + { + currLinkState->conState = BOX_DATA; + return exchange_boxes(currLinkState->in_data, currLinkState->box_data_storage, currLinkState->curr_gb_rom, currLinkState->debug_charset); + } + return currLinkState->in_data; + break; +case BOX_DATA: + return exchange_boxes(currLinkState->in_data, currLinkState->box_data_storage, currLinkState->curr_gb_rom, currLinkState->debug_charset); + break; +case REBOOT: + currLinkState->section_data_counter = 0; + currLinkState->conState = REMOVE_ARRAY_PREAMBLE; + return 0xFD; + break; + +case REMOVE_ARRAY_PREAMBLE: + if (currLinkState->in_data != 0xFD) + { + currLinkState->conState = SEND_REMOVE_ARRAY; + return exchange_remove_array(currLinkState->in_data, currLinkState->box, currLinkState->cancel_connection); + } + return currLinkState->in_data; + break; + +case SEND_REMOVE_ARRAY: + if (currLinkState->section_data_counter >= 29) // This assumes the preamble does not count towards the number of bytes sent over the cable + { + currLinkState->conState = END2; + } + currLinkState->section_data_counter++; + return exchange_remove_array(currLinkState->in_data, currLinkState->box, currLinkState->cancel_connection); + break; + +default: + return currLinkState->in_data; + break; +} + +return 0; // This should never hit but the compiler likes this being here +}*/ +/* int loop(byte *box_data_storage, byte *curr_payload, GB_ROM *curr_gb_rom, PokeBox *box, const u16 *debug_charset, bool cancel_connection) { #define LINE_WIDTH 21 @@ -400,12 +377,12 @@ ConnectionState prevConState; void handshake() { - if (g_debug_options.print_link_data && key_hit(KEY_SELECT)) + if (g_debug_options.print_link_data && key_hit(KEY_LEFT)) { currLinkState->irq_enabled = false; } - else if (g_debug_options.print_link_data && key_hit(KEY_START)) + else if (g_debug_options.print_link_data && key_hit(KEY_RIGHT)) { currLinkState->irq_enabled = true; } diff --git a/source/pokemon_party.cpp b/source/pokemon_party.cpp index c9c67ba..235143d 100644 --- a/source/pokemon_party.cpp +++ b/source/pokemon_party.cpp @@ -15,6 +15,10 @@ #include "ptgb_save_data_manager.h" #include "libraries/Pokemon-Gen3-to-Gen-X/include/save.h" +#include "payload_rb_experimental_lz10_bin.h" +#include "payload_rb_lz10_bin.h" +#include "payload_y_experimental_lz10_bin.h" + static const byte gen1_rb_debug_box_data[0x462] = { // Num of Pokemon 0x14, @@ -183,8 +187,9 @@ void Pokemon_Party::start_link() // This used to clear out the box data, probably isn't needed anymore // memset(box_data_array, 0, curr_gb_rom.box_data_size); - //last_error = loop(&box_data_array[0], current_payload, &curr_gb_rom, &box, debug_charset, false); - while(true){ + // last_error = loop(&box_data_array[0], current_payload, &curr_gb_rom, &box, debug_charset, false); + while (true) + { VBlankIntrWait(); }; box.loadData(curr_gb_rom.generation, (Language)curr_gb_rom.language, box_data_array); @@ -212,7 +217,7 @@ void Pokemon_Party::continue_link(bool cancel_connection) load_localized_charset(debug_charset, 3, ENGLISH); - //last_error = loop(&box_data_array[0], current_payload, &curr_gb_rom, &box, debug_charset, cancel_connection); + // last_error = loop(&box_data_array[0], current_payload, &curr_gb_rom, &box, debug_charset, cancel_connection); } } @@ -354,6 +359,7 @@ void Pokemon_Party::init_payload() // WARNING: Ensure sure decompression_buffer is large enough! + /* if (curr_gb_rom.generation == 1) { if (curr_gb_rom.version == YELLOW_ID) @@ -368,7 +374,10 @@ void Pokemon_Party::init_payload() else // if(curr_gb_rom.generation == 2) { payload_src = gb_gen2_payloads_lz10_bin; - } + }*/ + + // For now this is going to be hardcoded, just as a test. + payload_src = payload_rb_experimental_lz10_bin; // byte 2-4 of the compressed data store the decompressed size payload_file_size = payload_src[1] | (payload_src[2] << 8) | (payload_src[3] << 16); diff --git a/tools/gb-payload-generator/Makefile b/tools/gb-payload-generator/Makefile new file mode 100644 index 0000000..1f77623 --- /dev/null +++ b/tools/gb-payload-generator/Makefile @@ -0,0 +1,44 @@ +SHELL := /bin/bash + +ASM_DIR := src/payloads +OUTPUT_DIR := build +DEPENDENCIES_DIR := dependencies +OBJECT_FILE := $(OUTPUT_DIR)/output.o + +PAYLOADS := $(notdir $(wildcard $(ASM_DIR)/*.asm)) +DEPENDENCY_FILES := $(notdir $(wildcard $(DEPENDENCIES_DIR)/*.asm)) + +.PHONY: all base language_specific clean + +all: base language_specific + +base: | $(OUTPUT_DIR) + @set -e; \ + cd $(ASM_DIR); \ + for file in $(PAYLOADS); do \ + echo "Building base payload $$file"; \ + rgbasm -o ../../$(OBJECT_FILE) "./$$file"; \ + rgblink -x -o "../../$(OUTPUT_DIR)/$${file%%.*}.bin" ../../$(OBJECT_FILE); \ + done + +language_specific: | $(OUTPUT_DIR) + @set -e; \ + if [ -z "$(strip $(DEPENDENCY_FILES))" ]; then \ + echo "No dependency asm files found in $(DEPENDENCIES_DIR); skipping language-specific payloads."; \ + exit 0; \ + fi; \ + cd $(ASM_DIR); \ + for file in $(PAYLOADS); do \ + for dependency in $(DEPENDENCY_FILES); do \ + language_version="$${dependency%%.asm}"; \ + echo "Building $$file with $$language_version"; \ + rgbasm -P "../../$(DEPENDENCIES_DIR)/$$dependency" -o ../../$(OBJECT_FILE) "./$$file"; \ + rgblink -x -o "../../$(OUTPUT_DIR)/$${file%%.*}_$${language_version}.bin" ../../$(OBJECT_FILE); \ + done; \ + done + +$(OUTPUT_DIR): + @mkdir -p $(OUTPUT_DIR) + +clean: + @rm -rf $(OUTPUT_DIR) diff --git a/tools/gb-payload-generator/include/constants/charmap.asm b/tools/gb-payload-generator/include/constants/charmap.asm new file mode 100644 index 0000000..f096797 --- /dev/null +++ b/tools/gb-payload-generator/include/constants/charmap.asm @@ -0,0 +1,386 @@ +; $00-$17 are TX_* constants (see macros/scripts/text.asm) + +; Control characters (see home/text.asm) + + charmap "", $00 + charmap "", $49 + charmap "", $4a ; "" + charmap "<_CONT>", $4b ; implements "" + charmap "", $4c + charmap "", $4e + charmap "", $4f + charmap "@", $50 ; string terminator + charmap "", $51 + charmap "", $52 ; wPlayerName + charmap "", $53 ; wRivalName + charmap "#", $54 ; "POKé" + charmap "", $55 + charmap "<……>", $56 ; "……" + charmap "", $57 + charmap "", $58 + charmap "", $59 + charmap "", $5a + charmap "", $5b ; "PC" + charmap "", $5c ; "TM" + charmap "", $5d ; "TRAINER" + charmap "", $5e ; "ROCKET" + charmap "", $5f + +; Actual characters (from gfx/font/font_extra.png) + + charmap "", $60 ; unused + charmap "", $61 ; unused + charmap "", $62 ; unused + charmap "", $63 ; unused + charmap "", $64 ; unused + charmap "", $65 ; unused + charmap "", $66 ; unused + charmap "", $67 ; unused + charmap "", $68 ; unused + charmap "", $69 + charmap "", $6a + charmap "", $6b ; unused + charmap "", $6c ; unused + charmap "", $6d ; colon with tinier dots than ":" + charmap "ぃ", $6e ; hiragana small i, unused + charmap "ぅ", $6f ; hiragana small u, unused + charmap "‘", $70 ; opening single quote + charmap "’", $71 ; closing single quote + charmap "“", $72 ; opening quote + charmap "”", $73 ; closing quote + charmap "·", $74 ; middle dot, unused + charmap "…", $75 ; ellipsis + charmap "ぁ", $76 ; hiragana small a, unused + charmap "ぇ", $77 ; hiragana small e, unused + charmap "ぉ", $78 ; hiragana small o, unused + + charmap "┌", $79 + charmap "─", $7a + charmap "┐", $7b + charmap "│", $7c + charmap "└", $7d + charmap "┘", $7e + charmap " ", $7f + +; Actual characters (from gfx/font/font_battle_extra.png) + + charmap "", $6e + + charmap "", $70 ; narrow "to" + + charmap "『", $72 ; Japanese opening quote, unused + charmap "", $73 + charmap "№", $74 + +; Actual characters (from other graphics files) + + ; needed for ShowPokedexDataInternal (see engine/menus/pokedex.asm) + charmap "′", $60 ; gfx/pokedex/pokedex.png + charmap "″", $61 ; gfx/pokedex/pokedex.png + + ; needed for StatusScreen (see engine/pokemon/status_screen.asm) + charmap "", $72 ; gfx/font/P.1bpp + + ; needed for LoadTownMap_Fly (see engine/items/town_map.asm) + charmap "▲", $ed ; gfx/town_map/up_arrow.1bpp + + ; needed for PrintAlphabet (see engine/menus/naming_screen.asm) + charmap "", $f0 ; gfx/font/ED.1bpp + +; Actual characters (from gfx/font/font.png) + + charmap "A", $80 + charmap "B", $81 + charmap "C", $82 + charmap "D", $83 + charmap "E", $84 + charmap "F", $85 + charmap "G", $86 + charmap "H", $87 + charmap "I", $88 + charmap "J", $89 + charmap "K", $8a + charmap "L", $8b + charmap "M", $8c + charmap "N", $8d + charmap "O", $8e + charmap "P", $8f + charmap "Q", $90 + charmap "R", $91 + charmap "S", $92 + charmap "T", $93 + charmap "U", $94 + charmap "V", $95 + charmap "W", $96 + charmap "X", $97 + charmap "Y", $98 + charmap "Z", $99 + + charmap "(", $9a + charmap ")", $9b + charmap ":", $9c + charmap ";", $9d + charmap "[", $9e + charmap "]", $9f + + charmap "a", $a0 + charmap "b", $a1 + charmap "c", $a2 + charmap "d", $a3 + charmap "e", $a4 + charmap "f", $a5 + charmap "g", $a6 + charmap "h", $a7 + charmap "i", $a8 + charmap "j", $a9 + charmap "k", $aa + charmap "l", $ab + charmap "m", $ac + charmap "n", $ad + charmap "o", $ae + charmap "p", $af + charmap "q", $b0 + charmap "r", $b1 + charmap "s", $b2 + charmap "t", $b3 + charmap "u", $b4 + charmap "v", $b5 + charmap "w", $b6 + charmap "x", $b7 + charmap "y", $b8 + charmap "z", $b9 + + charmap "é", $ba + charmap "'d", $bb + charmap "'l", $bc + charmap "'s", $bd + charmap "'t", $be + charmap "'v", $bf + + charmap "'", $e0 + charmap "", $e1 + charmap "", $e2 + charmap "-", $e3 + + charmap "'r", $e4 + charmap "'m", $e5 + + charmap "?", $e6 + charmap "!", $e7 + charmap ".", $e8 + + charmap "ァ", $e9 ; katakana small a, unused + charmap "ゥ", $ea ; katakana small u, unused + charmap "ェ", $eb ; katakana small e, unused + + charmap "▷", $ec + charmap "▶", $ed + charmap "▼", $ee + charmap "♂", $ef + charmap "¥", $f0 + charmap "×", $f1 + charmap "", $f2 ; decimal point; same as "." in English + charmap "/", $f3 + charmap ",", $f4 + charmap "♀", $f5 + + charmap "0", $f6 + charmap "1", $f7 + charmap "2", $f8 + charmap "3", $f9 + charmap "4", $fa + charmap "5", $fb + charmap "6", $fc + charmap "7", $fd + charmap "8", $fe + charmap "9", $ff + + +; Japanese kana, for those bits of text that were not translated to English + + charmap "ガ", $05 + charmap "ギ", $06 + charmap "グ", $07 + charmap "ゲ", $08 + charmap "ゴ", $09 + charmap "ザ", $0a + charmap "ジ", $0b + charmap "ズ", $0c + charmap "ゼ", $0d + charmap "ゾ", $0e + charmap "ダ", $0f + charmap "ヂ", $10 + charmap "ヅ", $11 + charmap "デ", $12 + charmap "ド", $13 + + charmap "バ", $19 + charmap "ビ", $1a + charmap "ブ", $1b + charmap "ボ", $1c + + charmap "が", $26 + charmap "ぎ", $27 + charmap "ぐ", $28 + charmap "げ", $29 + charmap "ご", $2a + charmap "ざ", $2b + charmap "じ", $2c + charmap "ず", $2d + charmap "ぜ", $2e + charmap "ぞ", $2f + charmap "だ", $30 + charmap "ぢ", $31 + charmap "づ", $32 + charmap "で", $33 + charmap "ど", $34 + + charmap "ば", $3a + charmap "び", $3b + charmap "ぶ", $3c + charmap "べ", $3d + charmap "ぼ", $3e + + charmap "パ", $40 + charmap "ピ", $41 + charmap "プ", $42 + charmap "ポ", $43 + charmap "ぱ", $44 + charmap "ぴ", $45 + charmap "ぷ", $46 + charmap "ぺ", $47 + charmap "ぽ", $48 + + charmap "「", $70 + charmap "」", $71 + charmap "』", $73 + charmap "⋯", $75 + + charmap " ", $7f + + charmap "ア", $80 + charmap "イ", $81 + charmap "ウ", $82 + charmap "エ", $83 + charmap "オ", $84 + charmap "カ", $85 + charmap "キ", $86 + charmap "ク", $87 + charmap "ケ", $88 + charmap "コ", $89 + charmap "サ", $8a + charmap "シ", $8b + charmap "ス", $8c + charmap "セ", $8d + charmap "ソ", $8e + charmap "タ", $8f + charmap "チ", $90 + charmap "ツ", $91 + charmap "テ", $92 + charmap "ト", $93 + charmap "ナ", $94 + charmap "ニ", $95 + charmap "ヌ", $96 + charmap "ネ", $97 + charmap "ノ", $98 + charmap "ハ", $99 + charmap "ヒ", $9a + charmap "フ", $9b + charmap "ホ", $9c + charmap "マ", $9d + charmap "ミ", $9e + charmap "ム", $9f + charmap "メ", $a0 + charmap "モ", $a1 + charmap "ヤ", $a2 + charmap "ユ", $a3 + charmap "ヨ", $a4 + charmap "ラ", $a5 + charmap "ル", $a6 + charmap "レ", $a7 + charmap "ロ", $a8 + charmap "ワ", $a9 + charmap "ヲ", $aa + charmap "ン", $ab + charmap "ッ", $ac + charmap "ャ", $ad + charmap "ュ", $ae + charmap "ョ", $af + charmap "ィ", $b0 + + charmap "あ", $b1 + charmap "い", $b2 + charmap "う", $b3 + charmap "え", $b4 + charmap "お", $b5 + charmap "か", $b6 + charmap "き", $b7 + charmap "く", $b8 + charmap "け", $b9 + charmap "こ", $ba + charmap "さ", $bb + charmap "し", $bc + charmap "す", $bd + charmap "せ", $be + charmap "そ", $bf + charmap "た", $c0 + charmap "ち", $c1 + charmap "つ", $c2 + charmap "て", $c3 + charmap "と", $c4 + charmap "な", $c5 + charmap "に", $c6 + charmap "ぬ", $c7 + charmap "ね", $c8 + charmap "の", $c9 + charmap "は", $ca + charmap "ひ", $cb + charmap "ふ", $cc + charmap "へ", $cd + charmap "ほ", $ce + charmap "ま", $cf + charmap "み", $d0 + charmap "む", $d1 + charmap "め", $d2 + charmap "も", $d3 + charmap "や", $d4 + charmap "ゆ", $d5 + charmap "よ", $d6 + charmap "ら", $d7 + charmap "り", $d8 + charmap "る", $d9 + charmap "れ", $da + charmap "ろ", $db + charmap "わ", $dc + charmap "を", $dd + charmap "ん", $de + charmap "っ", $df + charmap "ゃ", $e0 + charmap "ゅ", $e1 + charmap "ょ", $e2 + + charmap "ー", $e3 + charmap "゚", $e4 + charmap "゙", $e5 + + charmap "?", $e6 + charmap "!", $e7 + charmap "。", $e8 + + charmap "円", $f0 + + charmap ".", $f2 + charmap "/", $f3 + + charmap "ォ", $f4 + + charmap "0", $f6 + charmap "1", $f7 + charmap "2", $f8 + charmap "3", $f9 + charmap "4", $fa + charmap "5", $fb + charmap "6", $fc + charmap "7", $fd + charmap "8", $fe + charmap "9", $ff diff --git a/tools/gb-payload-generator/include/constants/hardware.inc b/tools/gb-payload-generator/include/constants/hardware.inc new file mode 100644 index 0000000..e1e3b2c --- /dev/null +++ b/tools/gb-payload-generator/include/constants/hardware.inc @@ -0,0 +1,1110 @@ +;****************************************************************************** +; Game Boy hardware constant definitions +; https://github.com/gbdev/hardware.inc +;****************************************************************************** + +; To the extent possible under law, the authors of this work have +; waived all copyright and related or neighboring rights to the work. +; See https://creativecommons.org/publicdomain/zero/1.0/ for details. +; SPDX-License-Identifier: CC0-1.0 + +; If this file was already included, don't do it again +if !def(HARDWARE_INC) + +; Check for the minimum supported RGBDS version +if !def(__RGBDS_MAJOR__) || !def(__RGBDS_MINOR__) || !def(__RGBDS_PATCH__) + fail "This version of 'hardware.inc' requires RGBDS version 0.5.0 or later" +endc +if __RGBDS_MAJOR__ == 0 && __RGBDS_MINOR__ < 5 + fail "This version of 'hardware.inc' requires RGBDS version 0.5.0 or later." +endc + +; Define the include guard and the current hardware.inc version +; (do this after the RGBDS version check since the `def` syntax depends on it) +def HARDWARE_INC equ 1 +def HARDWARE_INC_VERSION equs "5.3.0" + +; Usage: rev_Check_hardware_inc +; Examples: +; rev_Check_hardware_inc 1.2.3 +; rev_Check_hardware_inc 1.2 (equivalent to 1.2.0) +; rev_Check_hardware_inc 1 (equivalent to 1.0.0) +MACRO rev_Check_hardware_inc + if _NARG == 1 ; Actual invocation by the user + def hw_inc_cur_ver\@ equs strrpl("{HARDWARE_INC_VERSION}", ".", ",") + def hw_inc_min_ver\@ equs strrpl("\1", ".", ",") + rev_Check_hardware_inc {hw_inc_cur_ver\@}, {hw_inc_min_ver\@}, 0, 0 + purge hw_inc_cur_ver\@, hw_inc_min_ver\@ + else ; Recursive invocation + if \1 != \4 || (\2 < \5 || (\2 == \5 && \3 < \6)) + fail "Version \1.\2.\3 of 'hardware.inc' is incompatible with requested version \4.\5.\6" + endc + endc +ENDM + + +;****************************************************************************** +; Memory-mapped registers ($FFxx range) +;****************************************************************************** + +; -- JOYP / P1 ($FF00) -------------------------------------------------------- +; Joypad face buttons +def rJOYP equ $FF00 + +def B_JOYP_GET_BUTTONS equ 5 ; 0 = reading buttons [r/w] +def B_JOYP_GET_CTRL_PAD equ 4 ; 0 = reading Control Pad [r/w] + def JOYP_GET equ %00_11_0000 ; select which inputs to read from the lower nybble + def JOYP_GET_BUTTONS equ %00_01_0000 ; reading A/B/Select/Start buttons + def JOYP_GET_CTRL_PAD equ %00_10_0000 ; reading Control Pad directions + def JOYP_GET_NONE equ %00_11_0000 ; reading nothing + +def B_JOYP_START equ 3 ; 0 = Start is pressed (if reading buttons) [ro] +def B_JOYP_SELECT equ 2 ; 0 = Select is pressed (if reading buttons) [ro] +def B_JOYP_B equ 1 ; 0 = B is pressed (if reading buttons) [ro] +def B_JOYP_A equ 0 ; 0 = A is pressed (if reading buttons) [ro] +def B_JOYP_DOWN equ 3 ; 0 = Down is pressed (if reading Control Pad) [ro] +def B_JOYP_UP equ 2 ; 0 = Up is pressed (if reading Control Pad) [ro] +def B_JOYP_LEFT equ 1 ; 0 = Left is pressed (if reading Control Pad) [ro] +def B_JOYP_RIGHT equ 0 ; 0 = Right is pressed (if reading Control Pad) [ro] + def JOYP_INPUTS equ %0000_1111 ; bits equal to 0 indicate pressed (when reading inputs) + def JOYP_START equ 1 << B_JOYP_START + def JOYP_SELECT equ 1 << B_JOYP_SELECT + def JOYP_B equ 1 << B_JOYP_B + def JOYP_A equ 1 << B_JOYP_A + def JOYP_DOWN equ 1 << B_JOYP_DOWN + def JOYP_UP equ 1 << B_JOYP_UP + def JOYP_LEFT equ 1 << B_JOYP_LEFT + def JOYP_RIGHT equ 1 << B_JOYP_RIGHT + +; SGB command packet transfer uses for JOYP bits +def B_JOYP_SGB_ONE equ 5 ; 0 = sending 1 bit +def B_JOYP_SGB_ZERO equ 4 ; 0 = sending 0 bit + def JOYP_SGB_START equ %00_00_0000 ; start SGB packet transfer + def JOYP_SGB_ONE equ %00_01_0000 ; send 1 bit + def JOYP_SGB_ZERO equ %00_10_0000 ; send 0 bit + def JOYP_SGB_FINISH equ %00_11_0000 ; finish SGB packet transfer + +; Combined input byte, with Control Pad in high nybble (conventional order) +def B_PAD_DOWN equ 7 +def B_PAD_UP equ 6 +def B_PAD_LEFT equ 5 +def B_PAD_RIGHT equ 4 +def B_PAD_START equ 3 +def B_PAD_SELECT equ 2 +def B_PAD_B equ 1 +def B_PAD_A equ 0 + def PAD_CTRL_PAD equ %1111_0000 + def PAD_BUTTONS equ %0000_1111 + def PAD_DOWN equ 1 << B_PAD_DOWN + def PAD_UP equ 1 << B_PAD_UP + def PAD_LEFT equ 1 << B_PAD_LEFT + def PAD_RIGHT equ 1 << B_PAD_RIGHT + def PAD_START equ 1 << B_PAD_START + def PAD_SELECT equ 1 << B_PAD_SELECT + def PAD_B equ 1 << B_PAD_B + def PAD_A equ 1 << B_PAD_A + +; Combined input byte, with Control Pad in low nybble (swapped order) +def B_PAD_SWAP_START equ 7 +def B_PAD_SWAP_SELECT equ 6 +def B_PAD_SWAP_B equ 5 +def B_PAD_SWAP_A equ 4 +def B_PAD_SWAP_DOWN equ 3 +def B_PAD_SWAP_UP equ 2 +def B_PAD_SWAP_LEFT equ 1 +def B_PAD_SWAP_RIGHT equ 0 + def PAD_SWAP_CTRL_PAD equ %0000_1111 + def PAD_SWAP_BUTTONS equ %1111_0000 + def PAD_SWAP_START equ 1 << B_PAD_SWAP_START + def PAD_SWAP_SELECT equ 1 << B_PAD_SWAP_SELECT + def PAD_SWAP_B equ 1 << B_PAD_SWAP_B + def PAD_SWAP_A equ 1 << B_PAD_SWAP_A + def PAD_SWAP_DOWN equ 1 << B_PAD_SWAP_DOWN + def PAD_SWAP_UP equ 1 << B_PAD_SWAP_UP + def PAD_SWAP_LEFT equ 1 << B_PAD_SWAP_LEFT + def PAD_SWAP_RIGHT equ 1 << B_PAD_SWAP_RIGHT + +; -- SB ($FF01) --------------------------------------------------------------- +; Serial transfer data [r/w] +def rSB equ $FF01 + +; -- SC ($FF02) --------------------------------------------------------------- +; Serial transfer control +def rSC equ $FF02 + +def B_SC_START equ 7 ; reading 1 = transfer in progress, writing 1 = start transfer [r/w] +def B_SC_SPEED equ 1 ; (CGB only) 1 = use faster internal clock [r/w] +def B_SC_SOURCE equ 0 ; 0 = use external clock ("slave"), 1 = use internal clock ("master") [r/w] + def SC_START equ 1 << B_SC_START + def SC_SPEED equ 1 << B_SC_SPEED + def SC_SLOW equ 0 << B_SC_SPEED + def SC_FAST equ 1 << B_SC_SPEED + def SC_SOURCE equ 1 << B_SC_SOURCE + def SC_EXTERNAL equ 0 << B_SC_SOURCE + def SC_INTERNAL equ 1 << B_SC_SOURCE + +; -- $FF03 is unused ---------------------------------------------------------- + +; -- DIV ($FF04) -------------------------------------------------------------- +; Divider register [r/w] +def rDIV equ $FF04 + +; -- TIMA ($FF05) ------------------------------------------------------------- +; Timer counter [r/w] +def rTIMA equ $FF05 + +; -- TMA ($FF06) -------------------------------------------------------------- +; Timer modulo [r/w] +def rTMA equ $FF06 + +; -- TAC ($FF07) -------------------------------------------------------------- +; Timer control +def rTAC equ $FF07 + +def B_TAC_START equ 2 ; enable incrementing TIMA [r/w] + def TAC_STOP equ 0 << B_TAC_START + def TAC_START equ 1 << B_TAC_START + +def TAC_CLOCK equ %000000_11 ; the frequency at which TIMA increments [r/w] + def TAC_4KHZ equ %000000_00 ; every 256 M-cycles = ~4 KHz on DMG + def TAC_262KHZ equ %000000_01 ; every 4 M-cycles = ~262 KHz on DMG + def TAC_65KHZ equ %000000_10 ; every 16 M-cycles = ~65 KHz on DMG + def TAC_16KHZ equ %000000_11 ; every 64 M-cycles = ~16 KHz on DMG + +; -- $FF08-$FF0E are unused --------------------------------------------------- + +; -- IF ($FF0F) --------------------------------------------------------------- +; Pending interrupts +def rIF equ $FF0F + +def B_IF_JOYPAD equ 4 ; 1 = joypad interrupt is pending [r/w] +def B_IF_SERIAL equ 3 ; 1 = serial interrupt is pending [r/w] +def B_IF_TIMER equ 2 ; 1 = timer interrupt is pending [r/w] +def B_IF_STAT equ 1 ; 1 = STAT interrupt is pending [r/w] +def B_IF_VBLANK equ 0 ; 1 = VBlank interrupt is pending [r/w] + def IF_JOYPAD equ 1 << B_IF_JOYPAD + def IF_SERIAL equ 1 << B_IF_SERIAL + def IF_TIMER equ 1 << B_IF_TIMER + def IF_STAT equ 1 << B_IF_STAT + def IF_VBLANK equ 1 << B_IF_VBLANK + +; -- AUD1SWEEP / NR10 ($FF10) ------------------------------------------------- +; Audio channel 1 sweep +def rAUD1SWEEP equ $FF10 + +def AUD1SWEEP_TIME equ %0_111_0000 ; how long between sweep iterations + ; (in 128 Hz ticks, ~7.8 ms apart) [r/w] + +def B_AUD1SWEEP_DIR equ 3 ; sweep direction [r/w] + def AUD1SWEEP_DIR equ 1 << B_AUD1SWEEP_DIR + def AUD1SWEEP_UP equ 0 << B_AUD1SWEEP_DIR + def AUD1SWEEP_DOWN equ 1 << B_AUD1SWEEP_DIR + +def AUD1SWEEP_SHIFT equ %00000_111 ; how much the period increases/decreases per iteration [r/w] + +; -- AUD1LEN / NR11 ($FF11) --------------------------------------------------- +; Audio channel 1 length timer and duty cycle +def rAUD1LEN equ $FF11 + +def AUD1LEN_DUTY equ %11_000000 ; ratio of time spent high vs. time spent low [r/w] + def AUD1LEN_DUTY_12_5 equ %00_000000 ; 12.5% + def AUD1LEN_DUTY_25 equ %01_000000 ; 25% + def AUD1LEN_DUTY_50 equ %10_000000 ; 50% + def AUD1LEN_DUTY_75 equ %11_000000 ; 75% + +def AUD1LEN_TIMER equ %00_111111 ; initial length timer (0-63) [wo] + +; -- AUD1ENV / NR12 ($FF12) --------------------------------------------------- +; Audio channel 1 volume and envelope +def rAUD1ENV equ $FF12 + +def AUD1ENV_INIT_VOLUME equ %1111_0000 ; initial volume [r/w] + +def B_AUD1ENV_DIR equ 3 ; direction of volume envelope [r/w] + def AUD1ENV_DIR equ 1 << B_AUD1ENV_DIR + def AUD1ENV_DOWN equ 0 << B_AUD1ENV_DIR + def AUD1ENV_UP equ 1 << B_AUD1ENV_DIR + +def AUD1ENV_PACE equ %00000_111 ; how long between envelope iterations + ; (in 64 Hz ticks, ~15.6 ms apart) [r/w] + +; -- AUD1LOW / NR13 ($FF13) --------------------------------------------------- +; Audio channel 1 period (low 8 bits) [wo] +def rAUD1LOW equ $FF13 + +; -- AUD1HIGH / NR14 ($FF14) -------------------------------------------------- +; Audio channel 1 period (high 3 bits) and control +def rAUD1HIGH equ $FF14 + +def B_AUD1HIGH_RESTART equ 7 ; 1 = restart the channel [wo] +def B_AUD1HIGH_LEN_ENABLE equ 6 ; 1 = reset the channel after the length timer expires [r/w] + def AUD1HIGH_RESTART equ 1 << B_AUD1HIGH_RESTART + def AUD1HIGH_LENGTH_OFF equ 0 << B_AUD1HIGH_LEN_ENABLE + def AUD1HIGH_LENGTH_ON equ 1 << B_AUD1HIGH_LEN_ENABLE + +def AUD1HIGH_PERIOD_HIGH equ %00000_111 ; upper 3 bits of the channel's period [r/w] + +; -- $FF15 is unused ---------------------------------------------------------- + +; -- AUD2LEN / NR21 ($FF16) --------------------------------------------------- +; Audio channel 2 length timer and duty cycle +def rAUD2LEN equ $FF16 + +def AUD2LEN_DUTY equ %11_000000 ; ratio of time spent high vs. time spent low [r/w] + def AUD2LEN_DUTY_12_5 equ %00_000000 ; 12.5% + def AUD2LEN_DUTY_25 equ %01_000000 ; 25% + def AUD2LEN_DUTY_50 equ %10_000000 ; 50% + def AUD2LEN_DUTY_75 equ %11_000000 ; 75% + +def AUD2LEN_TIMER equ %00_111111 ; initial length timer (0-63) [wo] + +; -- AUD2ENV / NR22 ($FF17) --------------------------------------------------- +; Audio channel 2 volume and envelope +def rAUD2ENV equ $FF17 + +def AUD2ENV_INIT_VOLUME equ %1111_0000 ; initial volume [r/w] + +def B_AUD2ENV_DIR equ 3 ; direction of volume envelope [r/w] + def AUD2ENV_DIR equ 1 << B_AUD2ENV_DIR + def AUD2ENV_DOWN equ 0 << B_AUD2ENV_DIR + def AUD2ENV_UP equ 1 << B_AUD2ENV_DIR + +def AUD2ENV_PACE equ %00000_111 ; how long between envelope iterations + ; (in 64 Hz ticks, ~15.6 ms apart) [r/w] + +; -- AUD2LOW / NR23 ($FF18) --------------------------------------------------- +; Audio channel 2 period (low 8 bits) [wo] +def rAUD2LOW equ $FF18 + +; -- AUD2HIGH / NR24 ($FF19) -------------------------------------------------- +; Audio channel 2 period (high 3 bits) and control +def rAUD2HIGH equ $FF19 + +def B_AUD2HIGH_RESTART equ 7 ; 1 = restart the channel [wo] +def B_AUD2HIGH_LEN_ENABLE equ 6 ; 1 = reset the channel after the length timer expires [r/w] + def AUD2HIGH_RESTART equ 1 << B_AUD2HIGH_RESTART + def AUD2HIGH_LENGTH_OFF equ 0 << B_AUD2HIGH_LEN_ENABLE + def AUD2HIGH_LENGTH_ON equ 1 << B_AUD2HIGH_LEN_ENABLE + +def AUD2HIGH_PERIOD_HIGH equ %00000_111 ; upper 3 bits of the channel's period [r/w] + +; -- AUD3ENA / NR30 ($FF1A) --------------------------------------------------- +; Audio channel 3 enable +def rAUD3ENA equ $FF1A + +def B_AUD3ENA_ENABLE equ 7 ; 1 = channel is active [r/w] + def AUD3ENA_OFF equ 0 << B_AUD3ENA_ENABLE + def AUD3ENA_ON equ 1 << B_AUD3ENA_ENABLE + +; -- AUD3LEN / NR31 ($FF1B) --------------------------------------------------- +; Audio channel 3 length timer [wo] +def rAUD3LEN equ $FF1B + +; -- AUD3LEVEL / NR32 ($FF1C) ------------------------------------------------- +; Audio channel 3 volume +def rAUD3LEVEL equ $FF1C + +def AUD3LEVEL_VOLUME equ %0_11_00000 ; volume level [r/w] + def AUD3LEVEL_MUTE equ %0_00_00000 ; 0% (muted) + def AUD3LEVEL_100 equ %0_01_00000 ; 100% + def AUD3LEVEL_50 equ %0_10_00000 ; 50% + def AUD3LEVEL_25 equ %0_11_00000 ; 25% + +; -- AUD3LOW / NR33 ($FF1D) --------------------------------------------------- +; Audio channel 3 period (low 8 bits) [wo] +def rAUD3LOW equ $FF1D + +; -- AUD3HIGH / NR34 ($FF1E) -------------------------------------------------- +; Audio channel 3 period (high 3 bits) and control +def rAUD3HIGH equ $FF1E + +def B_AUD3HIGH_RESTART equ 7 ; 1 = restart the channel [wo] +def B_AUD3HIGH_LEN_ENABLE equ 6 ; 1 = reset the channel after the length timer expires [r/w] + def AUD3HIGH_RESTART equ 1 << B_AUD3HIGH_RESTART + def AUD3HIGH_LENGTH_OFF equ 0 << B_AUD3HIGH_LEN_ENABLE + def AUD3HIGH_LENGTH_ON equ 1 << B_AUD3HIGH_LEN_ENABLE + +def AUD3HIGH_PERIOD_HIGH equ %00000_111 ; upper 3 bits of the channel's period [r/w] + +; -- $FF1F is unused ---------------------------------------------------------- + +; -- AUD4LEN / NR41 ($FF20) --------------------------------------------------- +; Audio channel 4 length timer +def rAUD4LEN equ $FF20 + +def AUD4LEN_TIMER equ %00_111111 ; initial length timer (0-63) [wo] + +; -- AUD4ENV / NR42 ($FF21) --------------------------------------------------- +; Audio channel 4 volume and envelope +def rAUD4ENV equ $FF21 + +def AUD4ENV_INIT_VOLUME equ %1111_0000 ; initial volume [r/w] + +def B_AUD4ENV_DIR equ 3 ; direction of volume envelope [r/w] + def AUD4ENV_DIR equ 1 << B_AUD4ENV_DIR + def AUD4ENV_DOWN equ 0 << B_AUD4ENV_DIR + def AUD4ENV_UP equ 1 << B_AUD4ENV_DIR + +def AUD4ENV_PACE equ %00000_111 ; how long between envelope iterations + ; (in 64 Hz ticks, ~15.6 ms apart) [r/w] + +; -- AUD4POLY / NR43 ($FF22) -------------------------------------------------- +; Audio channel 4 period and randomness +def rAUD4POLY equ $FF22 + +def AUD4POLY_SHIFT equ %1111_0000 ; coarse control of the channel's period [r/w] + +def B_AUD4POLY_WIDTH equ 3 ; controls the noise generator (LFSR)'s step width [r/w] + def AUD4POLY_15STEP equ 0 << B_AUD4POLY_WIDTH + def AUD4POLY_7STEP equ 1 << B_AUD4POLY_WIDTH + +def AUD4POLY_DIV equ %00000_111 ; fine control of the channel's period [r/w] + +; -- AUD4GO / NR44 ($FF23) ---------------------------------------------------- +; Audio channel 4 control +def rAUD4GO equ $FF23 + +def B_AUD4GO_RESTART equ 7 ; 1 = restart the channel [wo] +def B_AUD4GO_LEN_ENABLE equ 6 ; 1 = reset the channel after the length timer expires [r/w] + def AUD4GO_RESTART equ 1 << B_AUD4GO_RESTART + def AUD4GO_LENGTH_OFF equ 0 << B_AUD4GO_LEN_ENABLE + def AUD4GO_LENGTH_ON equ 1 << B_AUD4GO_LEN_ENABLE + +; -- AUDVOL / NR50 ($FF24) ---------------------------------------------------- +; Audio master volume and VIN mixer +def rAUDVOL equ $FF24 + +def B_AUDVOL_VIN_LEFT equ 7 ; 1 = output VIN to left ear (SO2, speaker 2) [r/w] + def AUDVOL_VIN_LEFT equ 1 << B_AUDVOL_VIN_LEFT + +def AUDVOL_LEFT equ %0_111_0000 ; 0 = barely audible, 7 = full volume [r/w] + +def B_AUDVOL_VIN_RIGHT equ 3 ; 1 = output VIN to right ear (SO1, speaker 1) [r/w] + def AUDVOL_VIN_RIGHT equ 1 << B_AUDVOL_VIN_RIGHT + +def AUDVOL_RIGHT equ %00000_111 ; 0 = barely audible, 7 = full volume [r/w] + +; -- AUDTERM / NR51 ($FF25) --------------------------------------------------- +; Audio channel mixer +def rAUDTERM equ $FF25 + +def B_AUDTERM_4_LEFT equ 7 ; 1 = output channel 4 to left ear [r/w] +def B_AUDTERM_3_LEFT equ 6 ; 1 = output channel 3 to left ear [r/w] +def B_AUDTERM_2_LEFT equ 5 ; 1 = output channel 2 to left ear [r/w] +def B_AUDTERM_1_LEFT equ 4 ; 1 = output channel 1 to left ear [r/w] +def B_AUDTERM_4_RIGHT equ 3 ; 1 = output channel 4 to right ear [r/w] +def B_AUDTERM_3_RIGHT equ 2 ; 1 = output channel 3 to right ear [r/w] +def B_AUDTERM_2_RIGHT equ 1 ; 1 = output channel 2 to right ear [r/w] +def B_AUDTERM_1_RIGHT equ 0 ; 1 = output channel 1 to right ear [r/w] + def AUDTERM_4_LEFT equ 1 << B_AUDTERM_4_LEFT + def AUDTERM_3_LEFT equ 1 << B_AUDTERM_3_LEFT + def AUDTERM_2_LEFT equ 1 << B_AUDTERM_2_LEFT + def AUDTERM_1_LEFT equ 1 << B_AUDTERM_1_LEFT + def AUDTERM_4_RIGHT equ 1 << B_AUDTERM_4_RIGHT + def AUDTERM_3_RIGHT equ 1 << B_AUDTERM_3_RIGHT + def AUDTERM_2_RIGHT equ 1 << B_AUDTERM_2_RIGHT + def AUDTERM_1_RIGHT equ 1 << B_AUDTERM_1_RIGHT + +; -- AUDENA / NR52 ($FF26) ---------------------------------------------------- +; Audio master enable +def rAUDENA equ $FF26 + +def B_AUDENA_ENABLE equ 7 ; 0 = disable the APU (resets all audio registers to 0!) [r/w] +def B_AUDENA_ENABLE_CH4 equ 3 ; 1 = channel 4 is running [ro] +def B_AUDENA_ENABLE_CH3 equ 2 ; 1 = channel 3 is running [ro] +def B_AUDENA_ENABLE_CH2 equ 1 ; 1 = channel 2 is running [ro] +def B_AUDENA_ENABLE_CH1 equ 0 ; 1 = channel 1 is running [ro] + def AUDENA_OFF equ 0 << B_AUDENA_ENABLE + def AUDENA_ON equ 1 << B_AUDENA_ENABLE + def AUDENA_CH4_OFF equ 0 << B_AUDENA_ENABLE_CH4 + def AUDENA_CH4_ON equ 1 << B_AUDENA_ENABLE_CH4 + def AUDENA_CH3_OFF equ 0 << B_AUDENA_ENABLE_CH3 + def AUDENA_CH3_ON equ 1 << B_AUDENA_ENABLE_CH3 + def AUDENA_CH2_OFF equ 0 << B_AUDENA_ENABLE_CH2 + def AUDENA_CH2_ON equ 1 << B_AUDENA_ENABLE_CH2 + def AUDENA_CH1_OFF equ 0 << B_AUDENA_ENABLE_CH1 + def AUDENA_CH1_ON equ 1 << B_AUDENA_ENABLE_CH1 + +; -- $FF27-$FF2F are unused --------------------------------------------------- + +; -- AUD3WAVE ($FF30-$FF3F) --------------------------------------------------- +; Audio channel 3 wave pattern RAM [r/w] +def rAUD3WAVE_0 equ $FF30 +def rAUD3WAVE_1 equ $FF31 +def rAUD3WAVE_2 equ $FF32 +def rAUD3WAVE_3 equ $FF33 +def rAUD3WAVE_4 equ $FF34 +def rAUD3WAVE_5 equ $FF35 +def rAUD3WAVE_6 equ $FF36 +def rAUD3WAVE_7 equ $FF37 +def rAUD3WAVE_8 equ $FF38 +def rAUD3WAVE_9 equ $FF39 +def rAUD3WAVE_A equ $FF3A +def rAUD3WAVE_B equ $FF3B +def rAUD3WAVE_C equ $FF3C +def rAUD3WAVE_D equ $FF3D +def rAUD3WAVE_E equ $FF3E +def rAUD3WAVE_F equ $FF3F + +; -- LCDC ($FF40) ------------------------------------------------------------- +; PPU graphics control +def rLCDC equ $FF40 + +def B_LCDC_ENABLE equ 7 ; whether the PPU (and LCD) are turned on [r/w] +def B_LCDC_WIN_MAP equ 6 ; which tilemap the Window reads from [r/w] +def B_LCDC_WINDOW equ 5 ; whether the Window is enabled [r/w] +def B_LCDC_BLOCKS equ 4 ; which "tile blocks" the BG and Window use [r/w] +def B_LCDC_BG_MAP equ 3 ; which tilemap the BG reads from [r/w] +def B_LCDC_OBJ_SIZE equ 2 ; how many pixels tall each OBJ is [r/w] +def B_LCDC_OBJS equ 1 ; whether OBJs are enabled [r/w] +def B_LCDC_BG equ 0 ; (DMG only) whether the BG is enabled [r/w] +def B_LCDC_PRIO equ 0 ; (CGB only) whether OBJ priority bits are enabled [r/w] + def LCDC_ENABLE equ 1 << B_LCDC_ENABLE + def LCDC_OFF equ 0 << B_LCDC_ENABLE + def LCDC_ON equ 1 << B_LCDC_ENABLE + def LCDC_WIN_MAP equ 1 << B_LCDC_WIN_MAP + def LCDC_WIN_9800 equ 0 << B_LCDC_WIN_MAP + def LCDC_WIN_9C00 equ 1 << B_LCDC_WIN_MAP + def LCDC_WINDOW equ 1 << B_LCDC_WINDOW + def LCDC_WIN_OFF equ 0 << B_LCDC_WINDOW + def LCDC_WIN_ON equ 1 << B_LCDC_WINDOW + def LCDC_BLOCKS equ 1 << B_LCDC_BLOCKS + def LCDC_BLOCK21 equ 0 << B_LCDC_BLOCKS + def LCDC_BLOCK01 equ 1 << B_LCDC_BLOCKS + def LCDC_BG_MAP equ 1 << B_LCDC_BG_MAP + def LCDC_BG_9800 equ 0 << B_LCDC_BG_MAP + def LCDC_BG_9C00 equ 1 << B_LCDC_BG_MAP + def LCDC_OBJ_SIZE equ 1 << B_LCDC_OBJ_SIZE + def LCDC_OBJ_8 equ 0 << B_LCDC_OBJ_SIZE + def LCDC_OBJ_16 equ 1 << B_LCDC_OBJ_SIZE + def LCDC_OBJS equ 1 << B_LCDC_OBJS + def LCDC_OBJ_OFF equ 0 << B_LCDC_OBJS + def LCDC_OBJ_ON equ 1 << B_LCDC_OBJS + def LCDC_BG equ 1 << B_LCDC_BG + def LCDC_BG_OFF equ 0 << B_LCDC_BG + def LCDC_BG_ON equ 1 << B_LCDC_BG + def LCDC_PRIO equ 1 << B_LCDC_PRIO + def LCDC_PRIO_OFF equ 0 << B_LCDC_PRIO + def LCDC_PRIO_ON equ 1 << B_LCDC_PRIO + +; -- STAT ($FF41) ------------------------------------------------------------- +; Graphics status and interrupt control +def rSTAT equ $FF41 + +def B_STAT_LYC equ 6 ; 1 = LY match triggers the STAT interrupt [r/w] +def B_STAT_MODE_2 equ 5 ; 1 = OAM Scan triggers the PPU interrupt [r/w] +def B_STAT_MODE_1 equ 4 ; 1 = VBlank triggers the PPU interrupt [r/w] +def B_STAT_MODE_0 equ 3 ; 1 = HBlank triggers the PPU interrupt [r/w] +def B_STAT_LYCF equ 2 ; 1 = LY is currently equal to LYC [ro] +def B_STAT_BUSY equ 1 ; 1 = the PPU is currently accessing VRAM [ro] + def STAT_LYC equ 1 << B_STAT_LYC + def STAT_MODE_2 equ 1 << B_STAT_MODE_2 + def STAT_MODE_1 equ 1 << B_STAT_MODE_1 + def STAT_MODE_0 equ 1 << B_STAT_MODE_0 + def STAT_LYCF equ 1 << B_STAT_LYCF + def STAT_BUSY equ 1 << B_STAT_BUSY + +def STAT_MODE equ %000000_11 ; PPU's current status [ro] + def STAT_HBLANK equ %000000_00 ; waiting after a line's rendering (HBlank) + def STAT_VBLANK equ %000000_01 ; waiting between frames (VBlank) + def STAT_OAM equ %000000_10 ; checking which OBJs will be rendered on this line (OAM scan) + def STAT_LCD equ %000000_11 ; pushing pixels to the LCD + +; -- SCY ($FF42) -------------------------------------------------------------- +; Background Y scroll offset (in pixels) [r/w] +def rSCY equ $FF42 + +; -- SCX ($FF43) -------------------------------------------------------------- +; Background X scroll offset (in pixels) [r/w] +def rSCX equ $FF43 + +; -- LY ($FF44) --------------------------------------------------------------- +; Y coordinate of the line currently processed by the PPU (0-153) [ro] +def rLY equ $FF44 + +def LY_VBLANK equ 144 ; 144-153 is the VBlank period + +; -- LYC ($FF45) -------------------------------------------------------------- +; Value that LY is constantly compared to [r/w] +def rLYC equ $FF45 + +; -- DMA ($FF46) -------------------------------------------------------------- +; OAM DMA start address (high 8 bits) and start [wo] +def rDMA equ $FF46 + +; -- BGP ($FF47) -------------------------------------------------------------- +; (DMG only) Background color mapping [r/w] +def rBGP equ $FF47 + +def BGP_SGB_TRANSFER equ %11_10_01_00 ; set BGP to this value before SGB VRAM transfer + +; -- OBP0 ($FF48) ------------------------------------------------------------- +; (DMG only) OBJ color mapping #0 [r/w] +def rOBP0 equ $FF48 + +; -- OBP1 ($FF49) ------------------------------------------------------------- +; (DMG only) OBJ color mapping #1 [r/w] +def rOBP1 equ $FF49 + +; -- WY ($FF4A) --------------------------------------------------------------- +; Y coordinate of the Window's top-left pixel (0-143) [r/w] +def rWY equ $FF4A + +; -- WX ($FF4B) --------------------------------------------------------------- +; X coordinate of the Window's top-left pixel, plus 7 (7-166) [r/w] +def rWX equ $FF4B + +def WX_OFS equ 7 ; subtract this to get the actual Window X coordinate + +; -- SYS / KEY0 ($FF4C) ------------------------------------------------------- +; (CGB boot ROM only) CPU mode select +def rSYS equ $FF4C + +; This is known as the "CPU mode register" in Fig. 11 of this patent: +; https://patents.google.com/patent/US6322447B1/en?oq=US6322447bi +; "OBJ priority mode designating register" in the same patent +; Credit to @mattcurrie for this finding! + +def SYS_MODE equ %0000_11_00 ; current system mode [r/w] + def SYS_CGB equ %0000_00_00 ; CGB mode + def SYS_DMG equ %0000_01_00 ; DMG compatibility mode + def SYS_PGB1 equ %0000_10_00 ; LCD is driven externally, CPU is stopped + def SYS_PGB2 equ %0000_11_00 ; LCD is driven externally, CPU is running + +; -- SPD / KEY1 ($FF4D) ------------------------------------------------------- +; (CGB only) Double-speed mode control +def rSPD equ $FF4D + +def B_SPD_DOUBLE equ 7 ; current clock speed [ro] +def B_SPD_PREPARE equ 0 ; 1 = next `stop` instruction will switch clock speeds [r/w] + def SPD_SINGLE equ 0 << B_SPD_DOUBLE + def SPD_DOUBLE equ 1 << B_SPD_DOUBLE + def SPD_PREPARE equ 1 << B_SPD_PREPARE + +; -- $FF4E is unused ---------------------------------------------------------- + +; -- VBK ($FF4F) -------------------------------------------------------------- +; (CGB only) VRAM bank number (0 or 1) +def rVBK equ $FF4F + +def VBK_BANK equ %0000000_1 ; mapped VRAM bank [r/w] + +; -- BANK ($FF50) ------------------------------------------------------------- +; (boot ROM only) Boot ROM mapping control +def rBANK equ $FF50 + +def B_BANK_ON equ 0 ; whether the boot ROM is mapped [wo] + def BANK_ON equ 0 << B_BANK_ON + def BANK_OFF equ 1 << B_BANK_ON + +; -- VDMA_SRC_HIGH / HDMA1 ($FF51) -------------------------------------------- +; (CGB only) VRAM DMA source address (high 8 bits) [wo] +def rVDMA_SRC_HIGH equ $FF51 + +; -- VDMA_SRC_LOW / HDMA2 ($FF52) --------------------------------------------- +; (CGB only) VRAM DMA source address (low 8 bits) [wo] +def rVDMA_SRC_LOW equ $FF52 + +; -- VDMA_DEST_HIGH / HDMA3 ($FF53) ------------------------------------------- +; (CGB only) VRAM DMA destination address (high 8 bits) [wo] +def rVDMA_DEST_HIGH equ $FF53 + +; -- VDMA_DEST_LOW / HDMA4 ($FF54) -------------------------------------------- +; (CGB only) VRAM DMA destination address (low 8 bits) [wo] +def rVDMA_DEST_LOW equ $FF54 + +; -- VDMA_LEN / HDMA5 ($FF55) ------------------------------------------------- +; (CGB only) VRAM DMA length, mode, and start +def rVDMA_LEN equ $FF55 + +def B_VDMA_LEN_MODE equ 7 ; on write: VRAM DMA mode [wo] + def VDMA_LEN_MODE equ 1 << B_VDMA_LEN_MODE + def VDMA_LEN_MODE_GENERAL equ 0 << B_VDMA_LEN_MODE ; GDMA (general-purpose) + def VDMA_LEN_MODE_HBLANK equ 1 << B_VDMA_LEN_MODE ; HDMA (HBlank) + +def B_VDMA_LEN_BUSY equ 7 ; on read: is a VRAM DMA active? + def VDMA_LEN_BUSY equ 1 << B_VDMA_LEN_BUSY + def VDMA_LEN_NO equ 0 << B_VDMA_LEN_BUSY + def VDMA_LEN_YES equ 1 << B_VDMA_LEN_BUSY + +def VDMA_LEN_SIZE equ %0_1111111 ; how many 16-byte blocks (minus 1) to transfer [r/w] + +; -- RP ($FF56) --------------------------------------------------------------- +; (CGB only) Infrared communications port +def rRP equ $FF56 + +def RP_READ equ %11_000000 ; whether the IR read is enabled [r/w] + def RP_DISABLE equ %00_000000 + def RP_ENABLE equ %11_000000 + +def B_RP_DATA_IN equ 1 ; 0 = IR light is being received [ro] +def B_RP_LED_ON equ 0 ; 1 = IR light is being sent [r/w] + def RP_DATA_IN equ 1 << B_RP_DATA_IN + def RP_LED_ON equ 1 << B_RP_LED_ON + def RP_WRITE_LOW equ 0 << B_RP_LED_ON + def RP_WRITE_HIGH equ 1 << B_RP_LED_ON + +; -- $FF57-$FF67 are unused --------------------------------------------------- + +; -- BGPI / BCPS ($FF68) ------------------------------------------------------ +; (CGB only) Background palette I/O index +def rBGPI equ $FF68 + +def B_BGPI_AUTOINC equ 7 ; whether the index field is incremented after each write to BCPD [r/w] + def BGPI_AUTOINC equ 1 << B_BGPI_AUTOINC + +def BGPI_INDEX equ %00_111111 ; the index within Palette RAM accessed via BCPD [r/w] + +; -- BGPD / BCPD ($FF69) ------------------------------------------------------ +; (CGB only) Background palette I/O access [r/w] +def rBGPD equ $FF69 + +; -- OBPI / OCPS ($FF6A) ------------------------------------------------------ +; (CGB only) OBJ palette I/O index +def rOBPI equ $FF6A + +def B_OBPI_AUTOINC equ 7 ; whether the index field is incremented after each write to OBPD [r/w] + def OBPI_AUTOINC equ 1 << B_OBPI_AUTOINC + +def OBPI_INDEX equ %00_111111 ; the index within Palette RAM accessed via OBPD [r/w] + +; -- OBPD / OCPD ($FF6B) ------------------------------------------------------ +; (CGB only) OBJ palette I/O access [r/w] +def rOBPD equ $FF6B + +; -- OPRI ($FF6C) ------------------------------------------------------------- +; (CGB boot ROM only) OBJ draw priority mode +def rOPRI equ $FF6C + +def B_OPRI_PRIORITY equ 0 ; which drawing priority is used for OBJs [r/w] + def OPRI_PRIORITY equ 1 << B_OPRI_PRIORITY + def OPRI_OAM equ 0 << B_OPRI_PRIORITY ; CGB mode default: earliest OBJ in OAM wins + def OPRI_COORD equ 1 << B_OPRI_PRIORITY ; DMG mode default: leftmost OBJ wins + +; -- $FF6D-$FF6F are unused --------------------------------------------------- + +; -- WBK / SVBK ($FF70) ------------------------------------------------------- +; (CGB only) WRAM bank number +def rWBK equ $FF70 + +def WBK_BANK equ %00000_111 ; mapped WRAM bank (0-7) [r/w] + +; -- $FF71-$FF75 are unused --------------------------------------------------- + +; -- PCM12 ($FF76) ------------------------------------------------------------ +; Audio channels 1 and 2 output +def rPCM12 equ $FF76 + +def PCM12_CH2 equ %1111_0000 ; audio channel 2 output [ro] +def PCM12_CH1 equ %0000_1111 ; audio channel 1 output [ro] + +; -- PCM34 ($FF77) ------------------------------------------------------------ +; Audio channels 3 and 4 output +def rPCM34 equ $FF77 + +def PCM34_CH4 equ %1111_0000 ; audio channel 4 output [ro] +def PCM34_CH3 equ %0000_1111 ; audio channel 3 output [ro] + +; -- $FF78-$FF7F are unused --------------------------------------------------- + +; -- IE ($FFFF) --------------------------------------------------------------- +; Interrupt enable +def rIE equ $FFFF + +def B_IE_JOYPAD equ 4 ; 1 = joypad interrupt is enabled [r/w] +def B_IE_SERIAL equ 3 ; 1 = serial interrupt is enabled [r/w] +def B_IE_TIMER equ 2 ; 1 = timer interrupt is enabled [r/w] +def B_IE_STAT equ 1 ; 1 = STAT interrupt is enabled [r/w] +def B_IE_VBLANK equ 0 ; 1 = VBlank interrupt is enabled [r/w] + def IE_JOYPAD equ 1 << B_IE_JOYPAD + def IE_SERIAL equ 1 << B_IE_SERIAL + def IE_TIMER equ 1 << B_IE_TIMER + def IE_STAT equ 1 << B_IE_STAT + def IE_VBLANK equ 1 << B_IE_VBLANK + + +;****************************************************************************** +; Cartridge registers (MBC) +;****************************************************************************** + +; Note that these "registers" are each actually accessible at an entire address range; +; however, one address for each of these ranges is considered the "canonical" one, and +; these addresses are what's provided here. + + +; ** Common to most MBCs ****************************************************** + +; -- RAMG ($0000-$1FFF) ------------------------------------------------------- +; Whether SRAM can be accessed [wo] +def rRAMG equ $0000 + +; Common values (not for HuC1 or HuC-3) +def RAMG_SRAM_DISABLE equ $00 +def RAMG_SRAM_ENABLE equ $0A ; some MBCs accept any value whose low nybble is $A + +; (HuC-3 only) switch SRAM to map cartridge RAM, RTC, or IR +def RAMG_CART_RAM_RO equ $00 ; select cartridge RAM [ro] +def RAMG_CART_RAM equ $0A ; select cartridge RAM [r/w] +def RAMG_RTC_IN equ $0B ; select RTC command/argument [wo] + def RAMG_RTC_IN_CMD equ %0_111_0000 ; command + def RAMG_RTC_IN_ARG equ %0_000_1111 ; argument +def RAMG_RTC_OUT equ $0C ; select RTC command/response [ro] + def RAMG_RTC_OUT_CMD equ %0_111_0000 ; command + def RAMG_RTC_OUT_RESULT equ %0_000_1111 ; result +def RAMG_RTC_SEMAPHORE equ $0D ; select RTC semaphore [r/w] +def RAMG_IR equ $0E ; (HuC1 and HuC-3 only) select IR [r/w] + +; -- ROMB ($2000-$3FFF) ------------------------------------------------------- +; ROM bank number (not for MBC5 or MBC6) [wo] +def rROMB equ $2000 + +; -- RAMB ($4000-$5FFF) ------------------------------------------------------- +; SRAM bank number (not for MBC2, MBC6, or MBC7) [wo] +def rRAMB equ $4000 + +; (MBC3 only) Special RAM bank numbers that actually map values into RTCREG +def RAMB_RTC_S equ $08 ; seconds counter (0-59) +def RAMB_RTC_M equ $09 ; minutes counter (0-59) +def RAMB_RTC_H equ $0A ; hours counter (0-23) +def RAMB_RTC_DL equ $0B ; days counter, low byte (0-255) +def RAMB_RTC_DH equ $0C ; days counter, high bit and other flags + def B_RAMB_RTC_DH_CARRY equ 7 ; 1 = days counter overflowed [wo] + def B_RAMB_RTC_DH_HALT equ 6 ; 0 = run timer, 1 = stop timer [wo] + def B_RAMB_RTC_DH_HIGH equ 0 ; days counter, high bit (bit 8) [wo] + def RAMB_RTC_DH_CARRY equ 1 << B_RAMB_RTC_DH_CARRY + def RAMB_RTC_DH_HALT equ 1 << B_RAMB_RTC_DH_HALT + def RAMB_RTC_DH_HIGH equ 1 << B_RAMB_RTC_DH_HIGH + +def B_RAMB_RUMBLE equ 3 ; (MBC5 and MBC7 only) enable the rumble motor (if any) + def RAMB_RUMBLE equ 1 << B_RAMB_RUMBLE + def RAMB_RUMBLE_OFF equ 0 << B_RAMB_RUMBLE + def RAMB_RUMBLE_ON equ 1 << B_RAMB_RUMBLE + + +; ** MBC1 and MMM01 only ****************************************************** + +; -- BMODE ($6000-$7FFF) ------------------------------------------------------ +; Banking mode select [wo] +def rBMODE equ $6000 + +def BMODE_SIMPLE equ $00 ; locks ROMB and RAMB to bank 0 +def BMODE_ADVANCED equ $01 ; allows bank-switching with RAMB + + +; ** MBC2 only **************************************************************** + +; -- ROM2B ($0000-$3FFF with bit 8 set) --------------------------------------- +; ROM bank number [wo] +def rROM2B equ $2100 + + +; ** MBC3 only **************************************************************** + +; -- RTCLATCH ($6000-$7FFF) --------------------------------------------------- +; RTC latch clock data [wo] +def rRTCLATCH equ $6000 + +; Write $00 then $01 to latch the current time into RTCREG +def RTCLATCH_START equ $00 +def RTCLATCH_FINISH equ $01 + +; -- RTCREG ($A000-$BFFF) ----------------------------------------------------- +; RTC register [r/w] +def rRTCREG equ $A000 + + +; ** MBC5 only **************************************************************** + +; -- ROMB0 ($2000-$2FFF) ------------------------------------------------------ +; ROM bank number low byte (bits 0-7) [wo] +def rROMB0 equ $2000 + +; -- ROMB1 ($3000-$3FFF) ------------------------------------------------------ +; ROM bank number high bit (bit 8) [wo] +def rROMB1 equ $3000 + + +; ** MBC6 only **************************************************************** + +; -- RAMBA ($0400-$07FF) ------------------------------------------------------ +; RAM bank A number [wo] +def rRAMBA equ $0400 + +; -- RAMBB ($0800-$0BFF) ------------------------------------------------------ +; RAM bank B number [wo] +def rRAMBB equ $0800 + +; -- FLASH ($0C00-$0FFF) ------------------------------------------------------ +; Whether the flash chip can be accessed [wo] +def rFLASH equ $0C00 + +; -- FMODE ($1000) ------------------------------------------------------------ +; Write mode select for the flash chip +def rFMODE equ $1000 + +; -- ROMBA ($2000-$27FF) ------------------------------------------------------ +; ROM/Flash bank A number [wo] +def rROMBA equ $2000 + +; -- FLASHA ($2800-$2FFF) ----------------------------------------------------- +; ROM/Flash bank A select [wo] +def rFLASHA equ $2800 + +; -- ROMBB ($3000-$37FF) ------------------------------------------------------ +; ROM/Flash bank B number [wo] +def rROMBB equ $3000 + +; -- FLASHB ($3800-$3FFF) ----------------------------------------------------- +; ROM/Flash bank B select [wo] +def rFLASHB equ $3800 + + +; ** MBC7 only **************************************************************** + +; -- RAMREG ($4000-$5FFF) ----------------------------------------------------- +; Enable RAM register access [wo] +def rRAMREG equ $4000 + +def RAMREG_ENABLE equ $40 + +; -- ACCLATCH0 ($Ax0x) -------------------------------------------------------- +; Latch accelerometer start [wo] +def rACCLATCH0 equ $A000 + +; Write $55 to ACCLATCH0 to erase the latched data +def ACCLATCH0_START equ $55 + +; -- ACCLATCH1 ($Ax1x) -------------------------------------------------------- +; Latch accelerometer finish [wo] +def rACCLATCH1 equ $A010 + +; Write $AA to ACCLATCH1 to latch the accelerometer and update ACCEL* +def ACCLATCH1_FINISH equ $AA + +; -- ACCELX0 ($Ax2x) ---------------------------------------------------------- +; Accelerometer X value low byte [ro] +def rACCELX0 equ $A020 + +; -- ACCELX1 ($Ax3x) ---------------------------------------------------------- +; Accelerometer X value high byte [ro] +def rACCELX1 equ $A030 + +; -- ACCELY0 ($Ax4x) ---------------------------------------------------------- +; Accelerometer Y value low byte [ro] +def rACCELY0 equ $A040 + +; -- ACCELY1 ($Ax5x) ---------------------------------------------------------- +; Accelerometer Y value high byte [ro] +def rACCELY1 equ $A050 + +; -- EEPROM ($Ax8x) ----------------------------------------------------------- +; EEPROM access [r/w] +def rEEPROM equ $A080 + + +; ** HuC1 only **************************************************************** + +; -- IRREG ($A000-$BFFF) ------------------------------------------------------ +; IR register [r/w] +def rIRREG equ $A000 + +; whether the IR transmitter sees light +def IR_LED_OFF equ $C0 +def IR_LED_ON equ $C1 + + +;****************************************************************************** +; Screen-related constants +;****************************************************************************** + +def SCREEN_WIDTH_PX equ 160 ; width of screen in pixels +def SCREEN_HEIGHT_PX equ 144 ; height of screen in pixels +def SCREEN_WIDTH equ 20 ; width of screen in bytes +def SCREEN_HEIGHT equ 18 ; height of screen in bytes +def SCREEN_AREA equ SCREEN_WIDTH * SCREEN_HEIGHT ; size of screen in bytes + +def TILEMAP_WIDTH_PX equ 256 ; width of tilemap in pixels +def TILEMAP_HEIGHT_PX equ 256 ; height of tilemap in pixels +def TILEMAP_WIDTH equ 32 ; width of tilemap in bytes +def TILEMAP_HEIGHT equ 32 ; height of tilemap in bytes +def TILEMAP_AREA equ TILEMAP_WIDTH * TILEMAP_HEIGHT ; size of tilemap in bytes + +def TILE_WIDTH equ 8 ; width of tile in pixels +def TILE_HEIGHT equ 8 ; height of tile in pixels +def TILE_SIZE equ 16 ; size of tile in bytes (2 bits/pixel) + +def COLOR_SIZE equ 2 ; size of color in bytes (little-endian BGR555) +def PAL_COLORS equ 4 ; colors per palette +def PAL_SIZE equ COLOR_SIZE * PAL_COLORS ; size of palette in bytes + +def COLOR_CH_WIDTH equ 5 ; bits per RGB color channel +def COLOR_CH_MAX equ (1 << COLOR_CH_WIDTH) - 1 + def B_COLOR_RED equ COLOR_CH_WIDTH * 0 ; bits 4-0 + def B_COLOR_GREEN equ COLOR_CH_WIDTH * 1 ; bits 9-5 + def B_COLOR_BLUE equ COLOR_CH_WIDTH * 2 ; bits 14-10 + def COLOR_RED equ %000_11111 ; for the low byte + def COLOR_GREEN_LOW equ %111_00000 ; for the low byte + def COLOR_GREEN_HIGH equ %0_00000_11 ; for the high byte + def COLOR_BLUE equ %0_11111_00 ; for the high byte + +; (DMG only) grayscale shade indexes for BGP, OBP0, and OBP1 +def SHADE_WHITE equ %00 +def SHADE_LIGHT equ %01 +def SHADE_DARK equ %10 +def SHADE_BLACK equ %11 + +; Tilemaps the BG or Window can read from (controlled by LCDC) +def TILEMAP0 equ $9800 ; $9800-$9BFF +def TILEMAP1 equ $9C00 ; $9C00-$9FFF + +; (CGB only) BG tile attribute fields +def B_BG_PRIO equ 7 ; whether the BG tile colors 1-3 are drawn above OBJs +def B_BG_YFLIP equ 6 ; whether the whole BG tile is flipped vertically +def B_BG_XFLIP equ 5 ; whether the whole BG tile is flipped horizontally +def B_BG_BANK1 equ 3 ; which VRAM bank the BG tile is taken from +def BG_PALETTE equ %00000_111 ; which palette the BG tile uses + def BG_PRIO equ 1 << B_BG_PRIO + def BG_YFLIP equ 1 << B_BG_YFLIP + def BG_XFLIP equ 1 << B_BG_XFLIP + def BG_BANK0 equ 0 << B_BG_BANK1 + def BG_BANK1 equ 1 << B_BG_BANK1 + + +;****************************************************************************** +; OBJ-related constants +;****************************************************************************** + +; OAM attribute field offsets +rsreset +def OAMA_Y rb ; 0 + def OAM_Y_OFS equ 16 ; subtract 16 from what's written to OAM to get the real Y position +def OAMA_X rb ; 1 + def OAM_X_OFS equ 8 ; subtract 8 from what's written to OAM to get the real X position +def OAMA_TILEID rb ; 2 +def OAMA_FLAGS rb ; 3 + def B_OAM_PRIO equ 7 ; whether the OBJ is drawn below BG colors 1-3 + def B_OAM_YFLIP equ 6 ; whether the whole OBJ is flipped vertically + def B_OAM_XFLIP equ 5 ; whether the whole OBJ is flipped horizontally + def B_OAM_PAL1 equ 4 ; (DMG only) which of the two palettes the OBJ uses + def B_OAM_BANK1 equ 3 ; (CGB only) which VRAM bank the OBJ takes its tile(s) from + def OAM_PALETTE equ %00000_111 ; (CGB only) which palette the OBJ uses + def OAM_PRIO equ 1 << B_OAM_PRIO + def OAM_YFLIP equ 1 << B_OAM_YFLIP + def OAM_XFLIP equ 1 << B_OAM_XFLIP + def OAM_PAL0 equ 0 << B_OAM_PAL1 + def OAM_PAL1 equ 1 << B_OAM_PAL1 + def OAM_BANK0 equ 0 << B_OAM_BANK1 + def OAM_BANK1 equ 1 << B_OAM_BANK1 +def OBJ_SIZE rb 0 ; size of OBJ in bytes = 4 + +def OAM_COUNT equ 40 ; how many OBJs there are room for in OAM +def OAM_SIZE equ OBJ_SIZE * OAM_COUNT + + +;****************************************************************************** +; Audio channel RAM addresses +;****************************************************************************** + +def AUD1RAM equ $FF10 ; $FF10-$FF14 +def AUD2RAM equ $FF15 ; $FF15-$FF19 +def AUD3RAM equ $FF1A ; $FF1A-$FF1E +def AUD4RAM equ $FF1F ; $FF1F-$FF23 +def AUDRAM_SIZE equ 5 ; size of each audio channel RAM in bytes + +def _AUD3WAVERAM equ $FF30 ; $FF30-$FF3F +def AUD3WAVE_SIZE equ 16 ; size of wave pattern RAM in bytes + + +;****************************************************************************** +; Interrupt vector addresses +;****************************************************************************** + +def INT_HANDLER_VBLANK equ $0040 ; VBlank interrupt handler address +def INT_HANDLER_STAT equ $0048 ; STAT interrupt handler address +def INT_HANDLER_TIMER equ $0050 ; timer interrupt handler address +def INT_HANDLER_SERIAL equ $0058 ; serial interrupt handler address +def INT_HANDLER_JOYPAD equ $0060 ; joypad interrupt handler address + + +;****************************************************************************** +; Boot-up register values +;****************************************************************************** + +; Register A = CPU type +def BOOTUP_A_DMG equ $01 +def BOOTUP_A_CGB equ $11 ; CGB or AGB +def BOOTUP_A_MGB equ $FF + def BOOTUP_A_SGB equ BOOTUP_A_DMG + def BOOTUP_A_SGB2 equ BOOTUP_A_MGB + +; Register B = CPU qualifier (if A is BOOTUP_A_CGB) +def B_BOOTUP_B_AGB equ 0 + def BOOTUP_B_CGB equ 0 << B_BOOTUP_B_AGB + def BOOTUP_B_AGB equ 1 << B_BOOTUP_B_AGB + +; Register C = CPU qualifier +def BOOTUP_C_DMG equ $13 +def BOOTUP_C_SGB equ $14 +def BOOTUP_C_CGB equ $00 ; CGB or AGB + +; Register D = color qualifier +def BOOTUP_D_MONO equ $00 ; DMG, MGB, SGB, or CGB or AGB in DMG mode +def BOOTUP_D_COLOR equ $FF ; CGB or AGB + +; Register E = CPU qualifier (distinguishes DMG variants) +def BOOTUP_E_DMG0 equ $C1 +def BOOTUP_E_DMG equ $C8 +def BOOTUP_E_SGB equ $00 +def BOOTUP_E_CGB_DMGMODE equ $08 ; CGB or AGB in DMG mode +def BOOTUP_E_CGB equ $56 ; CGB or AGB + + +;****************************************************************************** +; Aliases +;****************************************************************************** + +; Prefer the standard names to these aliases, which may be official but are +; less directly meaningful or human-readable. + +def rP1 equ rJOYP + +def rNR10 equ rAUD1SWEEP +def rNR11 equ rAUD1LEN +def rNR12 equ rAUD1ENV +def rNR13 equ rAUD1LOW +def rNR14 equ rAUD1HIGH +def rNR21 equ rAUD2LEN +def rNR22 equ rAUD2ENV +def rNR23 equ rAUD2LOW +def rNR24 equ rAUD2HIGH +def rNR30 equ rAUD3ENA +def rNR31 equ rAUD3LEN +def rNR32 equ rAUD3LEVEL +def rNR33 equ rAUD3LOW +def rNR34 equ rAUD3HIGH +def rNR41 equ rAUD4LEN +def rNR42 equ rAUD4ENV +def rNR43 equ rAUD4POLY +def rNR44 equ rAUD4GO +def rNR50 equ rAUDVOL +def rNR51 equ rAUDTERM +def rNR52 equ rAUDENA + +def rKEY0 equ rSYS +def rKEY1 equ rSPD + +def rHDMA1 equ rVDMA_SRC_HIGH +def rHDMA2 equ rVDMA_SRC_LOW +def rHDMA3 equ rVDMA_DEST_HIGH +def rHDMA4 equ rVDMA_DEST_LOW +def rHDMA5 equ rVDMA_LEN + +def rBCPS equ rBGPI +def rBCPD equ rBGPD + +def rOCPS equ rOBPI +def rOCPD equ rOBPD + +def rSVBK equ rWBK + +endc ; HARDWARE_INC diff --git a/tools/gb-payload-generator/include/constants/serial_constants.asm b/tools/gb-payload-generator/include/constants/serial_constants.asm new file mode 100644 index 0000000..37747b7 --- /dev/null +++ b/tools/gb-payload-generator/include/constants/serial_constants.asm @@ -0,0 +1,31 @@ +DEF ESTABLISH_CONNECTION_WITH_INTERNAL_CLOCK EQU $01 +DEF ESTABLISH_CONNECTION_WITH_EXTERNAL_CLOCK EQU $02 + +DEF USING_EXTERNAL_CLOCK EQU $01 +DEF USING_INTERNAL_CLOCK EQU $02 +DEF CONNECTION_NOT_ESTABLISHED EQU $ff + +; signals the start of an array of bytes transferred over the link cable +DEF SERIAL_PREAMBLE_BYTE EQU $FD + +; this byte is used when there is no data to send +DEF SERIAL_NO_DATA_BYTE EQU $FE + +; signals the end of one part of a patch list (there are two parts) for player/enemy party data +DEF SERIAL_PATCH_LIST_PART_TERMINATOR EQU $FF + +DEF SERIAL_PREAMBLE_LENGTH EQU 6 +DEF SERIAL_RN_PREAMBLE_LENGTH EQU 7 +DEF SERIAL_RNS_LENGTH EQU 10 + +DEF LINK_STATE_NONE EQU $00 ; not using link +DEF LINK_STATE_IN_CABLE_CLUB EQU $01 ; in a cable club room (Colosseum or Trade Centre) +DEF LINK_STATE_START_TRADE EQU $02 ; pre-trade selection screen initialisation +DEF LINK_STATE_START_BATTLE EQU $03 ; pre-battle initialisation +DEF LINK_STATE_BATTLING EQU $04 ; in a link battle +DEF LINK_STATE_RESET EQU $05 ; reset game (unused) +DEF LINK_STATE_TRADING EQU $32 ; in a link trade + +DEF LINKBATTLE_RUN EQU $F +DEF LINKBATTLE_STRUGGLE EQU $E +DEF LINKBATTLE_NO_ACTION EQU $D diff --git a/tools/gb-payload-generator/include/constants/symbols.asm b/tools/gb-payload-generator/include/constants/symbols.asm new file mode 100644 index 0000000..f7188a7 --- /dev/null +++ b/tools/gb-payload-generator/include/constants/symbols.asm @@ -0,0 +1,23 @@ +DEF Start = 0x100 +DEF ClearScreen = 0x190F +DEF PlaceString = 0x1955 +DEF SoftReset = 0x1F49 +DEF Serial_ExchangeBytes = 0x216F +DEF Bankswitch = 0x35D6 +DEF CableClub_TextBoxBorder = 0x5AB3 +DEF SaveCurrentBoxDataBank = 0x1C +DEF SaveCurrentBoxData = 0x77E2 +DEF SavePartyAndDexDataBank = 0x1C +DEF SavePartyAndDexData = 0x780F +DEF RemovePokemon = 0x7B68 + +DEF wTileMap = 0xC3A0 +DEF wSerialPartyMonsPatchList = 0xC508 +DEF wSerialEnemyMonsPatchList = 0xC5D0 +DEF wSerialOtherGameboyRandomNumberListBlock = 0xCD91 +DEF wWhichPokemon = 0xCF92 +DEF wRemoveMonFromBox = 0xCF95 +DEF wSerialEnemyDataBlock = 0xD893 +DEF wBoxCount = 0xDA80 + +DEF hOnCGB = 0xFFFE \ No newline at end of file diff --git a/tools/gb-payload-generator/include/payload/patches.asm b/tools/gb-payload-generator/include/payload/patches.asm new file mode 100644 index 0000000..ca7babe --- /dev/null +++ b/tools/gb-payload-generator/include/payload/patches.asm @@ -0,0 +1,78 @@ +DEF DRAWBOX_OFFSET = 0x5883 +DEF DRAWBOX_SEARCH = 0xE53E + +DEF SERIAL_EXCHANGEBYTES_OFFSET = 0xBF1 +DEF SERIAL_EXCHANGEBYTES_SEARCH = 0xE0AB + +DEF PRINTWAITINGTEXT_OFFSET = 0x49ED +DEF PRINTWAITINGTEXT_SEARCH = 0x216B + +; start at 0x0bf1, search for E0 AB + +; Serial_ExchangeBytes has different offsets based on language: +; RB +; EN: 0x216F +; FR: 0x216B +; DE: 0x216F +; IT: 0x216F +; SP: 0x216E +; Y +; EN: 00:1FCB +; FR: 00:1FC7 +; DE: 00:1FD0 +; IT: 00:1FCB +; SP: 00:1FCA +; JP +; RG&RGA: 00:0BF1 +; B : 00:214F +; Y0: 00:1FF4 +; Y1-3: 00:1FFD + +; start at 01:5883, look for 0xE5 0x3E + +; CableClub_TextBoxBorder has the following offsets: +; RB +; EN: 01:5AB3 +; FR: 01:5B41 +; DE: 01:5ACD +; IT: 01:5B0E & 01:5B0D +; SP: 01:5B00 +; Y +; EN: 01:5b67 +; FR: 01:5be3 +; DE: 01:5b6f +; IT: 01:5bb0 +; SP: 01:5ba4 +; JP +; G: 01:58DF +; R: 01:58DE +; GA: 01:5884 +; RA: 01:5883 +; B : 01:59F1 +; Y : 01:5A8D + +; PrintWaitingText analysis +; +; start 0x49ed, look for 216b +; +; Red & Blue +; EN: 0x4c05 +; FR: 0x4c82 +; DE: 0x4c01 +; ITR:0x4c56 +; ITB:0x4c55 +; SP: 0x4c3d +; JPR:0x49ed +; JPG:0x49ee +; JPB:0x4b5c +; +; Yellow +; EN: 0x4b89 +; FR: 0x4c05 +; DE: 0x4b84 +; IT: 0x4bd9 +; SP: 0x4bc2 +; JP0:0x4ad7 +; JP1:0x4ad7 +; JP2:0x4ad7 +; JP3:0x4ad7 \ No newline at end of file diff --git a/tools/gb-payload-generator/include/payload/payload.asm b/tools/gb-payload-generator/include/payload/payload.asm new file mode 100644 index 0000000..daac687 --- /dev/null +++ b/tools/gb-payload-generator/include/payload/payload.asm @@ -0,0 +1,3 @@ +DEF RNGSEED_SIZE = 0xA +DEF PAYLOAD_SIZE = 0x1AA +DEF PATCHLIST_SIZE = 0xC9 \ No newline at end of file diff --git a/tools/gb-payload-generator/include/payload/settings.asm b/tools/gb-payload-generator/include/payload/settings.asm new file mode 100644 index 0000000..82d6a60 --- /dev/null +++ b/tools/gb-payload-generator/include/payload/settings.asm @@ -0,0 +1,5 @@ +DEF sendingText EQUS "\"SENDING DATA@\"" +DEF RIVAL_NAME EQUS "\"FENNEL\"" +DEF textBorderWidth = 0x0C +DEF textBorderHeight = 0x01 +DEF textBorderLeftCorner = wTileMap + 0x8F \ No newline at end of file diff --git a/tools/gb-payload-generator/include/payload/species.asm b/tools/gb-payload-generator/include/payload/species.asm new file mode 100644 index 0000000..9c1f862 --- /dev/null +++ b/tools/gb-payload-generator/include/payload/species.asm @@ -0,0 +1,2 @@ +DEF shortMon = 0xE3 +DEF pointerMon = 0xFC \ No newline at end of file diff --git a/tools/gb-payload-generator/src/payloads/payload_rb.asm b/tools/gb-payload-generator/src/payloads/payload_rb.asm new file mode 100644 index 0000000..5a345a6 --- /dev/null +++ b/tools/gb-payload-generator/src/payloads/payload_rb.asm @@ -0,0 +1,137 @@ +INCLUDE "../../include/constants/charmap.asm" +INCLUDE "../../include/constants/serial_constants.asm" +INCLUDE "../../include/constants/symbols.asm" +INCLUDE "../../include/payload/species.asm" +INCLUDE "../../include/payload/payload.asm" +INCLUDE "../../include/payload/patches.asm" +INCLUDE "../../include/payload/settings.asm" + +DEF packetStartAddress = 0xCFE7 +DEF packetSize = 8 + +SECTION "Payload", ROM0 +Payload: +LOAD "RNGSeed", WRAM0[wSerialOtherGameboyRandomNumberListBlock - 1] +RNGSeed: + ds 5 + jp SerialPatchList +.end +ds RNGSEED_SIZE - (RNGSeed.end - RNGSeed), 0 +ENDL + + +LOAD "PartyData", WRAMX[wSerialEnemyDataBlock - 1] +PartyDataPreamble: + ds 7, SERIAL_PREAMBLE_BYTE +.end + +PartyData: + db RIVAL_NAME + ds 11 - STRLEN(RIVAL_NAME), CHARVAL("@") +.partyCount + db 0x06 +.partyList + ds 0x160, shortMon + db 0xFC + db 0xFF +.transferList + ds 30, SERIAL_PREAMBLE_BYTE + ds 11, 0xFF +.end +ds PAYLOAD_SIZE - (PartyData.end - PartyDataPreamble), 0 +ENDL + +LOAD "serialPatch", WRAM0[wSerialEnemyMonsPatchList - 1] +SerialPatchPreamble: + ds 5, SERIAL_PREAMBLE_BYTE + ds 2, SERIAL_PATCH_LIST_PART_TERMINATOR +.end + +SerialPatchList: + call ClearScreen + ld hl, wTileMap + 0x8F + ld bc, (textBorderHeight << 8) + textBorderWidth + call CableClub_TextBoxBorder + ld l, LOW(0x44) + ld de, .waitString + call PlaceString + +.sendPacketLoop + ld hl, 0xCFFD + ld a, [hli] + ld d, [hl] + ld e, a + ld hl, packetStartAddress + ld c, 0 +.packet_loop + ld b, 0 + ld a, [de] + add a, c + ld c, a + ld a, [de] + inc de + cp a, SERIAL_NO_DATA_BYTE + jr nz, .feBypass + dec a + inc b +.feBypass + ld [hli], a + ld [hl], b + inc hl + ld a, LOW(packetStartAddress + 2 * packetSize) + cp a, l + jr nz, .packet_loop + ld a, c + res 7, a + ld [hli], a + ld a, d + ld [hli], a + ld a, e + ld [hli], a + + ld a, 1 + ldh [0xFFAA], a + ld hl, 0xCFE5 + ld [hl], SERIAL_PREAMBLE_BYTE + inc hl + ld [hl], 0 + dec hl + ld de, 0xCFFA + ld bc, 0x15 + call Serial_ExchangeBytes + ld a, [0xCFFE] + inc a + jr nz, .sendPacketLoop + + inc a + ldh [0xFFAA], a + ld [wRemoveMonFromBox], a + ld hl, 0x316 + ld de, PartyData.transferList + ld bc, 0x1E + call Serial_ExchangeBytes + ld hl, PartyData.transferList +.removeArrayLoop + ld a, [wBoxCount] + ld b, a + ld a, [hli] + cp a, 0xFF + jr z, .saveBox + cp a, b + jr nc, .removeArrayLoop + + ld [wWhichPokemon], a + push hl + call RemovePokemon + pop hl + jr .removeArrayLoop +.saveBox + ld b, SaveCurrentBoxDataBank + ld hl, SaveCurrentBoxData + call Bankswitch + jp SoftReset +.waitString + db "SENDING DATA@" +.end +ds PATCHLIST_SIZE - (SerialPatchList.end - SerialPatchPreamble), 0 + \ No newline at end of file diff --git a/tools/gb-payload-generator/src/payloads/payload_rb_experimental.asm b/tools/gb-payload-generator/src/payloads/payload_rb_experimental.asm new file mode 100644 index 0000000..a8895a9 --- /dev/null +++ b/tools/gb-payload-generator/src/payloads/payload_rb_experimental.asm @@ -0,0 +1,161 @@ +INCLUDE "../../include/constants/charmap.asm" +INCLUDE "../../include/constants/serial_constants.asm" +INCLUDE "../../include/constants/symbols.asm" +INCLUDE "../../include/payload/species.asm" +INCLUDE "../../include/payload/payload.asm" +INCLUDE "../../include/payload/patches.asm" +INCLUDE "../../include/payload/settings.asm" + +SECTION "Universal payload non-JP R/B", ROM0 + +; RNG Seed +; 10 Bytes +; Location of the entrance vector + +LOAD "RNGSeed", WRAM0[wSerialOtherGameboyRandomNumberListBlock - 1] +RNGSeed: + ds 5 + jp SerialPatchList +.end +ds RNGSEED_SIZE - (RNGSeed.end - RNGSeed), 0 +ENDL + +; Payload, consisting of preamble, rival name and actual party data + +; Preamble (offset 0x00) +; 0x07 in length +; must be filled with 0xFD, one 0xFD will be consumed and fail to transfer. + +; Rival name (offset 0x07) +; 0x0B in lenth +; Set to the stored name + +; Party Data (offset 0x12) +; Can be up to 0x1A8 bytes in length. +; First byte determines number of pokémon (arbitrary value) +; Following values determine party data, the main part we care about is the pokémon list. +; Since we disrupt the stack with the party list, the party list itself does not need to be terminated. + +LOAD "PAYLOAD", WRAMX[wSerialEnemyDataBlock - 1] +PartyPreamble: + ds 7, SERIAL_PREAMBLE_BYTE +.end +RivalName: + db RIVAL_NAME + ds 11 - STRLEN(RIVAL_NAME), CHARVAL("@") +PartyData: + db 0x06 +.partyList + ds 0x160, shortMon + db pointerMon +.end +ds PAYLOAD_SIZE - (PartyData.end - PartyPreamble), 0 +ENDL + +; SerialPatchPreamble (offset 0x1B4) +; 0x07 in length +; Set as five 0xFD and two 0xFF, prevents patchlist from modifying anything on the party list. + +; SerialPatchList (offset 0x1BB) +; At 0x1BB / 0x1DE, 0xC2 in length (0xC4, but the last 2 are unused) +; Fill with custom code + +LOAD "serialPatch", WRAM0[wSerialEnemyMonsPatchList - 1] +SerialPatchPreamble: + ds 5, SERIAL_PREAMBLE_BYTE + ds 2, SERIAL_PATCH_LIST_PART_TERMINATOR +.end +SerialPatchList: + ld bc, 0x0168 + ld hl, 0xC3A0 +.clearScreenLoop + ld a, 0x7F + ld [hli], a + dec bc + ld a, b + or c + jr nz, .clearScreenLoop + + ld hl, PRINTWAITINGTEXT_OFFSET + ld de, .callPrintWaitingText + 1 + ld bc, PRINTWAITINGTEXT_SEARCH + call .findPointerAndPatch +.callPrintWaitingText + call .callPrintWaitingText + + ld hl, SERIAL_EXCHANGEBYTES_OFFSET + ld de, .callSerial_ExchangeBytes + 3 + ld bc, SERIAL_EXCHANGEBYTES_SEARCH + call .findPointerAndPatch +.sendChecksum + ld hl, 0x14E ; cartridge checksum + ld a, [hli] + ld b, [hl] + + ld hl, SerialPatchPreamble + 3 ; prepare to send cartridge checksum + ld [hli], a + ld [hl], b + inc hl + add a, b + res 7, a + ld [hl], a ; place checksum + ld hl, SerialPatchPreamble + 2 + ld de, wSerialPartyMonsPatchList + 2 + push hl + push hl + push de + ld bc, 0x5 + call .callSerial_ExchangeBytes + pop de + pop hl + ld bc, 0xFF ; placeholder, exact size to be determined + call .callSerial_ExchangeBytes + ; to do: add verification that the payload is correctly processed + pop hl + inc hl + push hl + push hl + pop de + ld c, 0xC0 +.findNotPreamble + ld a, [hli] + cp SERIAL_PREAMBLE_BYTE + jr z, .findNotPreamble +.alignPayload + ld [de], a + inc de + ld a, [hli] + dec c + jp nz, .alignPayload + pop hl + ld a, [hl] + and a, a + jr z, .sendChecksum + inc a + jp z, Start + jp hl +.callSerial_ExchangeBytes + ld a, 1 + jp .callSerial_ExchangeBytes +.findPointerAndPatch +; b = first opcode to search for +; c = second opcode to search for +; de = address to write pointer to +; hl = offset to start searching from +; return destination pointer in hl, made to minimize amount of M-cycles taken + ld a, [hli] + cp b + jr nz, .findPointerAndPatch + ld a, [hl] + cp c + jr nz, .findPointerAndPatch + dec hl + ld a, l + ld [de], a + inc de + ld a, h + ld [de], a + ret +.end +ds PATCHLIST_SIZE - (SerialPatchList.end - SerialPatchPreamble), 0 +ENDL \ No newline at end of file diff --git a/tools/gb-payload-generator/src/payloads/payload_y_experimental.asm b/tools/gb-payload-generator/src/payloads/payload_y_experimental.asm new file mode 100644 index 0000000..5ed068d --- /dev/null +++ b/tools/gb-payload-generator/src/payloads/payload_y_experimental.asm @@ -0,0 +1,207 @@ +INCLUDE "../../include/constants/charmap.asm" +INCLUDE "../../include/constants/serial_constants.asm" +INCLUDE "../../include/constants/symbols.asm" +INCLUDE "../../include/constants/hardware.inc" +INCLUDE "../../include/payload/species.asm" +INCLUDE "../../include/payload/payload.asm" +INCLUDE "../../include/payload/patches.asm" +INCLUDE "../../include/payload/settings.asm" + +SECTION "Universal payload non-JP Y", ROM0 + +; RNG Seed +; 10 Bytes +; Location of the entrance vector + +LOAD "RNGSeed", WRAM0[wSerialOtherGameboyRandomNumberListBlock - 1] +RNGSeed: + ds 5 + jp SerialPatchList +.end +ds RNGSEED_SIZE - (RNGSeed.end - RNGSeed), 0 +ENDL + +; Payload, consisting of preamble, rival name and actual party data + +; Preamble (offset 0x00) +; 0x07 in length +; must be filled with 0xFD, one 0xFD will be consumed and fail to transfer. + +; Rival name (offset 0x07) +; 0x0B in lenth +; Set to the stored name + +; Party Data (offset 0x12) +; Can be up to 0x1A8 bytes in length. +; First byte determines number of pokémon (arbitrary value) +; Following values determine party data, the main part we care about is the pokémon list. +; Since we disrupt the stack with the party list, the party list itself does not need to be terminated. + +LOAD "PAYLOAD", WRAMX[wSerialEnemyDataBlock - 1] +PartyPreamble: + ds 7, SERIAL_PREAMBLE_BYTE +.end +RivalName: + db RIVAL_NAME + ds 11 - STRLEN(RIVAL_NAME), CHARVAL("@") +PartyData: + db 0x06 +.partyList + ds 0x160, shortMon + db pointerMon +.end +ds PAYLOAD_SIZE - (PartyData.end - PartyPreamble), 0 +ENDL + +; SerialPatchPreamble (offset 0x1B4) +; 0x07 in length +; Set as five 0xFD and two 0xFF, prevents patchlist from modifying anything on the party list. + +; SerialPatchList (offset 0x1BB) +; At 0x1BB / 0x1DE, 0xC2 in length (0xC4, but the last 2 are unused) +; Fill with custom code + +; FD FD FD FD FF FF 00 00 00 00 00 00 00 00 00 00 +; 00 00 00 01 68 01 00 00 00 21 A0 C3 3E 7F 22 0B +; 78 B1 20 F8 21 ED 49 11 04 C6 00 00 00 01 6B 21 +; CD 75 C6 CD 03 C6 21 F1 0B 11 71 C6 06 E0 00 00 +; 00 0E AB CD 75 C6 21 4E 01 2A 46 21 D2 C5 22 70 +; 23 F5 00 00 00 F1 80 CB BF 77 21 D1 C5 11 0A C5 +; E5 E5 D5 01 05 00 00 00 00 CD 6E C6 D1 E1 01 FF +; 00 CD 6E C6 E1 23 E5 E5 0E C0 00 00 00 E5 D1 2A +; FE FD 28 FB 12 13 2A 0D C2 54 C6 E1 7E F5 00 00 +; 00 F1 A7 28 B1 3C C0 F0 FE C6 10 C3 00 01 3E 01 +; C3 6E C6 00 00 2A B8 20 FC 7E B9 20 F8 2B 7D 12 +; 13 7C 12 C9 + +; FD FD 8C 84 96 FF|00 00 00 00 00 00 00 00 00 00 +; 00 00 00[00]00 00 8C 84 96 00 00 00 00 00 00 00 +; 00 00 00 00 00 00 00 00 00 00 8C 84 96 00 00 00 +; 00 00 00 00 00 00 00 00 00 00 00 00 00 00 8C 84 +; 96 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; 00 00 8C 84 96 00 00 00 00 00 00 00 00 00 00 00 +; 00 00 00 00 00 00 8C 84 96 00 00 00 00 00 00 00 +; 00 00 00 00 00 00 00 00 00 00 8C 84 96 00 00 00 +; 00 00 00 00 00 00 00 00 00 00 00 00 00 00 8C 84 +; 96 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; 00 00 8C 84 96 00 00 00 00 00 00 00 00 00 00 00 +; 00 00 00 00 00 00 8C 84 96 00 00 00 00 00 00 00 +; 00 00 00 00 00 00 00 00 19 19 8C 84 96 19 19 19 + +; 27 bytes lost -> adc h ; add h; sub a [hl] + +LOAD "serialPatch", WRAM0[wSerialEnemyMonsPatchList - 1] +SerialPatchPreamble: + ds 5, SERIAL_PREAMBLE_BYTE + ds 2, SERIAL_PATCH_LIST_PART_TERMINATOR +.end +SerialPatchList: + ds 13, 0 ; start from 0xC5E4 + ld bc, 0x0168 + ds 3, 0 + ld hl, 0xC3A0 +.clearScreenLoop + ld a, 0x7F + ld [hli], a + dec bc + ld a, b + or c + jr nz, .clearScreenLoop + ld hl, PRINTWAITINGTEXT_OFFSET + ld de, .callPrintWaitingText + 1 + ds 3, 0 + ld bc, PRINTWAITINGTEXT_SEARCH + call .findPointerAndPatch +.callPrintWaitingText + call .callPrintWaitingText + + ld hl, SERIAL_EXCHANGEBYTES_OFFSET + ld de, .callSerial_ExchangeBytes + 3 + ld b, HIGH(SERIAL_EXCHANGEBYTES_SEARCH) + ds 3, 0 + ld c, LOW(SERIAL_EXCHANGEBYTES_SEARCH) + call .findPointerAndPatch +.sendChecksum + ld hl, 0x14E ; cartridge checksum + ld a, [hli] + ld b, [hl] + + ld hl, SerialPatchPreamble + 3 ; prepare to send cartridge checksum + ld [hli], a + ld [hl], b + inc hl + push af + ds 3, 0 + pop af + add a, b + res 7, a + ld [hl], a ; place checksum + ld hl, SerialPatchPreamble + 2 + ld de, wSerialPartyMonsPatchList + 2 + push hl + push hl + push de + ld bc, 0x5 + ds 3, 0 + call .callSerial_ExchangeBytes + pop de + pop hl + ld bc, 0xFF ; placeholder, exact size to be determined + call .callSerial_ExchangeBytes + ; to do: add verification that the payload is correctly processed + pop hl + inc hl + push hl + push hl + ld c, 0xC0 + ds 3, 0 + push hl + pop de +.findNotPreamble + ld a, [hli] + cp SERIAL_PREAMBLE_BYTE + jr z, .findNotPreamble +.alignPayload + ld [de], a + inc de + ld a, [hli] + dec c + jp nz, .alignPayload + pop hl + ld a, [hl] + push af + ds 3, 0 + pop af + and a, a + jr z, .sendChecksum + inc a + ret nz + ldh a, [hOnCGB] + add a, BOOTUP_A_CGB - 1 + jp Start +.callSerial_ExchangeBytes + ld a, 1 + jp .callSerial_ExchangeBytes + ds 2, 0 ; first byte of 3 gets overwritten by RAMPatch +.findPointerAndPatch +; b = first opcode to search for +; c = second opcode to search for +; de = address to write pointer to +; hl = offset to start searching from +; return destination pointer in hl, made to minimize amount of M-cycles taken + ld a, [hli] + cp b + jr nz, .findPointerAndPatch + ld a, [hl] + cp c + jr nz, .findPointerAndPatch + dec hl + ld a, l + ld [de], a + inc de + ld a, h + ld [de], a + ret +.end +ds PATCHLIST_SIZE - (SerialPatchList.end - SerialPatchPreamble), 0 +ENDL \ No newline at end of file diff --git a/tools/payload-generator/Makefile b/tools/rom-value-generator/Makefile similarity index 100% rename from tools/payload-generator/Makefile rename to tools/rom-value-generator/Makefile diff --git a/tools/payload-generator/include/gb_rom_values/base_gb_rom_struct.h b/tools/rom-value-generator/include/gb_rom_values/base_gb_rom_struct.h similarity index 100% rename from tools/payload-generator/include/gb_rom_values/base_gb_rom_struct.h rename to tools/rom-value-generator/include/gb_rom_values/base_gb_rom_struct.h diff --git a/tools/payload-generator/include/gb_rom_values/gb_rom_values.h b/tools/rom-value-generator/include/gb_rom_values/gb_rom_values.h similarity index 100% rename from tools/payload-generator/include/gb_rom_values/gb_rom_values.h rename to tools/rom-value-generator/include/gb_rom_values/gb_rom_values.h diff --git a/tools/payload-generator/include/gba_rom_values/base_gba_rom_struct.h b/tools/rom-value-generator/include/gba_rom_values/base_gba_rom_struct.h similarity index 100% rename from tools/payload-generator/include/gba_rom_values/base_gba_rom_struct.h rename to tools/rom-value-generator/include/gba_rom_values/base_gba_rom_struct.h diff --git a/tools/payload-generator/include/gba_rom_values/gba_rom_values.h b/tools/rom-value-generator/include/gba_rom_values/gba_rom_values.h similarity index 100% rename from tools/payload-generator/include/gba_rom_values/gba_rom_values.h rename to tools/rom-value-generator/include/gba_rom_values/gba_rom_values.h diff --git a/tools/payload-generator/include/payloads/binary_patch_generator.h b/tools/rom-value-generator/include/payloads/binary_patch_generator.h similarity index 100% rename from tools/payload-generator/include/payloads/binary_patch_generator.h rename to tools/rom-value-generator/include/payloads/binary_patch_generator.h diff --git a/tools/payload-generator/include/payloads/payload_builder.h b/tools/rom-value-generator/include/payloads/payload_builder.h similarity index 100% rename from tools/payload-generator/include/payloads/payload_builder.h rename to tools/rom-value-generator/include/payloads/payload_builder.h diff --git a/tools/payload-generator/include/payloads/payload_file_reader.h b/tools/rom-value-generator/include/payloads/payload_file_reader.h similarity index 100% rename from tools/payload-generator/include/payloads/payload_file_reader.h rename to tools/rom-value-generator/include/payloads/payload_file_reader.h diff --git a/tools/payload-generator/include/payloads/payload_file_writer.h b/tools/rom-value-generator/include/payloads/payload_file_writer.h similarity index 100% rename from tools/payload-generator/include/payloads/payload_file_writer.h rename to tools/rom-value-generator/include/payloads/payload_file_writer.h diff --git a/tools/payload-generator/include/payloads/z80_asm.h b/tools/rom-value-generator/include/payloads/z80_asm.h similarity index 100% rename from tools/payload-generator/include/payloads/z80_asm.h rename to tools/rom-value-generator/include/payloads/z80_asm.h diff --git a/tools/payload-generator/src/debug_scripts/__pycache__/bgb_link.cpython-313.pyc b/tools/rom-value-generator/src/debug_scripts/__pycache__/bgb_link.cpython-313.pyc similarity index 100% rename from tools/payload-generator/src/debug_scripts/__pycache__/bgb_link.cpython-313.pyc rename to tools/rom-value-generator/src/debug_scripts/__pycache__/bgb_link.cpython-313.pyc diff --git a/tools/payload-generator/src/debug_scripts/bgb_link.py b/tools/rom-value-generator/src/debug_scripts/bgb_link.py similarity index 100% rename from tools/payload-generator/src/debug_scripts/bgb_link.py rename to tools/rom-value-generator/src/debug_scripts/bgb_link.py diff --git a/tools/payload-generator/src/debug_scripts/cable_club_y.py b/tools/rom-value-generator/src/debug_scripts/cable_club_y.py similarity index 100% rename from tools/payload-generator/src/debug_scripts/cable_club_y.py rename to tools/rom-value-generator/src/debug_scripts/cable_club_y.py diff --git a/tools/payload-generator/src/gb_rom_values/gb_rom_values_eng.cpp b/tools/rom-value-generator/src/gb_rom_values/gb_rom_values_eng.cpp similarity index 100% rename from tools/payload-generator/src/gb_rom_values/gb_rom_values_eng.cpp rename to tools/rom-value-generator/src/gb_rom_values/gb_rom_values_eng.cpp diff --git a/tools/payload-generator/src/gb_rom_values/gb_rom_values_fre.cpp b/tools/rom-value-generator/src/gb_rom_values/gb_rom_values_fre.cpp similarity index 100% rename from tools/payload-generator/src/gb_rom_values/gb_rom_values_fre.cpp rename to tools/rom-value-generator/src/gb_rom_values/gb_rom_values_fre.cpp diff --git a/tools/payload-generator/src/gb_rom_values/gb_rom_values_ger.cpp b/tools/rom-value-generator/src/gb_rom_values/gb_rom_values_ger.cpp similarity index 100% rename from tools/payload-generator/src/gb_rom_values/gb_rom_values_ger.cpp rename to tools/rom-value-generator/src/gb_rom_values/gb_rom_values_ger.cpp diff --git a/tools/payload-generator/src/gb_rom_values/gb_rom_values_ita.cpp b/tools/rom-value-generator/src/gb_rom_values/gb_rom_values_ita.cpp similarity index 100% rename from tools/payload-generator/src/gb_rom_values/gb_rom_values_ita.cpp rename to tools/rom-value-generator/src/gb_rom_values/gb_rom_values_ita.cpp diff --git a/tools/payload-generator/src/gb_rom_values/gb_rom_values_spa.cpp b/tools/rom-value-generator/src/gb_rom_values/gb_rom_values_spa.cpp similarity index 100% rename from tools/payload-generator/src/gb_rom_values/gb_rom_values_spa.cpp rename to tools/rom-value-generator/src/gb_rom_values/gb_rom_values_spa.cpp diff --git a/tools/payload-generator/src/gba_rom_values/gba_rom_values_eng.cpp b/tools/rom-value-generator/src/gba_rom_values/gba_rom_values_eng.cpp similarity index 100% rename from tools/payload-generator/src/gba_rom_values/gba_rom_values_eng.cpp rename to tools/rom-value-generator/src/gba_rom_values/gba_rom_values_eng.cpp diff --git a/tools/payload-generator/src/gba_rom_values/gba_rom_values_fre.cpp b/tools/rom-value-generator/src/gba_rom_values/gba_rom_values_fre.cpp similarity index 100% rename from tools/payload-generator/src/gba_rom_values/gba_rom_values_fre.cpp rename to tools/rom-value-generator/src/gba_rom_values/gba_rom_values_fre.cpp diff --git a/tools/payload-generator/src/gba_rom_values/gba_rom_values_ger.cpp b/tools/rom-value-generator/src/gba_rom_values/gba_rom_values_ger.cpp similarity index 100% rename from tools/payload-generator/src/gba_rom_values/gba_rom_values_ger.cpp rename to tools/rom-value-generator/src/gba_rom_values/gba_rom_values_ger.cpp diff --git a/tools/payload-generator/src/gba_rom_values/gba_rom_values_ita.cpp b/tools/rom-value-generator/src/gba_rom_values/gba_rom_values_ita.cpp similarity index 100% rename from tools/payload-generator/src/gba_rom_values/gba_rom_values_ita.cpp rename to tools/rom-value-generator/src/gba_rom_values/gba_rom_values_ita.cpp diff --git a/tools/payload-generator/src/gba_rom_values/gba_rom_values_jpn.cpp b/tools/rom-value-generator/src/gba_rom_values/gba_rom_values_jpn.cpp similarity index 100% rename from tools/payload-generator/src/gba_rom_values/gba_rom_values_jpn.cpp rename to tools/rom-value-generator/src/gba_rom_values/gba_rom_values_jpn.cpp diff --git a/tools/payload-generator/src/gba_rom_values/gba_rom_values_spa.cpp b/tools/rom-value-generator/src/gba_rom_values/gba_rom_values_spa.cpp similarity index 100% rename from tools/payload-generator/src/gba_rom_values/gba_rom_values_spa.cpp rename to tools/rom-value-generator/src/gba_rom_values/gba_rom_values_spa.cpp diff --git a/tools/payload-generator/src/main.cpp b/tools/rom-value-generator/src/main.cpp similarity index 96% rename from tools/payload-generator/src/main.cpp rename to tools/rom-value-generator/src/main.cpp index 1234c19..ab6a935 100644 --- a/tools/payload-generator/src/main.cpp +++ b/tools/rom-value-generator/src/main.cpp @@ -185,7 +185,7 @@ void generate_and_test_payloads_for(uint8_t generation, bool yellow_version, con if (std::filesystem::exists(full_output_path)) { - std::filesystem::file_time_type inf_time = std::filesystem::last_write_time("tools/payload-generator/src/payloads/payload_builder.cpp"); + std::filesystem::file_time_type inf_time = std::filesystem::last_write_time("tools/rom-vallue-generator/src/payloads/payload_builder.cpp"); std::filesystem::file_time_type outf_time = std::filesystem::last_write_time(full_output_path); if (outf_time > inf_time) diff --git a/tools/payload-generator/src/payloads/binary_patch_generator.cpp b/tools/rom-value-generator/src/payloads/binary_patch_generator.cpp similarity index 100% rename from tools/payload-generator/src/payloads/binary_patch_generator.cpp rename to tools/rom-value-generator/src/payloads/binary_patch_generator.cpp diff --git a/tools/payload-generator/src/payloads/payload_builder.cpp b/tools/rom-value-generator/src/payloads/payload_builder.cpp similarity index 100% rename from tools/payload-generator/src/payloads/payload_builder.cpp rename to tools/rom-value-generator/src/payloads/payload_builder.cpp diff --git a/tools/payload-generator/src/payloads/payload_file_reader.cpp b/tools/rom-value-generator/src/payloads/payload_file_reader.cpp similarity index 100% rename from tools/payload-generator/src/payloads/payload_file_reader.cpp rename to tools/rom-value-generator/src/payloads/payload_file_reader.cpp diff --git a/tools/payload-generator/src/payloads/payload_file_writer.cpp b/tools/rom-value-generator/src/payloads/payload_file_writer.cpp similarity index 100% rename from tools/payload-generator/src/payloads/payload_file_writer.cpp rename to tools/rom-value-generator/src/payloads/payload_file_writer.cpp diff --git a/tools/payload-generator/src/payloads/z80_asm.cpp b/tools/rom-value-generator/src/payloads/z80_asm.cpp similarity index 100% rename from tools/payload-generator/src/payloads/z80_asm.cpp rename to tools/rom-value-generator/src/payloads/z80_asm.cpp