/ software / libsprite / tile.c
tile.c
 1  #include "tile.h"
 2  
 3  #include "pico/platform.h" // for __not_in_flash
 4  #include "hardware/interp.h"
 5  
 6  #define __ram_func(foo) __not_in_flash(#foo) foo
 7  
 8  static inline uint __attribute__((always_inline)) tile_log_size(tilesize_t size) {
 9  	return 3 + (int)size;
10  };
11  
12  static inline void setup_interp_tilemap_ptrs(interp_hw_t *interp, const uint8_t *row, uint x0, uint x_msb) {
13  	// Setup interpolator to add 1 to tile x, mask it with tile x mask, and
14  	// then add to tilemap row base. Since it's a preincrement, we walk the
15  	// initial x back by 1. This isn't a very exciting use of interpolators,
16  	// but it saves ~3 core registers for the pixel loops.
17  	interp_config c = interp_default_config();
18  	interp_config_set_mask(&c, 0, x_msb);
19  	interp_set_config(interp, 0, &c);
20  	interp->accum[0] = x0;
21  	interp->base[0] = 1;
22  	interp->ctrl[1] = 0;
23  	interp->base[2] = (uintptr_t)row;
24  }
25  
26  void __ram_func(tile16)(uint16_t *scanbuf, const tilebg_t *bg, uint raster_y, uint raster_w) {
27  	uint size_x_mask = (1u << bg->log_size_x) - 1;
28  	uint size_y_mask = (1u << bg->log_size_y) - 1;
29  	// Find render start/end point in tile space
30  	// Note tx1 may be "past the end" -- that's fine, it's just used for limits
31  	uint tx0 = bg->xscroll & size_x_mask;
32  	uint tx1 = tx0 + raster_w;
33  	uint ty = (bg->yscroll + raster_y) & size_y_mask;
34  
35  	const uint8_t *tilemap_row_ty = bg->tilemap + (ty >> tile_log_size(bg->tilesize)
36  		<< (bg->log_size_x - tile_log_size(bg->tilesize)));
37  	uint tile_x_at_tx0 = tx0 >> tile_log_size(bg->tilesize);
38  	uint tile_x_msb = bg->log_size_x - tile_log_size(bg->tilesize) - 1;
39  
40  	// NOTE this clobbers interp1, currently this will cause issues if you try
41  	// to run tile code and certain TMDS encode loops on the same core. Could
42  	// be fixed by save/restore, at the cost of some performance.
43  	setup_interp_tilemap_ptrs(interp1_hw, tilemap_row_ty, tile_x_at_tx0, tile_x_msb);
44  
45  	// Apply intra-tile y offset in advance, since this will be the same for
46  	// all pixels of all tiles we render in this call.
47  	uint tilesize = 1u << tile_log_size(bg->tilesize);
48  	const uint16_t *tileset_y_offs = (const uint16_t*)bg->tileset +
49  		(ty & (tilesize - 1)) * tilesize;
50  
51  	tile16_loop_t loop = (tile16_loop_t)bg->fill_loop;
52  	loop(scanbuf, tileset_y_offs, tx0, tx1);
53  }