dreamcast/example/maple_vibrator.cpp
Zack Buhman 511d99563d maple_bus_commands: zero-sized structs should be zero sized
From the GCC manual.

> GCC permits a C structure to have no members:

struct empty {
};

> The structure has size zero. In C++, empty structures are part of the
> language. G++ treats empty structures as if they had a single member of type
> char.

I was not aware of the different behavior in C++.

This fixes every maple example--most were broken for multiple reasons, including
this one.

This also enables SH4 caching. This includes linking code/data into the P1
area (previously this was not the case).

The maple examples (which indeed involve much use of DMA) require much work to
successfully work with the operand and copyback caches. The vibration example
currently is the most complete, though I should consider more on how I want to
structure maple response operand cache invalidation more generally.
2024-02-02 22:05:10 +08:00

216 lines
7.3 KiB
C++

#include <bit>
#include "vga.hpp"
#include "align.hpp"
#include "maple/maple.hpp"
#include "maple/maple_impl.hpp"
#include "maple/maple_bus_bits.hpp"
#include "maple/maple_bus_commands.hpp"
#include "maple/maple_bus_ft8.hpp"
#include "sh7091/serial.hpp"
static uint32_t * command_buf;
static uint32_t * receive_buf;
void do_lm_request(uint8_t port, uint8_t lm)
{
uint32_t destination_port;
uint32_t destination_ap;
switch (port) {
case 0:
destination_port = host_instruction::port_select::a;
destination_ap = ap::de::expansion_device | ap::port_select::a | lm;
break;
case 1:
destination_port = host_instruction::port_select::b;
destination_ap = ap::de::expansion_device | ap::port_select::b | lm;
break;
case 2:
destination_port = host_instruction::port_select::c;
destination_ap = ap::de::expansion_device | ap::port_select::c | lm;
break;
case 3:
destination_port = host_instruction::port_select::d;
destination_ap = ap::de::expansion_device | ap::port_select::d | lm;
break;
default:
return;
}
/*
get media info
*/
maple::init_host_command(command_buf, receive_buf,
destination_port,
destination_ap, get_media_info::command_code, (sizeof (struct get_media_info::data_fields)),
true);
using command_type = struct maple::host_command<get_media_info::data_fields>;
auto host_command = reinterpret_cast<command_type *>(command_buf);
auto& fields = host_command->bus_data.data_fields;
fields.function_type = std::byteswap(function_type::vibration);
fields.pt = std::byteswap(1 << 24);
serial::string("dma start\n");
const uint32_t size = (reinterpret_cast<uint32_t>(&host_command[1]) - reinterpret_cast<uint32_t>(&host_command[0]));
maple::dma_start(command_buf, size * 2);
using response_type = data_transfer<ft8::data_transfer::data_format>;
using command_response_type = struct maple::command_response<response_type::data_fields>;
for (uint32_t i = 0; i < (sizeof (command_response_type)) / 32; i++) {
asm volatile ("ocbp @%0"
: // output
: "r" (reinterpret_cast<uint32_t>(&receive_buf[(32 * i) / 4])) // input
);
}
auto response = reinterpret_cast<command_response_type *>(receive_buf);
auto& bus_data = response->bus_data;
if (bus_data.command_code != response_type::command_code) {
serial::string("lm did not reply to vibration get_media_info: ");
serial::integer<uint8_t>(lm);
return;
}
serial::string("lm replied to vibration get_media_info: ");
serial::integer<uint8_t>(lm);
auto& data_fields = bus_data.data_fields;
{
using namespace ft8::data_transfer::vset;
serial::string("vn: ");
serial::integer<uint8_t>(vn(data_fields.data.vset));
serial::string("vp: ");
serial::integer<uint8_t>(vp(data_fields.data.vset));
serial::string("vd: ");
serial::integer<uint8_t>(vd(data_fields.data.vset));
serial::string("pf: ");
serial::integer<uint8_t>(pf(data_fields.data.vset));
serial::string("cv: ");
serial::integer<uint8_t>(cv(data_fields.data.vset));
serial::string("pd: ");
serial::integer<uint8_t>(pd(data_fields.data.vset));
serial::string("owf: ");
serial::integer<uint8_t>(owf(data_fields.data.vset));
serial::string("va: ");
serial::integer<uint8_t>(va(data_fields.data.vset));
serial::string("\nfm0 (fmin): ");
serial::integer<uint8_t>(data_fields.data.fm0);
serial::string("fm1 (fmax): ");
serial::integer<uint8_t>(data_fields.data.fm1);
}
/*
set condition
*/
{
using command_type = set_condition<ft8::set_condition::data_format>;
maple::init_host_command(command_buf, receive_buf,
destination_port,
destination_ap, command_type::command_code, (sizeof (command_type::data_fields)),
true);
using host_command_type = struct maple::host_command<command_type::data_fields>;
auto host_command = reinterpret_cast<host_command_type *>(command_buf);
auto& fields = host_command->bus_data.data_fields;
fields.function_type = std::byteswap(function_type::vibration);
fields.write_in_data.ctrl = 0x11;
fields.write_in_data.pow = 0x70;
fields.write_in_data.freq = 0x27;
fields.write_in_data.inc = 0x00;
const uint32_t size = (reinterpret_cast<uint32_t>(&host_command[1]) - reinterpret_cast<uint32_t>(&host_command[0]));
maple::dma_start(command_buf, size);
using command_response_type = struct maple::command_response<device_reply::data_fields>;
for (uint32_t i = 0; i < (sizeof (command_response_type)) / 32; i++) {
asm volatile ("ocbp @%0"
: // output
: "r" (reinterpret_cast<uint32_t>(&receive_buf[(32 * i) / 4])) // input
);
}
auto command_response = reinterpret_cast<command_response_type *>(receive_buf);
auto& bus_data = command_response->bus_data;
if (bus_data.command_code != device_reply::command_code) {
serial::string("lm did not reply to vibration set_condition: ");
serial::integer<uint8_t>(lm);
} else {
serial::string("lm replied to vibration set_condition: ");
serial::integer<uint8_t>(lm);
}
}
}
void do_lm_requests(uint8_t port, uint8_t lm)
{
if (lm & ap::lm_bus::_0)
do_lm_request(port, lm & ap::lm_bus::_0);
if (lm & ap::lm_bus::_1)
do_lm_request(port, lm & ap::lm_bus::_1);
if (lm & ap::lm_bus::_2)
do_lm_request(port, lm & ap::lm_bus::_2);
if (lm & ap::lm_bus::_3)
do_lm_request(port, lm & ap::lm_bus::_3);
if (lm & ap::lm_bus::_4)
do_lm_request(port, lm & ap::lm_bus::_4);
}
void do_device_request()
{
using command_type = device_request;
using response_type = device_status;
const uint32_t size = maple::init_host_command_all_ports<command_type, response_type>(command_buf, receive_buf);
maple::dma_start(command_buf, size);
using command_response_type = struct maple::command_response<response_type::data_fields>;
for (uint32_t i = 0; i < ((sizeof (command_response_type)) * 4) / 32; i++) {
asm volatile ("ocbp @%0"
: // output
: "r" (reinterpret_cast<uint32_t>(&receive_buf[(32 * i) / 4])) // input
);
}
auto response = reinterpret_cast<command_response_type *>(receive_buf);
for (uint8_t port = 0; port < 4; port++) {
auto& bus_data = response[port].bus_data;
auto& data_fields = response[port].bus_data.data_fields;
if (bus_data.command_code != device_status::command_code) {
// the controller is disconnected
} else {
if ((data_fields.device_id.ft & std::byteswap(function_type::controller)) != 0) {
serial::string("controller: ");
serial::integer<uint8_t>(port);
//serial::integer<uint8_t>(bus_data.source_ap & ap::lm_bus::bit_mask);
do_lm_requests(port, bus_data.source_ap & ap::lm_bus::bit_mask);
}
}
}
}
void main()
{
uint32_t _command_buf[1024 / 4 + 32];
uint32_t _receive_buf[1024 / 4 + 32];
command_buf = align_32byte(_command_buf);
command_buf = reinterpret_cast<uint32_t *>(reinterpret_cast<uint32_t>(command_buf) | 0xa000'0000);
receive_buf = align_32byte(_receive_buf);
vga();
while (1) {
for (int i = 0; i < 120; i++) {
v_sync_out();
v_sync_in();
}
do_device_request();
};
}