diff --git a/imask.h b/imask.h new file mode 100644 index 0000000..620dcc0 --- /dev/null +++ b/imask.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +inline void set_imask(uint32_t imask) +{ + uint32_t sr; + + asm volatile ("stc sr,%0" + : "=r" (sr) + : + ); + + sr = (sr & ~0xf0) | (imask << 4); + + asm volatile ("ldc %0,sr" + : + : "r" (sr) + ); +} diff --git a/main.cpp b/main.cpp index 7071c90..a2af41a 100644 --- a/main.cpp +++ b/main.cpp @@ -11,6 +11,8 @@ #include "systembus.h" #include "maple/maple.h" #include "maple/maple_bits.h" +#include "maple/maple_bus_commands.h" +#include "maple/maple_bus_ft0.h" #include "holly/texture_memory_alloc.h" @@ -47,6 +49,10 @@ inline void serial_char(const char c) // wait for transmit fifo to become empty while ((sh7091.SCIF.SCFSR2 & SCFSR2__TDFE) == 0); + for (int i = 0; i < 100000; i++) { + asm volatile ("nop;"); + } + sh7091.SCIF.SCFTDR2 = static_cast(c); } @@ -60,14 +66,16 @@ void serial_string(const char * s) /* must be aligned to 32-bytes for DMA transfer */ // the aligned(32) attribute does not actually align to 32 bytes; gcc is the best compiler. // `+ 32` to allow for repositioning _scene to an actual 32-byte alignment. -uint32_t __attribute__((aligned(32))) _scene[((32 * 6) + 32) / 4]; +// __attribute__((aligned(32))) +uint32_t _scene[((32 * 6) + 32) / 4]; -uint32_t * align_32byte(uint32_t * mem) +template +T * align_32byte(T * mem) { - return reinterpret_cast(((reinterpret_cast(_scene) + 31) & ~31)); + return reinterpret_cast((((reinterpret_cast(mem) + 31) & ~31))); } -void serial_int(const uint32_t n) +void serial_int32(const uint32_t n) { char num_buf[9]; string::hex(num_buf, 8, n); @@ -77,19 +85,52 @@ void serial_int(const uint32_t n) serial_string("\n"); } -void maple_test() +void serial_int8(const uint8_t n) +{ + char num_buf[3]; + string::hex(num_buf, 2, n); + num_buf[2] = 0; + serial_string("0x"); + serial_string(num_buf); + serial_string("\n"); +} + +uint32_t _receive_address[(32 + 32) / 4] = {0}; +uint32_t _command_buf[(32 + 32) / 4] = {0}; + +bool maple_test() { - uint32_t _command_buf[(32 + 32) / 4]; - uint32_t _receive_address[(32 + 32) / 4]; uint32_t * command_buf = align_32byte(_command_buf); uint32_t * receive_address = align_32byte(_receive_address); + if ((((uint32_t)command_buf) & 31) != 0) serial_string("misaligned\n"); + if ((((uint32_t)receive_address) & 31) != 0) serial_string("misaligned\n"); + + for (int i = 0; i < (32 / 4); i++) { + command_buf[i] = 0; + } + + for (int i = 0; i < (32 / 4); i++) { + receive_address[i] = 0; + } + + v_sync_out(); + + //maple_init_device_request(command_buf, receive_address); + maple_init_get_condition(command_buf, receive_address); - maple_init_host_command(command_buf, receive_address); maple_dma_start(command_buf); - for (int i = 0; i < 32; i++) { - serial_int(receive_address[i]); + v_sync_in(); + + /* + for (int i = 0; i < (4 + 4 + 8); i++) { + serial_int8(reinterpret_cast(receive_address)[i]); } + */ + + // the data format for a FT0 (controller) data read + auto data_format = reinterpret_cast *>(&receive_address[1]); + return !(data_format->data.digital_button & ft0::data_transfer::digital_button::a); } extern "C" @@ -110,8 +151,7 @@ void main() v_sync_in(); - maple_test(); - + /* volatile uint16_t * framebuffer = reinterpret_cast(&texture_memory[0]); for (int y = 0; y < 480; y++) { for (int x = 0; x < 640; x++) { @@ -120,7 +160,9 @@ void main() framebuffer[y * 640 + x] = ((rgb.r >> 3) << 11) | ((rgb.g >> 2) << 5) | ((rgb.b >> 3) << 0); } } + */ + /* volatile texture_memory_alloc * mem = reinterpret_cast(0xa400'0000); volatile uint8_t * macaw = reinterpret_cast(&_binary_macaw_data_start); @@ -133,6 +175,7 @@ void main() uint16_t rgb565 = ((r / 8) << 11) | ((g / 4) << 5) | ((b / 8) << 0); mem->texture[px] = rgb565; } + */ holly.SOFTRESET = softreset::pipeline_soft_reset | softreset::ta_soft_reset; @@ -156,17 +199,22 @@ void main() } int frame = 0; + bool a_pressed = 0; + uint32_t color; while (true) { v_sync_out(); v_sync_in(); ta_polygon_converter_init(); - uint32_t ta_parameter_size = scene_transform(scene); + if (a_pressed) { color = 0xffffffff; } else { color = 0xffff7f00; } + uint32_t ta_parameter_size = scene_transform_quad(scene, color); ta_polygon_converter_transfer(scene, ta_parameter_size); ta_wait_opaque_list(); core_start_render(frame); + a_pressed = maple_test(); + frame = !frame; } } diff --git a/maple/maple.cpp b/maple/maple.cpp index 022d732..7bf3259 100644 --- a/maple/maple.cpp +++ b/maple/maple.cpp @@ -24,20 +24,41 @@ struct maple_host_command { } bus_data; }; -void maple_init_host_command(uint32_t * buf, uint32_t * receive_address) +void maple_init_host_command(uint32_t * buf, uint32_t * receive_address, uint8_t command_code, uint8_t data_size) { - auto host_command = reinterpret_cast *>(buf); + // this function does not care about the template instantiation of + // maple_host_command--data_fields is not manipulated here. + auto host_command = reinterpret_cast *>(buf); host_command->host_instruction = host_instruction::end_flag | host_instruction::port_select::a - | host_instruction::transfer_length(0); // 4 bytes + | host_instruction::transfer_length((data_size / 4)); - host_command->receive_data_storage_address = reinterpret_cast(receive_address); + host_command->receive_data_storage_address = reinterpret_cast(receive_address) & 0x1fff'ffff; - host_command->bus_data.command_code = device_request::command_code; + host_command->bus_data.command_code = command_code; host_command->bus_data.destination_ap = ap::de::device | ap::port_select::a; host_command->bus_data.source_ap = ap::port_select::a; - host_command->bus_data.data_size = 0; + host_command->bus_data.data_size = data_size / 4; +} + +void maple_init_device_request(uint32_t * buf, uint32_t * receive_address) +{ + maple_init_host_command(buf, receive_address, device_request::command_code, (sizeof (struct device_request::data_fields))); +} + +void maple_init_get_condition(uint32_t * buf, uint32_t * receive_address) +{ + maple_init_host_command(buf, receive_address, get_condition::command_code, (sizeof (struct get_condition::data_fields))); + + auto host_command = reinterpret_cast *>(buf); + + auto& function_type = host_command->bus_data.data_fields.function_type; + // controller function type + function_type[0] = 0x00; + function_type[1] = 0x00; + function_type[2] = 0x00; + function_type[3] = 0x01; } void maple_dma_start(uint32_t * command_buf) @@ -52,29 +73,28 @@ void maple_dma_start(uint32_t * command_buf) // disable maple-DMA maple_if.MDEN = mden::dma_enable::abort; - volatile uint32_t _dummy = maple_if.MDST; - (void)_dummy; + while (mdst::start_status::status(maple_if.MDST) != 0); // 20nsec * 0xc350 = 1ms constexpr uint32_t one_msec = 0xc350; maple_if.MSYS = msys::time_out_counter(one_msec) | msys::sending_rate::_2M; - maple_if.MDTSEL = mdtsel::trigger_select::software_initiation; - /* top address: the first/lowest address bottom address: the last/highest address */ maple_if.MDAPRO = mdapro::security_code | mdapro::top_address(0x00) | mdapro::bottom_address(0x7f); + maple_if.MDTSEL = mdtsel::trigger_select::software_initiation; + maple_if.MDSTAR = mdstar::table_address(reinterpret_cast(command_buf)); maple_if.MDEN = mden::dma_enable::enable; maple_if.MDST = mdst::start_status::start; // wait for completion + //while (mdst::start_status::status(maple_if.MDST) != 0); while ((system.ISTNRM & ISTNRM__END_OF_DMA_MAPLE_DMA) == 0); - system.ISTNRM = ISTNRM__END_OF_DMA_MAPLE_DMA; } diff --git a/maple/maple.h b/maple/maple.h index a0cd7d4..f756759 100644 --- a/maple/maple.h +++ b/maple/maple.h @@ -3,4 +3,6 @@ #include void maple_init_host_command(uint32_t * buf, uint32_t * receive_address); +void maple_init_device_request(uint32_t * buf, uint32_t * receive_address); +void maple_init_get_condition(uint32_t * buf, uint32_t * receive_address); void maple_dma_start(uint32_t * command_buf); diff --git a/maple/maple_bus_commands.h b/maple/maple_bus_commands.h index 259b86c..68b90fc 100644 --- a/maple/maple_bus_commands.h +++ b/maple/maple_bus_commands.h @@ -47,7 +47,7 @@ namespace device_status { namespace device_all_status { constexpr uint32_t command_code = 0x6; - template + template struct data_fields { uint8_t device_id[16]; uint8_t destination_code[1]; @@ -56,10 +56,10 @@ namespace device_all_status { uint8_t license[60]; uint8_t low_consumption_standby_current[2]; uint8_t maximum_current_consumption[2]; - uint8_t free_device_status[N]; + T free_device_status; }; - static_assert((sizeof (struct data_fields<0>)) == 112); + static_assert((sizeof (struct data_fields)) == 112); } namespace device_reply { @@ -72,12 +72,13 @@ namespace device_reply { namespace data_transfer { constexpr uint32_t command_code = 0x8; - template + template struct data_fields { - uint8_t data[N]; + uint8_t function_type[4]; + T data; }; - static_assert((sizeof (struct data_fields<0>)) == 0); + static_assert((sizeof (struct data_fields)) == 4); } namespace get_condition { @@ -117,16 +118,16 @@ namespace block_read { namespace block_write { constexpr uint32_t command_code = 0xc; - template + template struct data_fields { uint8_t function_type[4]; uint8_t pt[1]; uint8_t phase[1]; uint8_t block_no[2]; - uint8_t written_data[N]; + T written_data; }; - static_assert((sizeof (struct data_fields<0>)) == 8); + static_assert((sizeof (struct data_fields)) == 8); } namespace get_last_error { @@ -145,37 +146,37 @@ namespace get_last_error { namespace set_condition { constexpr uint32_t command_code = 0xe; - template + template struct data_fields { uint8_t function_type[4]; - uint8_t write_in_data[N]; + T write_in_data; }; - static_assert((sizeof (struct data_fields<0>)) == 4); + static_assert((sizeof (struct data_fields)) == 4); } namespace ft4_control { constexpr uint32_t command_code = 0xf; - template + template struct data_fields { uint8_t function_type[4]; - uint8_t ft4_data[N]; + T ft4_data; }; - static_assert((sizeof (struct data_fields<0>)) == 4); + static_assert((sizeof (struct data_fields)) == 4); } namespace ar_control { constexpr uint32_t command_code = 0x10; - template + template struct data_fields { uint8_t function_type[4]; - uint8_t data[N]; + T data; }; - static_assert((sizeof (struct data_fields<0>)) == 4); + static_assert((sizeof (struct data_fields)) == 4); } namespace function_type_unknown { diff --git a/maple/maple_bus_ft0.h b/maple/maple_bus_ft0.h new file mode 100644 index 0000000..3c37d7a --- /dev/null +++ b/maple/maple_bus_ft0.h @@ -0,0 +1,34 @@ +namespace ft0 { + namespace data_transfer { + namespace digital_button { + constexpr uint32_t ra = 1 << 7; + constexpr uint32_t la = 1 << 6; + constexpr uint32_t da = 1 << 5; + constexpr uint32_t ua = 1 << 4; + constexpr uint32_t start = 1 << 3; + constexpr uint32_t a = 1 << 2; + constexpr uint32_t b = 1 << 1; + constexpr uint32_t c = 1 << 0; + constexpr uint32_t rb = 1 << 15; + constexpr uint32_t lb = 1 << 14; + constexpr uint32_t db = 1 << 13; + constexpr uint32_t ub = 1 << 12; + constexpr uint32_t d = 1 << 11; + constexpr uint32_t x = 1 << 10; + constexpr uint32_t y = 1 << 9; + constexpr uint32_t z = 1 << 8; + } + + struct data_format { + uint16_t digital_button; + uint8_t analog_axis_1; + uint8_t analog_axis_2; + uint8_t analog_axis_3; + uint8_t analog_axis_4; + uint8_t analog_axis_5; + uint8_t analog_axis_6; + }; + static_assert((sizeof (struct data_format)) == 8); + } +} + diff --git a/maple_bus_commands.h b/maple_bus_commands.h deleted file mode 100644 index 18c41cc..0000000 --- a/maple_bus_commands.h +++ /dev/null @@ -1,225 +0,0 @@ -#include -#include - -#include "type.h" - -namespace device_request { - constexpr uint32_t command_code = 0x1; - struct data_fields { - }; -} - -namespace all_status_request { - constexpr uint32_t command_code = 0x2; - struct data_fields { - }; -} - -namespace device_reset { - constexpr uint32_t command_code = 0x3; - struct data_fields { - }; -} - -namespace device_kill { - constexpr uint32_t command_code = 0x4; - struct data_fields { - }; -} - -namespace device_status { - constexpr uint32_t command_code = 0x5; - struct data_fields { - - uint8_t device_id[16]; - uint8_t destination_code[1]; - uint8_t connection_direction[1]; - uint8_t product_name[30]; - uint8_t license[60]; - uint8_t low_consumption_standby_current[2]; - uint8_t maximum_current_consumption[2]; - }; - - static_assert((sizeof (struct data_fields)) == 112); -} - -namespace device_all_status { - constexpr uint32_t command_code = 0x6; - struct data_fields { - - template - uint8_t device_id[16]; - uint8_t destination_code[1]; - uint8_t connection_direction[1]; - uint8_t product_name[30]; - uint8_t license[60]; - uint8_t low_consumption_standby_current[2]; - uint8_t maximum_current_consumption[2]; - uint8_t free_device_status[N]; - }; - - static_assert((sizeof (struct data_fields<0>)) == 112); -} - -namespace device_reply { - constexpr uint32_t command_code = 0x7; - struct data_fields { - }; -} - -namespace data_transfer { - constexpr uint32_t command_code = 0x8; - struct data_fields { - - template - uint8_t data[N]; - }; - - static_assert((sizeof (struct data_fields<0>)) == 0); -} - -namespace get_condition { - constexpr uint32_t command_code = 0x9; - struct data_fields { - - uint8_t function_type[4]; - }; - - static_assert((sizeof (struct data_fields)) == 4); -} - -namespace get_media_info { - constexpr uint32_t command_code = 0xa; - struct data_fields { - - uint8_t function_type[4]; - uint8_t pt[4]; - }; - - static_assert((sizeof (struct data_fields)) == 8); -} - -namespace block_read { - constexpr uint32_t command_code = 0xb; - struct data_fields { - - uint8_t function_type[4]; - uint8_t pt[1]; - uint8_t phase[1]; - uint8_t block_no[2]; - }; - - static_assert((sizeof (struct data_fields)) == 8); -} - -namespace block_write { - constexpr uint32_t command_code = 0xc; - struct data_fields { - - template - uint8_t function_type[4]; - uint8_t pt[1]; - uint8_t phase[1]; - uint8_t block_no[2]; - uint8_t written_data[N]; - }; - - static_assert((sizeof (struct data_fields<0>)) == 8); -} - -namespace get_last_error { - constexpr uint32_t command_code = 0xd; - struct data_fields { - - uint8_t function_type[4]; - uint8_t pt[1]; - uint8_t phase[1]; - uint8_t block_no[2]; - }; - - static_assert((sizeof (struct data_fields)) == 8); -} - -namespace set_condition { - constexpr uint32_t command_code = 0xe; - struct data_fields { - - template - uint8_t function_type[4]; - uint8_t write_in_data[N]; - }; - - static_assert((sizeof (struct data_fields<0>)) == 4); -} - -namespace ft4_control { - constexpr uint32_t command_code = 0xf; - struct data_fields { - - template - uint8_t function_type[4]; - uint8_t ft4_data[N]; - }; - - static_assert((sizeof (struct data_fields<0>)) == 4); -} - -namespace ar_control { - constexpr uint32_t command_code = 0x10; - struct data_fields { - - template - uint8_t function_type[4]; - uint8_t data[N]; - }; - - static_assert((sizeof (struct data_fields<0>)) == 4); -} - -namespace function_type_unknown { - constexpr uint32_t command_code = 0xfe; - struct data_fields { - }; -} - -namespace command_unknown { - constexpr uint32_t command_code = 0xfd; - struct data_fields { - }; -} - -namespace transmit_again { - constexpr uint32_t command_code = 0xfc; - struct data_fields { - }; -} - -namespace file_error { - constexpr uint32_t command_code = 0xfb; - struct data_fields { - - uint8_t function_error_code[4]; - }; - - static_assert((sizeof (struct data_fields)) == 4); -} - -namespace lcd_error { - constexpr uint32_t command_code = 0xfa; - struct data_fields { - - uint8_t function_error_code[4]; - }; - - static_assert((sizeof (struct data_fields)) == 4); -} - -namespace ar_error { - constexpr uint32_t command_code = 0xf9; - struct data_fields { - - uint8_t function_error_code[4]; - }; - - static_assert((sizeof (struct data_fields)) == 4); -} diff --git a/regs/gen/maple_commands.py b/regs/gen/maple_commands.py index 30dd322..6429bb1 100644 --- a/regs/gen/maple_commands.py +++ b/regs/gen/maple_commands.py @@ -27,7 +27,7 @@ def command_namespace(namespace: CommandNamespace, if variable is not None: assert variable.lower() == "n" - yield "template " + yield "template " yield "struct data_fields {" @@ -37,7 +37,7 @@ def command_namespace(namespace: CommandNamespace, yield f"uint8_t {field_name}[{const}];" elif const == 0: assert var == "n" - yield f"uint8_t {field_name}[{var.upper()}];" + yield f"T {field_name};" else: yield f"uint8_t {field_name}[{const} + {var.upper()}];" @@ -47,7 +47,7 @@ def command_namespace(namespace: CommandNamespace, if variable is not None: assert variable == "n" - yield f"static_assert((sizeof (struct data_fields<0>)) == {length});" + yield f"static_assert((sizeof (struct data_fields)) == {length});" else: yield f"static_assert((sizeof (struct data_fields)) == {length});" diff --git a/regs/gen/maple_data_format.py b/regs/gen/maple_data_format.py new file mode 100644 index 0000000..116c713 --- /dev/null +++ b/regs/gen/maple_data_format.py @@ -0,0 +1,124 @@ +import csv +import sys +from dataclasses import dataclass +from collections import defaultdict + +from generate import renderer + +@dataclass +class Field: + name: str + bits: list[str] + +@dataclass +class Format: + name: str + fields: list[Field] + field_order: list[str] + size: int + +def read_input(filename): + with open(filename) as f: + reader = csv.reader(f, delimiter=",", quotechar='"') + rows = [ + [s.strip() for s in row] + for row in reader + ] + return rows + +def parse_data_format(ix, rows): + if ix >= len(rows): + return None + + while rows[ix][0] == "": + ix += 1 + if ix >= len(rows): + return None + + format_name, *header = rows[ix] + ix += 1 + assert format_name != "" + assert header == ["7", "6", "5", "4", "3", "2", "1", "0"] + + fields = defaultdict(list) + field_order = list() + size = 0 + while ix < len(rows) and rows[ix][0] != "": + field_name, *_bits = rows[ix] + ix += 1 + excess_bits = [b for b in _bits[8:] if b != ""] + assert excess_bits == [] + bits = [b for b in _bits[:8] if b != ""] + assert len(bits) in {0, 8}, bits + fields[field_name].append(Field(field_name, list(bits))) + size += 1 + if field_name not in field_order: + field_order.append(field_name) + + return ix, Format(format_name, dict(fields), field_order, size) + +def parse(rows): + ix = 0 + formats = [] + + while True: + ix_format = parse_data_format(ix, rows) + if ix_format is None: + break + ix, format = ix_format + formats.append(format) + + assert len(formats) > 0 + return formats + +bit_order = [7, 6, 5, 4, 3, 2, 1, 0] + +def render_format(format): + yield f"namespace {format.name} {{" + for field_name in format.field_order: + subfields = format.fields[field_name] + if not any(field.bits != [] for field in subfields): + continue + + yield f"namespace {field_name} {{" + for ix, field in enumerate(subfields): + bit_offset = 8 * ix + if field.bits != []: + assert len(field.bits) == 8 + for byte_ix, bit in zip(bit_order, field.bits): + bit_ix = byte_ix + bit_offset + yield f"constexpr uint32_t {bit.lower()} = 1 << {bit_ix};" + yield "}" + yield "" + + yield f"struct data_format {{" + + for field_name in format.field_order: + subfields = format.fields[field_name] + if len(subfields) == 1: + field, = subfields + yield f"uint8_t {field_name};" + elif len(subfields) == 2: + yield f"uint16_t {field_name};" + else: + assert False, len(subfields) + + yield "};" + assert format.size % 4 == 0, format.size + yield f"static_assert((sizeof (struct data_format)) == {format.size});" + yield "}" + +def render_formats(name, formats): + yield f"namespace {name} {{" + for format in formats: + yield from render_format(format) + yield "}" + +if __name__ == "__main__": + rows = read_input(sys.argv[1]) + name = sys.argv[1].split('.')[0].split('_')[-1] + assert len(name) == 3 or len(name) == 4 + formats = parse(rows) + render, out = renderer() + render(render_formats(name, formats)) + print(out.getvalue()) diff --git a/regs/maple_bus_commands.csv b/regs/maple_bus_commands.csv index e3700de..a065431 100644 --- a/regs/maple_bus_commands.csv +++ b/regs/maple_bus_commands.csv @@ -26,7 +26,8 @@ ,,,,, "device_reply","peripheral","0x07","0x00",, ,,,,, -"data_transfer","peripheral","0x08","n/4","data","n" +"data_transfer","peripheral","0x08","0x01+n/4","function_type",4 +"data_transfer",,,,"data","n" ,,,,, "get_condition","host","0x09","0x01","function_type",4 ,,,,, diff --git a/regs/maple_bus_commands.ods b/regs/maple_bus_commands.ods index abd09bd..1b56cf8 100644 Binary files a/regs/maple_bus_commands.ods and b/regs/maple_bus_commands.ods differ diff --git a/regs/maple_bus_ft0.csv b/regs/maple_bus_ft0.csv new file mode 100644 index 0000000..a081908 --- /dev/null +++ b/regs/maple_bus_ft0.csv @@ -0,0 +1,9 @@ +"data_transfer",7,6,5,4,3,2,1,0 +"digital_button","Ra","La","Da","Ua","Start","A","B","C" +"digital_button","Rb","Lb","Db","Ub","D","X","Y","Z" +"analog_axis_1",,,,,,,, +"analog_axis_2",,,,,,,, +"analog_axis_3",,,,,,,, +"analog_axis_4",,,,,,,, +"analog_axis_5",,,,,,,, +"analog_axis_6",,,,,,,, diff --git a/regs/maple_bus_ft0.ods b/regs/maple_bus_ft0.ods index 55a72af..c9d3363 100644 Binary files a/regs/maple_bus_ft0.ods and b/regs/maple_bus_ft0.ods differ diff --git a/scene.cpp b/scene.cpp index 5212a62..4896bad 100644 --- a/scene.cpp +++ b/scene.cpp @@ -49,11 +49,11 @@ struct scene_quad_ta_parameters { static_assert((sizeof (scene_quad_ta_parameters)) == 32 * 4); -uint32_t scene_transform_quad(uint32_t * _scene) +uint32_t scene_transform_quad(uint32_t * _scene, uint32_t base_color) { auto scene = reinterpret_cast(&_scene[0]); - uint32_t base_color = 0xffffff00; + //uint32_t base_color = 0xffffff00; scene->sprite = global_sprite(base_color); scene->vertex = vertex_sprite_type_0(scene_quad[0].x * 240 + 320, scene_quad[0].y * 240 + 240, diff --git a/scene.h b/scene.h index 0bade7f..9a6eade 100644 --- a/scene.h +++ b/scene.h @@ -2,6 +2,6 @@ #include -uint32_t scene_transform_quad(uint32_t * scene); +uint32_t scene_transform_quad(uint32_t * scene, uint32_t color); uint32_t scene_transform(uint32_t * scene);