Add tiles_and_sprites demo, adapted from RISCBoy
This commit is contained in:
parent
6a2ae03021
commit
82cc7ced44
8 changed files with 7591 additions and 2938 deletions
|
|
@ -8,6 +8,7 @@ add_subdirectory(moon)
|
|||
add_subdirectory(sprite_bounce)
|
||||
add_subdirectory(terminal)
|
||||
add_subdirectory(tiles)
|
||||
add_subdirectory(tiles_and_sprites)
|
||||
add_subdirectory(tiles_parallax)
|
||||
add_subdirectory(vista)
|
||||
add_subdirectory(vista-palette)
|
||||
|
|
|
|||
20
software/apps/tiles_and_sprites/CMakeLists.txt
Normal file
20
software/apps/tiles_and_sprites/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
add_executable(tiles_and_sprites
|
||||
main.c
|
||||
)
|
||||
|
||||
target_compile_options(tiles_and_sprites PRIVATE -Wall)
|
||||
|
||||
target_compile_definitions(tiles_and_sprites PRIVATE
|
||||
DVI_DEFAULT_SERIAL_CONFIG=${DVI_DEFAULT_SERIAL_CONFIG}
|
||||
)
|
||||
|
||||
target_link_libraries(tiles_and_sprites
|
||||
pico_stdlib
|
||||
pico_multicore
|
||||
pico_util
|
||||
libdvi
|
||||
libsprite
|
||||
)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(tiles_and_sprites)
|
||||
253
software/apps/tiles_and_sprites/main.c
Normal file
253
software/apps/tiles_and_sprites/main.c
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/multicore.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "hardware/sync.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/vreg.h"
|
||||
#include "pico/sem.h"
|
||||
|
||||
#include "dvi.h"
|
||||
#include "dvi_serialiser.h"
|
||||
#include "common_dvi_pin_configs.h"
|
||||
#include "sprite.h"
|
||||
#include "tile.h"
|
||||
|
||||
#include "tilemap.h"
|
||||
#include "zelda_mini_plus_walk_rgab5515.h"
|
||||
|
||||
// Pick one:
|
||||
#define MODE_640x480_60Hz
|
||||
// #define MODE_800x480_60Hz
|
||||
// #define MODE_800x600_60Hz
|
||||
// #define MODE_960x540p_60Hz
|
||||
// #define MODE_1280x720_30Hz
|
||||
|
||||
#if defined(MODE_640x480_60Hz)
|
||||
// DVDD 1.2V (1.1V seems ok too)
|
||||
#define FRAME_WIDTH 320
|
||||
#define FRAME_HEIGHT 240
|
||||
#define VREG_VSEL VREG_VOLTAGE_1_20
|
||||
#define DVI_TIMING dvi_timing_640x480p_60hz
|
||||
|
||||
#elif defined(MODE_800x480_60Hz)
|
||||
#define FRAME_WIDTH 400
|
||||
#define FRAME_HEIGHT 240
|
||||
#define VREG_VSEL VREG_VOLTAGE_1_20
|
||||
#define DVI_TIMING dvi_timing_800x480p_60hz
|
||||
|
||||
#elif defined(MODE_800x600_60Hz)
|
||||
// DVDD 1.3V, going downhill with a tailwind
|
||||
#define FRAME_WIDTH 400
|
||||
#define FRAME_HEIGHT 300
|
||||
#define VREG_VSEL VREG_VOLTAGE_1_30
|
||||
#define DVI_TIMING dvi_timing_800x600p_60hz
|
||||
|
||||
#elif defined(MODE_960x540p_60Hz)
|
||||
// DVDD 1.25V (slower silicon may need the full 1.3, or just not work)
|
||||
// Frame resolution is almost the same as a PSP :)
|
||||
#define FRAME_WIDTH 480
|
||||
#define FRAME_HEIGHT 270
|
||||
#define VREG_VSEL VREG_VOLTAGE_1_25
|
||||
#define DVI_TIMING dvi_timing_960x540p_60hz
|
||||
|
||||
#elif defined(MODE_1280x720_30Hz)
|
||||
// 1280x720p 30 Hz (nonstandard)
|
||||
// DVDD 1.25V (slower silicon may need the full 1.3, or just not work)
|
||||
#define FRAME_WIDTH 640
|
||||
#define FRAME_HEIGHT 360
|
||||
#define VREG_VSEL VREG_VOLTAGE_1_25
|
||||
#define DVI_TIMING dvi_timing_1280x720p_30hz
|
||||
|
||||
#else
|
||||
#error "Select a video mode!"
|
||||
#endif
|
||||
|
||||
#define LED_PIN 21
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Rendering/animation stuff
|
||||
|
||||
#define MAP_WIDTH 512
|
||||
#define MAP_HEIGHT 256
|
||||
|
||||
#define N_CHARACTERS 70
|
||||
|
||||
typedef struct {
|
||||
int16_t pos_x;
|
||||
int16_t pos_y;
|
||||
uint8_t tile;
|
||||
uint8_t tilestride;
|
||||
uint8_t ntiles;
|
||||
|
||||
int16_t xmin, ymin;
|
||||
int16_t xmax, ymax;
|
||||
uint8_t dir;
|
||||
uint8_t anim_frame;
|
||||
} character_t;
|
||||
|
||||
typedef struct {
|
||||
int16_t cam_x;
|
||||
int16_t cam_y;
|
||||
uint32_t frame_ctr;
|
||||
character_t chars[N_CHARACTERS];
|
||||
} game_state_t;
|
||||
|
||||
static inline int clip(int x, int min, int max) {
|
||||
return x < min ? min : x > max ? max : x;
|
||||
}
|
||||
|
||||
void game_init(game_state_t *state) {
|
||||
state->cam_x = 0;
|
||||
state->cam_y = 0;
|
||||
state->frame_ctr = 0;
|
||||
for (int i = 0; i < N_CHARACTERS; ++i) {
|
||||
state->chars[i].dir = (rand() >> 16) & 0x3;
|
||||
state->chars[i].anim_frame = 0;
|
||||
state->chars[i].xmin = 8;
|
||||
state->chars[i].ymin = -6;
|
||||
state->chars[i].xmax = MAP_WIDTH - 24;
|
||||
state->chars[i].ymax = 128+87;
|
||||
state->chars[i].pos_x = rand() & 0xff;
|
||||
state->chars[i].pos_x = rand() & 0xff;
|
||||
state->chars[i].tile = 102;
|
||||
state->chars[i].tilestride = 17;
|
||||
state->chars[i].ntiles = 2;
|
||||
}
|
||||
}
|
||||
|
||||
void update(game_state_t *state) {
|
||||
static bool cointoss = false;
|
||||
if ((cointoss = !cointoss))
|
||||
return;
|
||||
|
||||
state->frame_ctr++;
|
||||
|
||||
const int CAMERA_SPEED = 3;
|
||||
if (state->frame_ctr % 200 < 50)
|
||||
state->cam_x += CAMERA_SPEED;
|
||||
else if (state->frame_ctr % 200 < 100)
|
||||
state->cam_y += CAMERA_SPEED;
|
||||
else if (state->frame_ctr % 200 < 150)
|
||||
state->cam_x -= CAMERA_SPEED;
|
||||
else
|
||||
state->cam_y -= CAMERA_SPEED;
|
||||
state->cam_x = clip(state->cam_x, 0, MAP_WIDTH - FRAME_WIDTH);
|
||||
state->cam_y = clip(state->cam_y, 0, MAP_HEIGHT - FRAME_HEIGHT);
|
||||
|
||||
const int CHAR_SPEED = 2;
|
||||
for (int i = 0; i < N_CHARACTERS; ++i) {
|
||||
character_t *ch = &state->chars[i];
|
||||
if ((state->frame_ctr & 0x3u) == 0)
|
||||
ch->anim_frame = (ch->anim_frame + 1) & 0x3;
|
||||
if (!(rand() & 0xf00)) {
|
||||
ch->anim_frame = 0;
|
||||
ch->dir = (rand() >> 16) & 0x3;
|
||||
}
|
||||
ch->pos_x += ch->dir == 1 ? CHAR_SPEED : ch->dir == 3 ? -CHAR_SPEED : 0;
|
||||
ch->pos_y += ch->dir == 0 ? CHAR_SPEED : ch->dir == 2 ? -CHAR_SPEED : 0;
|
||||
ch->pos_x = clip(ch->pos_x, ch->xmin, ch->xmax);
|
||||
ch->pos_y = clip(ch->pos_y, ch->ymin, ch->ymax);
|
||||
}
|
||||
|
||||
static uint heartbeat = 0;
|
||||
if (++heartbeat >= 30) {
|
||||
heartbeat = 0;
|
||||
gpio_xor_mask(1u << LED_PIN);
|
||||
}
|
||||
}
|
||||
|
||||
struct dvi_inst dvi0;
|
||||
|
||||
void render(const game_state_t *gstate) {
|
||||
tilebg_t bg = {
|
||||
.xscroll = gstate->cam_x,
|
||||
.yscroll = gstate->cam_y,
|
||||
.tileset = zelda_mini_plus_walk,
|
||||
.tilemap = tilemap,
|
||||
.log_size_x = 9,
|
||||
.log_size_y = 8,
|
||||
.tilesize = TILESIZE_16,
|
||||
.fill_loop = (tile_loop_t)tile16_16px_alpha_loop
|
||||
};
|
||||
|
||||
sprite_t sp = {
|
||||
.log_size = 4,
|
||||
.has_opacity_metadata = false,
|
||||
};
|
||||
|
||||
for (uint y = 0; y < FRAME_HEIGHT; ++y) {
|
||||
uint16_t *pixbuf;
|
||||
queue_remove_blocking(&dvi0.q_colour_free, &pixbuf);
|
||||
|
||||
tile16(pixbuf, &bg, y, FRAME_WIDTH);
|
||||
|
||||
for (int i = 0; i < N_CHARACTERS; ++i) {
|
||||
const character_t *ch = &gstate->chars[i];
|
||||
sp.x = ch->pos_x - gstate->cam_x;
|
||||
const uint16_t *basetile = (const uint16_t*)zelda_mini_plus_walk +
|
||||
16 * 16 * (102 + (ch->dir << 2) + ch->anim_frame);
|
||||
for (int tile = 0; tile < ch->ntiles; ++tile) {
|
||||
sp.y = ch->pos_y - gstate->cam_y + tile * 16;
|
||||
sp.img = basetile + tile * ch->tilestride * 16 * 16;
|
||||
sprite_sprite16(pixbuf, &sp, y, FRAME_WIDTH);
|
||||
}
|
||||
}
|
||||
|
||||
queue_add_blocking(&dvi0.q_colour_valid, &pixbuf);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DVI setup & launch
|
||||
|
||||
#define N_SCANLINE_BUFFERS 4
|
||||
uint16_t static_scanbuf[N_SCANLINE_BUFFERS][FRAME_WIDTH];
|
||||
|
||||
void core1_main() {
|
||||
dvi_register_irqs_this_core(&dvi0, DMA_IRQ_0);
|
||||
while (queue_is_empty(&dvi0.q_colour_valid))
|
||||
__wfe();
|
||||
dvi_start(&dvi0);
|
||||
dvi_scanbuf_main_16bpp(&dvi0);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
int main() {
|
||||
vreg_set_voltage(VREG_VSEL);
|
||||
sleep_ms(10);
|
||||
set_sys_clock_khz(DVI_TIMING.bit_clk_khz, true);
|
||||
|
||||
setup_default_uart();
|
||||
|
||||
gpio_init(LED_PIN);
|
||||
gpio_set_dir(LED_PIN, GPIO_OUT);
|
||||
|
||||
printf("Configuring DVI\n");
|
||||
|
||||
dvi0.timing = &DVI_TIMING;
|
||||
dvi0.ser_cfg = DVI_DEFAULT_SERIAL_CONFIG;
|
||||
dvi_init(&dvi0, next_striped_spin_lock_num(), next_striped_spin_lock_num());
|
||||
|
||||
printf("Core 1 start\n");
|
||||
multicore_launch_core1(core1_main);
|
||||
|
||||
printf("Allocating scanline buffers\n");
|
||||
for (int i = 0; i < N_SCANLINE_BUFFERS; ++i) {
|
||||
void *bufptr = &static_scanbuf[i];
|
||||
queue_add_blocking((void*)&dvi0.q_colour_free, &bufptr);
|
||||
}
|
||||
|
||||
printf("Start rendering\n");
|
||||
game_state_t state;
|
||||
game_init(&state);
|
||||
while (1) {
|
||||
render(&state);
|
||||
update(&state);
|
||||
}
|
||||
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
20
software/apps/tiles_and_sprites/tilemap.h
Normal file
20
software/apps/tiles_and_sprites/tilemap.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#include <stdint.h>
|
||||
|
||||
static const uint8_t __attribute__ ((aligned(4))) tilemap[] = {
|
||||
93,95,95,95,95,95,95,95,95,95,95,95,95,95,95,98,86,97,95,95,95,95,95,95,95,95,95,95,95,95,95,94,
|
||||
96, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,96,
|
||||
96,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,53,54,55,19,96,
|
||||
96,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,45,46,47,18,18,18,18,70,38,72,19,96,
|
||||
96,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,62,63,64,18,18,18,18,87,88,89,19,96,
|
||||
96,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,79,80,81,18,18,18,18,18,18,18,19,96,
|
||||
96,34,35,35, 4, 3, 4, 3,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,36,96,
|
||||
96,37,37,37,34,85,34,36,37,37,37,37,37,37,37,37,37,37,37,37,85,37,37,37,37,37,37,85,37,37,37,96,
|
||||
96,37,37,37,37,37,37,37,37,37,37,37,37,85,37,37,37,37,37,37,37,37,37,85,37,37,37,37,37,37,37,96,
|
||||
96, 5, 6, 7, 5, 6, 7, 5, 6, 7, 5, 6, 7, 5, 6, 7, 5, 6, 7, 5, 6, 7, 5, 6,37,37,37,37,37, 6, 7,96,
|
||||
96,22, 9,24,22,25,24,22, 9,24,22,25,24,22, 9,24,22,25,24,22, 9,24,22,25,24,22, 9,24,22,25,24,96,
|
||||
96,42,43,43,44,40,41,39,40,41,39,40,41,39,40,41,39,40,41,39,40,41,39,40,41,39,40,41,39,40,85,96,
|
||||
96,59,51,52,61, 6, 7, 5, 6, 7, 5, 6, 7, 5, 6, 7,42,43,44, 5, 6, 7, 5, 6, 7, 5, 6, 7, 5, 6, 7,96,
|
||||
96,59,68,69,61, 9,24,22,25,24,22, 9,24,22,25,24,59,71,61,22,25,24,22, 9,24,22,25,24,22, 9,24,96,
|
||||
96,76,77,77,78,40,41,39,40,41,39,40,85,39,40,41,76,77,78,39,40,41,39,40,41,39,40,41,39,40,41,96,
|
||||
90,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,91,
|
||||
};
|
||||
5874
software/assets/oga_zelda_overworld_flat_rgab5515.h
generated
5874
software/assets/oga_zelda_overworld_flat_rgab5515.h
generated
File diff suppressed because it is too large
Load diff
BIN
software/assets/zelda_mini_plus_walk.png
Normal file
BIN
software/assets/zelda_mini_plus_walk.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
4359
software/assets/zelda_mini_plus_walk_rgab5515.h
generated
Normal file
4359
software/assets/zelda_mini_plus_walk_rgab5515.h
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -17,7 +17,7 @@ static inline void setup_interp_tilemap_ptrs(interp_hw_t *interp, const uint8_t
|
|||
interp_config c = interp_default_config();
|
||||
interp_config_set_mask(&c, 0, x_msb);
|
||||
interp_set_config(interp, 0, &c);
|
||||
interp->accum[0] = x0 - 1;
|
||||
interp->accum[0] = x0;
|
||||
interp->base[0] = 1;
|
||||
interp->accum[1] = 0; // necessary if ctrl is 0?
|
||||
interp->base[1] = 0;
|
||||
|
|
|
|||
Loading…
Reference in a new issue