maple: read controller input on real hardware

There were two notable bugs:

- the maple transfer/data sizes were not being set correctly

- align_32byte always realigned the address of `_scene`, and not the
  `mem` parameter as expected. This had the effect of the maple-DMA
  send and receive buffers being the same buffer. On real hardware,
  this causes unpredicable behavior.
This commit is contained in:
Zack Buhman 2023-12-10 00:41:16 +08:00
parent ca77db1734
commit 25fef821b0
15 changed files with 308 additions and 274 deletions

20
imask.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <cstdint>
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)
);
}

View File

@ -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<uint8_t>(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 <typename T>
T * align_32byte(T * mem)
{
return reinterpret_cast<uint32_t *>(((reinterpret_cast<uint32_t>(_scene) + 31) & ~31));
return reinterpret_cast<T *>((((reinterpret_cast<uint32_t>(mem) + 31) & ~31)));
}
void serial_int(const uint32_t n)
void serial_int32(const uint32_t n)
{
char num_buf[9];
string::hex<char>(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<char>(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<volatile uint8_t *>(receive_address)[i]);
}
*/
// the data format for a FT0 (controller) data read
auto data_format = reinterpret_cast<volatile data_transfer::data_fields<ft0::data_transfer::data_format> *>(&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<volatile uint16_t *>(&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<volatile texture_memory_alloc *>(0xa400'0000);
volatile uint8_t * macaw = reinterpret_cast<volatile uint8_t *>(&_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;
}
}

View File

@ -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<maple_host_command<device_request::data_fields> *>(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<maple_host_command<uint32_t> *>(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<uint32_t>(receive_address);
host_command->receive_data_storage_address = reinterpret_cast<uint32_t>(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<maple_host_command<get_condition::data_fields> *>(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<uint32_t>(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;
}

View File

@ -3,4 +3,6 @@
#include <cstdint>
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);

View File

@ -47,7 +47,7 @@ namespace device_status {
namespace device_all_status {
constexpr uint32_t command_code = 0x6;
template <int N>
template <typename T>
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<char[0]>)) == 112);
}
namespace device_reply {
@ -72,12 +72,13 @@ namespace device_reply {
namespace data_transfer {
constexpr uint32_t command_code = 0x8;
template <int N>
template <typename T>
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<char[0]>)) == 4);
}
namespace get_condition {
@ -117,16 +118,16 @@ namespace block_read {
namespace block_write {
constexpr uint32_t command_code = 0xc;
template <int N>
template <typename T>
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<char[0]>)) == 8);
}
namespace get_last_error {
@ -145,37 +146,37 @@ namespace get_last_error {
namespace set_condition {
constexpr uint32_t command_code = 0xe;
template <int N>
template <typename T>
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<char[0]>)) == 4);
}
namespace ft4_control {
constexpr uint32_t command_code = 0xf;
template <int N>
template <typename T>
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<char[0]>)) == 4);
}
namespace ar_control {
constexpr uint32_t command_code = 0x10;
template <int N>
template <typename T>
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<char[0]>)) == 4);
}
namespace function_type_unknown {

34
maple/maple_bus_ft0.h Normal file
View File

@ -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);
}
}

View File

@ -1,225 +0,0 @@
#include <stdint.h>
#include <stddef.h>
#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 <int N>
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 <int N>
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 <int N>
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 <int N>
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 <int N>
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 <int N>
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);
}

View File

@ -27,7 +27,7 @@ def command_namespace(namespace: CommandNamespace,
if variable is not None:
assert variable.lower() == "n"
yield "template <int N>"
yield "template <typename T>"
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<char[0]>)) == {length});"
else:
yield f"static_assert((sizeof (struct data_fields)) == {length});"

View File

@ -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())

View File

@ -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
,,,,,

1 name issuing_right command_code data_size data_field data_field_size
26
27 device_reply peripheral 0x07 0x00
28
29 data_transfer peripheral 0x08 n/4 0x01+n/4 data function_type n 4
30 data_transfer data n
31
32 get_condition host 0x09 0x01 function_type 4
33

Binary file not shown.

9
regs/maple_bus_ft0.csv Normal file
View File

@ -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",,,,,,,,
1 data_transfer 7 6 5 4 3 2 1 0
2 digital_button Ra La Da Ua Start A B C
3 digital_button Rb Lb Db Ub D X Y Z
4 analog_axis_1
5 analog_axis_2
6 analog_axis_3
7 analog_axis_4
8 analog_axis_5
9 analog_axis_6

Binary file not shown.

View File

@ -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_quad_ta_parameters *>(&_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,

View File

@ -2,6 +2,6 @@
#include <cstdint>
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);