input: added

This commit is contained in:
Zack Buhman 2023-07-25 00:25:06 +00:00
parent 210e1bd22a
commit c1a96584e4
9 changed files with 542 additions and 37 deletions

View File

@ -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))))

51
input.cpp Normal file
View File

@ -0,0 +1,51 @@
#include <cstdint>
#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;
}
}

57
input.hpp Normal file
View File

@ -0,0 +1,57 @@
#pragma once
#include <stdint.h>
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; }
};

115
main.cpp
View File

@ -1,11 +1,16 @@
#include <cstdint>
#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);
}

226
map.hpp Normal file
View File

@ -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));

60
map_object.hpp Normal file
View File

@ -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;
};
};

5
tools/parse/__main__.py Normal file
View File

@ -0,0 +1,5 @@
from pprint import pprint
from parse import parse
for i in parse.map_objects_list.items():
pprint(i)

View File

@ -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)

View File

@ -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