From c1a96584e4a03c4ccf15bfc3b623b01f94ed0eb1 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Tue, 25 Jul 2023 00:25:06 +0000 Subject: [PATCH] input: added --- Makefile | 2 +- input.cpp | 51 +++++++++ input.hpp | 57 ++++++++++ main.cpp | 115 +++++++++++++++---- map.hpp | 226 +++++++++++++++++++++++++++++++++++++ map_object.hpp | 60 ++++++++++ tools/parse/__main__.py | 5 + tools/parse/map_objects.py | 55 ++++++--- tools/parse/parse.py | 8 ++ 9 files changed, 542 insertions(+), 37 deletions(-) create mode 100644 input.cpp create mode 100644 input.hpp create mode 100644 map.hpp create mode 100644 map_object.hpp create mode 100644 tools/parse/__main__.py diff --git a/Makefile b/Makefile index 9794455..ea88ccd 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CFLAGS = -Isaturn OPT ?= -Og LIB = ./saturn -SRC = main.o +SRC = main.o input.o DEP = $(patsubst %.o,%.d,$(SRC)) res = $(subst pokered/,res/,$(patsubst %.$(1),%.$(1).o,$(wildcard $(2)*.$(1)))) diff --git a/input.cpp b/input.cpp new file mode 100644 index 0000000..53077e4 --- /dev/null +++ b/input.cpp @@ -0,0 +1,51 @@ +#include + +#include "smpc.h" + +#include "input.hpp" +#include "common/intback.hpp" + +static inline void +input_count(count_flop_t& button, const uint32_t input, const uint32_t mask) +{ + if ((input & mask) == 0) { + if (button.count < input_debounce) + button.count += 1; + else + button.das += 1; + } else { + if (button.count == 0) { + button.flop = 0; + button.das = 0; + button.repeat = 0; + } + else if (button.count > 0) + button.count -= 1; + } +} + +input_t input = { 0 }; + +void digital_callback(uint8_t fsm_state, uint8_t data) +{ + switch (fsm_state) { + case intback::DATA1: + input_count(input.right, data, DIGITAL__1__RIGHT); + input_count(input.left, data, DIGITAL__1__LEFT); + input_count(input.down, data, DIGITAL__1__DOWN); + input_count(input.up, data, DIGITAL__1__UP); + input_count(input.start, data, DIGITAL__1__START); + input_count(input.a, data, DIGITAL__1__A); + input_count(input.c, data, DIGITAL__1__C); + input_count(input.b, data, DIGITAL__1__B); + break; + case intback::DATA2: + input_count(input.r, data, DIGITAL__2__R); + input_count(input.x, data, DIGITAL__2__X); + input_count(input.y, data, DIGITAL__2__Y); + input_count(input.z, data, DIGITAL__2__Z); + input_count(input.l, data, DIGITAL__2__L); + break; + default: break; + } +} diff --git a/input.hpp b/input.hpp new file mode 100644 index 0000000..99d1bc8 --- /dev/null +++ b/input.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include + +struct count_flop_t { + int8_t count; + uint8_t flop; + uint8_t das; + uint8_t repeat; +}; + +struct input_t { + count_flop_t right; + count_flop_t left; + count_flop_t down; + count_flop_t up; + count_flop_t start; + count_flop_t a; + count_flop_t b; + count_flop_t c; + count_flop_t r; + count_flop_t x; + count_flop_t y; + count_flop_t z; + count_flop_t l; +}; + +void digital_callback(uint8_t fsm_state, uint8_t data); + +extern input_t input; + +constexpr int input_arr = 10; +constexpr int input_das = 20; +constexpr int input_debounce = 2; + +static constexpr inline int32_t +input_flopped(count_flop_t& button) +{ + if (button.count == input_debounce && button.flop == 0) { + button.flop = 1; + return 1; + } else if (button.flop == 1 && button.das == input_das && button.repeat == 0) { + button.repeat = 1; + button.das = 0; + return 2; + } else if (button.repeat == 1 && (button.das == input_arr)) { + button.das = 0; + return 2; + } else { + return 0; + } +} + +struct event { + static inline bool cursor_left() { return input_flopped(input.a) == 1; } + static inline bool cursor_right() { return input_flopped(input.b) == 1; } +}; diff --git a/main.cpp b/main.cpp index 586918d..6102a12 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,16 @@ #include #include "vdp2.h" +#include "scu.h" +#include "smpc.h" +#include "sh2.h" #include "common/copy.hpp" #include "common/vdp2_func.hpp" +#include "common/intback.hpp" #include "test.cpp" +#include "input.hpp" constexpr inline uint16_t rgb15(int32_t r, int32_t g, int32_t b) { @@ -72,14 +77,44 @@ constexpr inline void render_block(const uint32_t base_pattern, } } -void render(const uint32_t base_pattern) -{ - const map_t& map = maps[map_t::pallet_town]; +constexpr int32_t last_map = map_t::wardens_house; - for (uint32_t map_y = 0; map_y < map.height; map_y++) { - for (uint32_t map_x = 0; map_x < map.width; map_x++) { +struct state_t { + int32_t map_ix; + enum tileset_t::tileset tileset; + uint32_t base_pattern; +}; + +static state_t state = { 0, tileset_t::cavern, 0 }; + +enum tileset_t::tileset load_tileset(enum tileset_t::tileset tileset) +{ + uint32_t top = (sizeof (union vdp2_vram)); + uint32_t base_address = top = cell_data(tilesets[tileset].tileset, top); + state.base_pattern = base_address / 32; + + /* use 1-word (16-bit) pattern names */ + /* update N0SCN in the event base_pattern moves (it usually does not) */ + vdp2.reg.PNCN0 = PNCN0__N0PNB__1WORD | PNCN0__N0CNSM | PNCN0__N0SCN((state.base_pattern >> 10) & 0x1f); + + return tileset; +} + +#include "map.hpp" + +void render() +{ + const map_t& map = maps[maps_ix[state.map_ix]]; + + if (map.tileset != state.tileset) + state.tileset = load_tileset(map.tileset); + + uint32_t height = map.height > 16 ? 16 : map.height; + uint32_t width = map.width > 16 ? 16 : map.width; + for (uint32_t map_y = 0; map_y < height; map_y++) { + for (uint32_t map_x = 0; map_x < width; map_x++) { const uint8_t block = map.blocks.start[map.width * map_y + map_x]; - render_block(base_pattern, + render_block(state.base_pattern, tilesets[map.tileset], map_x, map_y, @@ -88,6 +123,48 @@ void render(const uint32_t base_pattern) } } +void update() +{ + if (event::cursor_right()) { + state.map_ix++; + if (state.map_ix >= map_ix_last) + state.map_ix = 0; + } + if (event::cursor_left()) { + state.map_ix--; + if (state.map_ix < 0) state.map_ix = last_map; + } +} + +static int count = 0; + +extern "C" +void v_blank_in_int(void) __attribute__ ((interrupt_handler)); +void v_blank_in_int() +{ + if (++count > 60) { + count = 0; + state.map_ix++; + if (state.map_ix >= map_ix_last) + state.map_ix = 0; + } + scu.reg.IST &= ~(IST__V_BLANK_IN); + scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN); + + render(); + update(); +} + +extern "C" +void smpc_int(void) __attribute__ ((interrupt_handler)); +void smpc_int(void) +{ + scu.reg.IST &= ~(IST__SMPC); + scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN); + + intback::fsm(digital_callback, nullptr); +} + void main() { v_blank_in(); @@ -122,28 +199,26 @@ void main() constexpr int page_size = 64 * 64 * 2; // N0PNB__1WORD (16-bit) constexpr int plane_size = page_size * 1; - vdp2.reg.CYCA0 = 0xeeeeeeee; - vdp2.reg.CYCA1 = 0xeeeeeeee; - vdp2.reg.CYCB0 = 0xeeeeeeee; - vdp2.reg.CYCB1 = 0xeeeeeeee; - vdp2.reg.MPOFN = MPOFN__N0MP(0); // bits 8~6 vdp2.reg.MPABN0 = MPABN0__N0MPB(0) | MPABN0__N0MPA(plane_a); // bits 5~0 vdp2.reg.MPCDN0 = MPABN0__N0MPD(0) | MPABN0__N0MPC(0); // bits 5~0 - uint32_t top = (sizeof (union vdp2_vram)); palette_data(); - uint32_t base_address = top = cell_data(tilesets[tileset_t::overworld].tileset, top); - uint32_t base_pattern = base_address / 32; - - /* use 1-word (16-bit) pattern names */ - vdp2.reg.PNCN0 = PNCN0__N0PNB__1WORD | PNCN0__N0CNSM | PNCN0__N0SCN((base_pattern >> 10) & 0x1f); - //vdp2.reg.PNCN0 = PNCN0__N0PNB__2WORD | PNCN0__N0CNSM; - - render(base_pattern); vdp2.reg.CYCA0 = 0x0fff'ffff; vdp2.reg.CYCA1 = 0xffff'ffff; vdp2.reg.CYCB0 = 0xffff'ffff; vdp2.reg.CYCB1 = 0x4fff'ffff; + + // initialize smpc + smpc.reg.DDR1 = 0; // INPUT + smpc.reg.DDR2 = 0; // INPUT + smpc.reg.IOSEL = 0; // SMPC control + smpc.reg.EXLE = 0; // + + sh2_vec[SCU_VEC__SMPC] = (u32)(&smpc_int); + sh2_vec[SCU_VEC__V_BLANK_IN] = (u32)(&v_blank_in_int); + + scu.reg.IST = 0; + scu.reg.IMS = ~(IMS__SMPC | IMS__V_BLANK_IN); } diff --git a/map.hpp b/map.hpp new file mode 100644 index 0000000..2b4fd7f --- /dev/null +++ b/map.hpp @@ -0,0 +1,226 @@ +enum map_t::map maps_ix[] = { + map_t::agathas_room, + map_t::bike_shop, + map_t::bills_house, + map_t::blues_house, + map_t::brunos_room, + map_t::celadon_chief_house, + map_t::celadon_city, + map_t::celadon_diner, + map_t::celadon_gym, + map_t::celadon_hotel, + map_t::celadon_mansion_1f, + map_t::celadon_mansion_2f, + map_t::celadon_mansion_3f, + map_t::celadon_mansion_roof, + map_t::celadon_mansion_roof_house, + map_t::celadon_mart_1f, + map_t::celadon_mart_2f, + map_t::celadon_mart_3f, + map_t::celadon_mart_4f, + map_t::celadon_mart_5f, + map_t::celadon_mart_elevator, + map_t::celadon_mart_roof, + map_t::celadon_pokecenter, + map_t::cerulean_badge_house, + map_t::cerulean_cave_1f, + map_t::cerulean_cave_2f, + map_t::cerulean_cave_b1f, + map_t::cerulean_city, + map_t::cerulean_gym, + map_t::cerulean_mart, + map_t::cerulean_pokecenter, + map_t::cerulean_trade_house, + map_t::cerulean_trashed_house, + map_t::champions_room, + map_t::cinnabar_gym, + map_t::cinnabar_island, + map_t::cinnabar_lab, + map_t::cinnabar_lab_fossil_room, + map_t::cinnabar_lab_metronome_room, + map_t::cinnabar_lab_trade_room, + map_t::cinnabar_mart, + map_t::cinnabar_pokecenter, + map_t::colosseum, + map_t::copycats_house_1f, + map_t::copycats_house_2f, + map_t::daycare, + map_t::digletts_cave, + map_t::digletts_cave_route_11, + map_t::digletts_cave_route_2, + map_t::fighting_dojo, + map_t::fuchsia_bills_grandpas_house, + map_t::fuchsia_city, + map_t::fuchsia_good_rod_house, + map_t::fuchsia_gym, + map_t::fuchsia_mart, + map_t::fuchsia_meeting_room, + map_t::fuchsia_pokecenter, + map_t::game_corner, + map_t::game_corner_prize_room, + map_t::hall_of_fame, + map_t::indigo_plateau, + map_t::indigo_plateau_lobby, + map_t::lances_room, + map_t::lavender_cubone_house, + map_t::lavender_mart, + map_t::lavender_pokecenter, + map_t::lavender_town, + map_t::loreleis_room, + map_t::mr_fujis_house, + map_t::mr_psychics_house, + map_t::mt_moon_1f, + map_t::mt_moon_b1f, + map_t::mt_moon_b2f, + map_t::mt_moon_pokecenter, + map_t::museum_1f, + map_t::museum_2f, + map_t::name_raters_house, + map_t::oaks_lab, + map_t::pallet_town, + map_t::pewter_city, + map_t::pewter_gym, + map_t::pewter_mart, + map_t::pewter_nidoran_house, + map_t::pewter_pokecenter, + map_t::pewter_speech_house, + map_t::pokemon_fan_club, + map_t::pokemon_mansion_1f, + map_t::pokemon_mansion_2f, + map_t::pokemon_mansion_3f, + map_t::pokemon_mansion_b1f, + map_t::pokemon_tower_1f, + map_t::pokemon_tower_2f, + map_t::pokemon_tower_3f, + map_t::pokemon_tower_4f, + map_t::pokemon_tower_5f, + map_t::pokemon_tower_6f, + map_t::pokemon_tower_7f, + map_t::power_plant, + map_t::reds_house_1f, + map_t::reds_house_2f, + map_t::rocket_hideout_b1f, + map_t::rocket_hideout_b2f, + map_t::rocket_hideout_b3f, + map_t::rocket_hideout_b4f, + map_t::rocket_hideout_elevator, + map_t::rock_tunnel_1f, + map_t::rock_tunnel_b1f, + map_t::rock_tunnel_pokecenter, + map_t::route_1, + map_t::route_10, + map_t::route_11, + map_t::route_11_gate_1f, + map_t::route_11_gate_2f, + map_t::route_12, + map_t::route_12_gate_1f, + map_t::route_12_gate_2f, + map_t::route_12_super_rod_house, + map_t::route_13, + map_t::route_14, + map_t::route_15, + map_t::route_15_gate_1f, + map_t::route_15_gate_2f, + map_t::route_16, + map_t::route_16_fly_house, + map_t::route_16_gate_1f, + map_t::route_16_gate_2f, + map_t::route_17, + map_t::route_18, + map_t::route_18_gate_1f, + map_t::route_18_gate_2f, + map_t::route_19, + map_t::route_2, + map_t::route_20, + map_t::route_21, + map_t::route_22, + map_t::route_22_gate, + map_t::route_23, + map_t::route_24, + map_t::route_25, + map_t::route_2_gate, + map_t::route_2_trade_house, + map_t::route_3, + map_t::route_4, + map_t::route_5, + map_t::route_5_gate, + map_t::route_6, + map_t::route_6_gate, + map_t::route_7, + map_t::route_7_gate, + map_t::route_8, + map_t::route_8_gate, + map_t::route_9, + map_t::safari_zone_center, + map_t::safari_zone_center_rest_house, + map_t::safari_zone_east, + map_t::safari_zone_east_rest_house, + map_t::safari_zone_gate, + map_t::safari_zone_north, + map_t::safari_zone_north_rest_house, + map_t::safari_zone_secret_house, + map_t::safari_zone_west, + map_t::safari_zone_west_rest_house, + map_t::saffron_city, + map_t::saffron_gym, + map_t::saffron_mart, + map_t::saffron_pidgey_house, + map_t::saffron_pokecenter, + map_t::seafoam_islands_1f, + map_t::seafoam_islands_b1f, + map_t::seafoam_islands_b2f, + map_t::seafoam_islands_b3f, + map_t::seafoam_islands_b4f, + map_t::silph_co_10f, + map_t::silph_co_11f, + map_t::silph_co_1f, + map_t::silph_co_2f, + map_t::silph_co_3f, + map_t::silph_co_4f, + map_t::silph_co_5f, + map_t::silph_co_6f, + map_t::silph_co_7f, + map_t::silph_co_8f, + map_t::silph_co_9f, + map_t::silph_co_elevator, + map_t::ss_anne_1f, + map_t::ss_anne_1f_rooms, + map_t::ss_anne_2f, + map_t::ss_anne_2f_rooms, + map_t::ss_anne_3f, + map_t::ss_anne_b1f, + map_t::ss_anne_b1f_rooms, + map_t::ss_anne_bow, + map_t::ss_anne_captains_room, + map_t::ss_anne_kitchen, + map_t::trade_center, + map_t::underground_path_north_south, + map_t::underground_path_route_5, + map_t::underground_path_route_6, + map_t::underground_path_route_7, + map_t::underground_path_route_8, + map_t::underground_path_west_east, + map_t::vermilion_city, + map_t::vermilion_dock, + map_t::vermilion_gym, + map_t::vermilion_mart, + map_t::vermilion_old_rod_house, + map_t::vermilion_pidgey_house, + map_t::vermilion_pokecenter, + map_t::vermilion_trade_house, + map_t::victory_road_1f, + map_t::victory_road_2f, + map_t::victory_road_3f, + map_t::viridian_city, + map_t::viridian_forest, + map_t::viridian_forest_north_gate, + map_t::viridian_forest_south_gate, + map_t::viridian_gym, + map_t::viridian_mart, + map_t::viridian_nickname_house, + map_t::viridian_pokecenter, + map_t::viridian_school_house, + map_t::wardens_house, +}; + +constexpr int map_ix_last = (sizeof (maps_ix)) / (sizeof (enum map_t::map)); diff --git a/map_object.hpp b/map_object.hpp new file mode 100644 index 0000000..f40253b --- /dev/null +++ b/map_object.hpp @@ -0,0 +1,60 @@ +struct position_t { + uint8_t x; + uint8_t y; +}; + +struct warp_event_t { + position_t position; + map_t::map destination_map; + uint8_t destination_warp_index; +}; + +struct object_event_t { + enum type { + generic, + item, + trainer, + pokemon, + }; + + enum movement { + stay, + walk, + }; + + enum range { + any_dir, + up_down, + left_right, + }; + + enum direction { + down, + up, + left, + right, + none, + }; + + position_t position; + uint8_t sprite_id; // fixme + enum movement movement; + union { + enum range range; + enum direction direction; + }; + uint8_t text_id; // fixme + union { + struct { + uint8_t id; + } item; + struct { + uint8_t type; // trainer class + uint8_t id; // trainer number + } trainer; + struct { + uint8_t id; // pokemon id + uint8_t level; + } pokemon; + }; +}; diff --git a/tools/parse/__main__.py b/tools/parse/__main__.py new file mode 100644 index 0000000..881f9ca --- /dev/null +++ b/tools/parse/__main__.py @@ -0,0 +1,5 @@ +from pprint import pprint +from parse import parse + +for i in parse.map_objects_list.items(): + pprint(i) diff --git a/tools/parse/map_objects.py b/tools/parse/map_objects.py index b665dc2..e16416d 100644 --- a/tools/parse/map_objects.py +++ b/tools/parse/map_objects.py @@ -5,7 +5,8 @@ from parse.map_header import tokenize_line @dataclass class ObjectEvent: - location: tuple[int, int] + type: str + position: tuple[int, int] sprite_id: str movement: str range_or_direction: str @@ -15,18 +16,18 @@ class ObjectEvent: @dataclass class WarpEvent: - location: tuple[int, int] + position: tuple[int, int] destination_map: str - destination_warp_id: str + destination_warp_index: str @dataclass class BgEvent: - location: tuple[int, int] + position: tuple[int, int] sign_id: str @dataclass class Object: - label: str + border_block: int warp_events: list object_events: list bg_events: list @@ -37,49 +38,71 @@ def tokenize_label(line): def tokenize_event(line): return list(tokenize_line(line)) +def tokenize_border_block(line): + return ('border_block', number.parse(line.strip().split()[1])) + def tokenize_lines(lines): for line in lines: if "_event " in line: yield tokenize_event(line) elif ':' in line: yield tokenize_label(line) + elif 'border block' in line: + # special case where we are parsing a comment + yield tokenize_border_block(line) + +def object_id(object_args): + types = { + 6: "trainer", + 5: "item", + 4: "generic", + } + assert len(object_args) in types, object_args + return types[len(object_args)] def flatten(tokens): label = None + border_block = None warp_events = [] object_events = [] bg_events = [] for token_name, args in tokens: - location = lambda : list(map(number.parse, args[0:2])) + position = lambda : list(map(number.parse, args[0:2])) if token_name == 'label': assert label is None - label = token_name + label = args elif token_name == 'object_event': + object_args = args[2:] event = ObjectEvent( - location(), - *(args[2:]) + object_id(object_args), + position(), + *object_args ) object_events.append(event) elif token_name == 'warp_event': - destination_map, destination_warp_id = args[2:] + destination_map, destination_warp_index = args[2:] event = WarpEvent( - location(), + position(), destination_map, - number.parse(destination_warp_id) + number.parse(destination_warp_index) ) warp_events.append(event) elif token_name == 'bg_event': event = BgEvent( - location(), + position(), *(args[2:]) ) bg_events.append(event) + elif token_name == 'border_block': + assert border_block is None + border_block = args else: assert False, (token_name, args) assert label is not None - return Object( - label=label, + assert border_block is not None + return label, Object( + border_block=border_block, warp_events = warp_events, object_events = object_events, bg_events = bg_events, @@ -93,4 +116,4 @@ def parse(path): def parse_all(prefix): base_path = prefix / 'data/maps/objects' paths = (p for p in base_path.iterdir() if p.is_file()) - return [parse(path) for path in paths] + return dict(parse(path) for path in paths) diff --git a/tools/parse/parse.py b/tools/parse/parse.py index d8e97d6..233e67f 100644 --- a/tools/parse/parse.py +++ b/tools/parse/parse.py @@ -24,3 +24,11 @@ collision_tile_ids_list = collision_tile_ids.parse(prefix) map_objects_list = map_objects.parse_all(prefix) hidden_objects_list = hidden_objects.parse(prefix) map_constants_list = map_constants.parse(prefix) + +# need: +#data/tilesets/pair_collision_tile_ids.asm +#ledge_tiles.asm +#cut_tree_blocks.asm + + +# home/vcopy: animations