nds/arm9/examples/texture.c
2024-09-03 14:00:37 -05:00

273 lines
8.4 KiB
C

#include "io_registers.h"
#include "bits.h"
#include "../res/hotaru_futaba.data.h"
#include "../res/hotaru_futaba.data.pal.h"
static inline uint16_t rgb565(const uint8_t * buf)
{
uint8_t r = buf[0] >> 3;
uint8_t g = buf[1] >> 3;
uint8_t g_l = (buf[1] >> 2) & 1;
uint8_t b = buf[2] >> 3;
return (g_l << 15) | (b << 10) | (g << 5) | (r << 0);
}
void main()
{
// power control
io_registers.a.POWCNT = 0
| POWCNT__lcd_output_destination__a_to_upper__b_to_lower
| POWCNT__geometry_engine__enable
| POWCNT__rendering_engine__enable
| POWCNT__lcd__enable;
// enable bg0 and 3d graphics
io_registers.a.DISPCNT = 0
| DISPCNT__display_mode__graphics_display
| DISPCNT__bg0__enable
| DISPCNT__display_selection_for_bg0__3d_graphics
;
// disable all 3d effects, enable texture mapping
io_registers.a.DISP3DCNT = 0
| DISP3DCNT__clear_image__disable
| DISP3DCNT__fog_master__disable
| DISP3DCNT__edge_marking__disable
| DISP3DCNT__anti_aliasing__disable
| DISP3DCNT__alpha_blending__disable
| DISP3DCNT__alpha_test__disable
| DISP3DCNT__texture_mapping__enable;
// memory bank allocation
// use VRAM-A for texture pixel data
// use VRAM-E for texture palette data
// temporarily map VRAM-A (128KB) to the arm9 address space:
// 0x06800000 - 0x0681FFFF
io_registers.a.VRAMCNT = 0
| VRAMCNT__vram_a__enable
| VRAMCNT__vram_a__mst(0b00); // arm9
// temporarily map VRAM-F (16KB) to the arm9 address space:
// 0x06890000 - 0x06893FFF
io_registers.a.WVRAMCNT = 0
| WVRAMCNT__vram_f__enable
| WVRAMCNT__vram_f__mst(0b000); // arm9
// at this point, VRAM-A/VRAM-E are not accessible by the 3d engine.
// copy palette data
const uint8_t * pal = (const uint8_t *)&_binary_hotaru_futaba_data_pal_start;
volatile uint16_t * vram_f = (volatile uint16_t *)(0x06890000);
for (int i = 0; i < 16; i++) {
// as exported by GIMP, the palette data is three bytes per pixel, packed
// RGB24
vram_f[i] = rgb565(&pal[i * 3]);
}
// copy pixel data
const uint8_t * pixels = (const uint8_t *)&_binary_hotaru_futaba_data_start;
volatile uint16_t * vram_a = (volatile uint16_t *)(0x06800000);
// notice the += 2 increment
for (int i = 0; i < 96 * 64; i += 2) {
// as exported by GIMP, the pixel data is two bytes per pixel.
// [0]: color index (0-15)
// [1]: alpha (0 or 255)
// generate a3i5 pixel data:
// | 15 14 13 | 12 10 09 08 | 07 06 05 | 04 03 02 01 00 |
// | t1 alpha | t1 index | t0 alpha | t0 index |
// process two pixels per loop iteration
uint8_t t0_index = pixels[(i + 0) * 2 + 0];
uint8_t t0_alpha = pixels[(i + 0) * 2 + 1];
uint8_t t1_index = pixels[(i + 1) * 2 + 0];
uint8_t t1_alpha = pixels[(i + 1) * 2 + 1];
// scale alpha from 8-bit to 3-bit
t0_alpha >>= 5;
t1_alpha >>= 5;
// mask index to 4-bit (though the data should already be 16-color, just in case)
t0_index &= 0xf;
t1_index &= 0xf;
// the data is now one byte per a3i5 texel, two a3i5 texels per uint16_t
uint16_t a3i5_texel = (t1_alpha << 13) | (t1_index << 8) | (t0_alpha << 5) | (t0_index << 0);
vram_a[i / 2] = a3i5_texel;
}
// arm9 no longer needs read/write access to texture data, remap vram to the 3d engine
// map VRAM-A (128KB) to the 3d-engine "texture image slot 0":
// 0x00000 - 0x1ffff (3d engine texture image address space)
io_registers.a.VRAMCNT = 0
| VRAMCNT__vram_a__enable
| VRAMCNT__vram_a__ofs(0) // slot 0
| VRAMCNT__vram_a__mst(0b11); // texture image
// map VRAM-F (16KB) to the 3d-engine "texture palette slot 0":
// 0x0000 - 0x3fff (3d engine texture palette address space)
io_registers.a.WVRAMCNT = 0
| WVRAMCNT__vram_f__enable
| WVRAMCNT__vram_f__ofs(0) // slot 0
| WVRAMCNT__vram_f__mst(0b011); // texture palette
// set the texture palette base address
io_registers.a.TEXPLTT_BASE = TEXPLTT_BASE__base_address(0);
io_registers.a.TEXIMAGE_PARAM = 0
| TEXIMAGE_PARAM__texture_coordinate_transformation_mode__texcoord_source
| TEXIMAGE_PARAM__texture_format__a3i5_translucent
| TEXIMAGE_PARAM__t_size__128_texels // actually 96 pixels
| TEXIMAGE_PARAM__s_size__64_texels
| TEXIMAGE_PARAM__texture_starting_address(0);
// the following polygons are fully opaque and are not
// backface-culled
// use
io_registers.a.POLYGON_ATTR = 0
| POLYGON_ATTR__alpha_value(31)
| POLYGON_ATTR__render_front_surface__enable
| POLYGON_ATTR__render_back_surface__enable;
// clear matrix stack status
io_registers.a.GXSTAT |= GXSTAT__matrix_stack_status__overflow_or_underflow;
// load identity matrices
io_registers.a.MTX_MODE = MTX_MODE__matrix_mode__projection;
io_registers.a.MTX_IDENTITY = 0;
// scale the x-axis by the ratio of the display height by the display width.
io_registers.a.MTX_SCALE = (192 << 12) / 256;
io_registers.a.MTX_SCALE = 1 << 12;
io_registers.a.MTX_SCALE = 1 << 12;
io_registers.a.MTX_MODE = MTX_MODE__matrix_mode__position_and_vector;
io_registers.a.MTX_IDENTITY = 0;
io_registers.a.MTX_MODE = MTX_MODE__matrix_mode__position;
io_registers.a.MTX_IDENTITY = 0;
// scale the x-axis by the ratio of the hotaru sprite dimensions
io_registers.a.MTX_SCALE = (64 << 12) / 96;
io_registers.a.MTX_SCALE = 1 << 12;
io_registers.a.MTX_SCALE = 1 << 12;
io_registers.a.MTX_MODE = MTX_MODE__matrix_mode__texture;
io_registers.a.MTX_IDENTITY = 0;
// set the 3d clear color to a dark red
io_registers.a.CLEAR_COLOR = 0
| CLEAR_COLOR__clear_polygon_id(31)
| CLEAR_COLOR__alpha_value(31)
| CLEAR_COLOR__blue(1)
| CLEAR_COLOR__green(1)
| CLEAR_COLOR__red(10);
// set the depth buffer clear value to the maximum value
io_registers.a.CLEAR_DEPTH = CLEAR_DEPTH__value(0x7fff);
// the 3d viewport is the entire display area
io_registers.a.VIEWPORT = 0
| VIEWPORT__y2(191)
| VIEWPORT__x2(255)
| VIEWPORT__y1(0)
| VIEWPORT__x1(0);
// VTX_10 uses signed 4.6 floating point (10 bit)
// | 9 | 8 7 6 | 5 4 3 2 1 0 |
// | s | int | decimal |
int vtx_10_fixed_point = 64; // == 2⁶
// TEXCOORD uses signed 12.4 floating point (16 bit)
int texcoord_fixed_point = 16; // == 2⁴
// quadrilateral; top-left corner is top-left of screen
/*
A--D
| |
| |
B--C
abcd
*/
int ax = -1.0 * vtx_10_fixed_point;
int ay = 1.0 * vtx_10_fixed_point;
int as = 0.0 * texcoord_fixed_point;
int at = 0.0 * texcoord_fixed_point;
int bx = -1.0 * vtx_10_fixed_point;
int by = -1.0 * vtx_10_fixed_point;
int bs = 0.0 * texcoord_fixed_point;
int bt = 95.0 * texcoord_fixed_point;
int cx = 1.0 * vtx_10_fixed_point;
int cy = -1.0 * vtx_10_fixed_point;
int cs = 63.0 * texcoord_fixed_point;
int ct = 95.0 * texcoord_fixed_point;
int dx = 1.0 * vtx_10_fixed_point;
int dy = 1.0 * vtx_10_fixed_point;
int ds = 63.0 * texcoord_fixed_point;
int dt = 0.0 * texcoord_fixed_point;
int z = 1.0 * vtx_10_fixed_point;
while (1) {
io_registers.a.COLOR = 0
| COLOR__blue(31)
| COLOR__green(31)
| COLOR__red(31);
// first quadrilateral: ABCD
io_registers.a.BEGIN_VTXS = BEGIN_VTXS__type__quadrilateral;
io_registers.a.TEXCOORD = 0
| TEXCOORD__t_coordinate(at)
| TEXCOORD__s_coordinate(as);
io_registers.a.VTX_10 = 0
| VTX_10__z_coordinate(z)
| VTX_10__y_coordinate(ay)
| VTX_10__x_coordinate(ax);
io_registers.a.TEXCOORD = 0
| TEXCOORD__t_coordinate(bt)
| TEXCOORD__s_coordinate(bs);
io_registers.a.VTX_10 = 0
| VTX_10__z_coordinate(z)
| VTX_10__y_coordinate(by)
| VTX_10__x_coordinate(bx);
io_registers.a.TEXCOORD = 0
| TEXCOORD__t_coordinate(ct)
| TEXCOORD__s_coordinate(cs);
io_registers.a.VTX_10 = 0
| VTX_10__z_coordinate(z)
| VTX_10__y_coordinate(cy)
| VTX_10__x_coordinate(cx);
io_registers.a.TEXCOORD = 0
| TEXCOORD__t_coordinate(dt)
| TEXCOORD__s_coordinate(ds);
io_registers.a.VTX_10 = 0
| VTX_10__z_coordinate(z)
| VTX_10__y_coordinate(dy)
| VTX_10__x_coordinate(dx);
// end of the ABCD quadrilateral
io_registers.a.END_VTXS = 0;
// wait for the geometry engine
while (io_registers.a.GXSTAT & GXSTAT__geometry_engine_busy);
while (io_registers.a.VCOUNT != 262);
while (io_registers.a.VCOUNT != 0);
// swap buffers
io_registers.a.SWAP_BUFFERS = 0;
}
}