main: rework sprite and tileset loading

All sprites and tilesets can fit at the same time. Load everything all
at once to avoid flickering during drawing.
This commit is contained in:
Zack Buhman 2023-07-25 23:46:44 +00:00
parent 1b71c6cfb6
commit 485b2f8dd5
4 changed files with 207 additions and 59 deletions

158
main.cpp
View File

@ -15,6 +15,8 @@
#include "gen/maps.hpp"
#include "gen/sprites.hpp"
#include "map_objects.hpp"
#include "maps.hpp" // hack?
#include "sprites.hpp"
constexpr inline uint16_t rgb15(int32_t r, int32_t g, int32_t b)
{
@ -100,52 +102,111 @@ constexpr inline void render_block(const uint32_t base_pattern,
constexpr int32_t last_map = map_t::wardens_house;
struct state_t {
int32_t map_ix;
enum tileset_t::tileset tileset;
uint32_t base_pattern;
struct draw_state_t {
struct {
uint16_t tilesets[tilesets_ix_last]; // div 32
uint16_t spritesheets[spritesheets_ix_last]; // div 128
} base_pattern;
};
static state_t state = { 0, tileset_t::cavern, 0 };
struct state_t {
int32_t map_ix;
draw_state_t draw;
};
enum tileset_t::tileset load_tileset(enum tileset_t::tileset tileset)
static state_t state = { 0 };
uint32_t load_tileset(uint32_t top, 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;
state.draw.base_pattern.tilesets[tileset] = 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;
return top;
}
// fixme: remove hack
#include "map.hpp"
uint32_t load_sprite(uint32_t top, enum spritesheet_t::spritesheet spritesheet)
{
const spritesheet_t& s = spritesheets[spritesheet];
uint32_t base_address = top = character_pattern_table(s.spritesheet, top);
state.draw.base_pattern.spritesheets[spritesheet] = base_address / 128;
void render()
return top;
}
void load_vram()
{
vdp2.reg.CYCA0 = 0xeeee'eeee;
vdp2.reg.CYCA1 = 0xeeee'eeee;
vdp2.reg.CYCB0 = 0xeeee'eeee;
vdp2.reg.CYCB1 = 0xeeee'eeee;
uint32_t vdp2_top = (sizeof (union vdp2_vram));
for (uint32_t i = 0; i < tilesets_ix_last; i++)
vdp2_top = load_tileset(vdp2_top, tilesets_ix[i]);
vdp2.reg.CYCA0 = 0x0fff'ffff;
vdp2.reg.CYCA1 = 0xffff'ffff;
vdp2.reg.CYCB0 = 0xffff'ffff;
vdp2.reg.CYCB1 = 0x4fff'ffff;
uint32_t vdp1_top = (sizeof (union vdp1_vram));
for (uint32_t i = 0; i < spritesheets_ix_last; i++)
vdp1_top = load_sprite(vdp1_top, spritesheets_ix[i]);
}
void render_sprites()
{
uint32_t ix = 2;
constexpr uint32_t color_address = 0;
const uint32_t character_address = (state.draw.base_pattern.spritesheets[spritesheet_t::oak] * 128) / 8;
vdp1.vram.cmd[ix].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__NORMAL_SPRITE;
vdp1.vram.cmd[ix].LINK = 0;
// The "end code" is 0xf, which is being used in the mai sprite palette. If
// both transparency and end codes are enabled, it seems there are only 14
// usable colors in the 4-bit color mode.
vdp1.vram.cmd[ix].PMOD = PMOD__ECD | PMOD__COLOR_MODE__COLOR_BANK_16;
// It appears Kronos does not correctly calculate the color address in the
// VDP1 debugger. Kronos will report FFFC when the actual color table address
// in this example is 7FFE0.
vdp1.vram.cmd[ix].COLR = color_address; // non-palettized (rgb15) color data
vdp1.vram.cmd[ix].SRCA = character_address;
vdp1.vram.cmd[ix].SIZE = SIZE__X(16) | SIZE__Y(16);
vdp1.vram.cmd[ix].XA = 5 * 16;
vdp1.vram.cmd[ix].YA = 5 * 16;
ix++;
}
void render_map()
{
const map_t& map = maps[maps_ix[state.map_ix]];
if (map.tileset != state.tileset)
state.tileset = load_tileset(map.tileset);
const uint8_t border_block = map_objects[maps_ix[state.map_ix]].border_block;
const uint32_t base_pattern = state.draw.base_pattern.tilesets[map.tileset];
vdp2.reg.PNCN0 = PNCN0__N0PNB__1WORD | PNCN0__N0CNSM | PNCN0__N0SCN((base_pattern >> 10) & 0x1f);
for (uint32_t map_y = 0; map_y < 16; map_y++) {
for (uint32_t map_x = 0; map_x < 16; map_x++) {
const uint8_t block =
(map_x < map.width && map_y < map.height)
? map.blocks.start[map.width * map_y + map_x]
: border_block;
render_block(state.base_pattern,
? map.blocks.start[map.width * map_y + map_x]
: border_block;
render_block(base_pattern,
tilesets[map.tileset],
map_x,
map_y,
block);
}
}
vdp2.reg.BGON = BGON__N0ON | BGON__N0TPON;
}
void render()
{
render_map();
render_sprites();
}
void update()
@ -206,13 +267,8 @@ void smpc_int(void)
intback::fsm(digital_callback, nullptr);
}
void sprite()
void init_vdp1()
{
uint32_t top = (sizeof (union vdp1_vram));
const spritesheet_t& spritesheet = spritesheets[spritesheet_t::oak];
uint32_t character_address = top = character_pattern_table(spritesheet.spritesheet, top);
uint32_t color_address = 0;
/* TVM settings must be performed from the second H-blank IN interrupt after the
V-blank IN interrupt to the H-blank IN interrupt immediately after the V-blank
OUT interrupt. */
@ -243,35 +299,14 @@ void sprite()
vdp1.vram.cmd[1].XA = 0;
vdp1.vram.cmd[1].YA = 0;
vdp1.vram.cmd[2].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__NORMAL_SPRITE;
vdp1.vram.cmd[2].LINK = 0;
// The "end code" is 0xf, which is being used in the mai sprite palette. If
// both transparency and end codes are enabled, it seems there are only 14
// usable colors in the 4-bit color mode.
vdp1.vram.cmd[2].PMOD = PMOD__ECD | PMOD__COLOR_MODE__COLOR_BANK_16;
// It appears Kronos does not correctly calculate the color address in the
// VDP1 debugger. Kronos will report FFFC when the actual color table address
// in this example is 7FFE0.
vdp1.vram.cmd[2].COLR = color_address; // non-palettized (rgb15) color data
vdp1.vram.cmd[2].SRCA = character_address >> 3;
vdp1.vram.cmd[2].SIZE = SIZE__X(16) | SIZE__Y(16);
vdp1.vram.cmd[2].XA = 5 * 16;
vdp1.vram.cmd[2].YA = 5 * 16;
vdp1.vram.cmd[3].CTRL = CTRL__END;
vdp1.vram.cmd[2].CTRL = CTRL__END;
// start drawing (execute the command list) on every frame
vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE;
}
void main()
void init_vdp2()
{
state.map_ix = map_t::celadon_city;
v_blank_in();
sprite();
vdp2.reg.PRISA = PRISA__S0PRIN(7); // Sprite register 0 PRIority Number
// DISP: Please make sure to change this bit from 0 to 1 during V blank.
@ -281,8 +316,8 @@ void main()
/* set the color mode to 5bits per channel, 1024 colors */
vdp2.reg.RAMCTL = RAMCTL__CRKTE | RAMCTL__CRMD__RGB_5BIT_1024 | RAMCTL__VRAMD | RAMCTL__VRBMD;
/* enable display of NBG0 */
vdp2.reg.BGON = BGON__N0ON | BGON__N0TPON;
/* disable display of NBG0 */
vdp2.reg.BGON = 0;
/* set character format for NBG0 to palettized 16 color
set enable "cell format" for NBG0
@ -309,11 +344,18 @@ void main()
vdp2.reg.MPCDN0 = MPABN0__N0MPD(0) | MPABN0__N0MPC(0); // bits 5~0
palette_data();
}
vdp2.reg.CYCA0 = 0x0fff'ffff;
vdp2.reg.CYCA1 = 0xffff'ffff;
vdp2.reg.CYCB0 = 0xffff'ffff;
vdp2.reg.CYCB1 = 0x4fff'ffff;
void main()
{
state.map_ix = map_t::celadon_city;
load_vram();
v_blank_in();
init_vdp1();
init_vdp2();
// free-running timer
sh2.reg.TCR = TCR__CKS__INTERNAL_DIV128;

View File

@ -224,3 +224,32 @@ enum map_t::map maps_ix[] = {
};
constexpr int map_ix_last = (sizeof (maps_ix)) / (sizeof (enum map_t::map));
enum tileset_t::tileset tilesets_ix[] = {
tileset_t::cavern,
tileset_t::cemetery,
tileset_t::club,
tileset_t::dojo,
tileset_t::facility,
tileset_t::forest,
tileset_t::forest_gate,
tileset_t::gate,
tileset_t::gym,
tileset_t::house,
tileset_t::interior,
tileset_t::lab,
tileset_t::lobby,
tileset_t::mansion,
tileset_t::mart,
tileset_t::museum,
tileset_t::overworld,
tileset_t::plateau,
tileset_t::pokecenter,
tileset_t::reds_house_1,
tileset_t::reds_house_2,
tileset_t::ship,
tileset_t::ship_port,
tileset_t::underground,
};
constexpr int tilesets_ix_last = (sizeof (tilesets_ix)) / (sizeof (enum tileset_t::tileset));

77
sprites.hpp Normal file
View File

@ -0,0 +1,77 @@
enum spritesheet_t::spritesheet spritesheets_ix[] = {
spritesheet_t::agatha,
spritesheet_t::balding_guy,
spritesheet_t::beauty,
spritesheet_t::biker,
spritesheet_t::bike_shop_clerk,
spritesheet_t::bird,
spritesheet_t::blue,
spritesheet_t::boulder,
spritesheet_t::brunette_girl,
spritesheet_t::bruno,
spritesheet_t::captain,
spritesheet_t::channeler,
spritesheet_t::clerk,
spritesheet_t::clipboard,
spritesheet_t::cook,
spritesheet_t::cooltrainer_f,
spritesheet_t::cooltrainer_m,
spritesheet_t::daisy,
spritesheet_t::fairy,
spritesheet_t::fisher,
spritesheet_t::fishing_guru,
spritesheet_t::fossil,
spritesheet_t::gambler,
spritesheet_t::gambler_asleep,
spritesheet_t::gameboy_kid,
spritesheet_t::gentleman,
spritesheet_t::giovanni,
spritesheet_t::girl,
spritesheet_t::gramps,
spritesheet_t::granny,
spritesheet_t::guard,
spritesheet_t::gym_guide,
spritesheet_t::hiker,
spritesheet_t::koga,
spritesheet_t::lance,
spritesheet_t::link_receptionist,
spritesheet_t::little_boy,
spritesheet_t::little_girl,
spritesheet_t::lorelei,
spritesheet_t::middle_aged_man,
spritesheet_t::middle_aged_woman,
spritesheet_t::mom,
spritesheet_t::monster,
spritesheet_t::mr_fuji,
spritesheet_t::none,
spritesheet_t::nurse,
spritesheet_t::oak,
spritesheet_t::old_amber,
spritesheet_t::paper,
spritesheet_t::pokedex,
spritesheet_t::poke_ball,
spritesheet_t::red,
spritesheet_t::rocker,
spritesheet_t::rocket,
spritesheet_t::safari_zone_worker,
spritesheet_t::sailor,
spritesheet_t::scientist,
spritesheet_t::seel,
spritesheet_t::silph_president,
spritesheet_t::silph_worker_f,
spritesheet_t::silph_worker_m,
spritesheet_t::snorlax,
spritesheet_t::super_nerd,
spritesheet_t::swimmer,
spritesheet_t::unused_gambler_asleep_1,
spritesheet_t::unused_gambler_asleep_2,
spritesheet_t::unused_gameboy_kid,
spritesheet_t::unused_guard,
spritesheet_t::unused_old_amber,
spritesheet_t::unused_scientist,
spritesheet_t::waiter,
spritesheet_t::warden,
spritesheet_t::youngster,
};
constexpr int spritesheets_ix_last = (sizeof (spritesheets_ix)) / (sizeof (enum spritesheet_t::spritesheet));

View File

@ -49,7 +49,7 @@ def struct_spritesheet_t():
"start_size_t spritesheet;",
"uint8_t sprite_count;",
"",
"enum sprite {",
"enum spritesheet {",
*sprite_names,
"};",
"};",