don't attempt to inspect maple frame data outside of device's frame

handler routine as the frame data format isn't standard across all
devices
This commit is contained in:
Anthony Pesch 2017-10-28 17:42:31 -04:00
parent 86d7b2a452
commit a082e4733d
6 changed files with 216 additions and 217 deletions

View File

@ -108,26 +108,20 @@ static void holly_maple_dma(struct holly *hl) {
uint32_t result_addr = sh4_read32(mem, addr);
addr += 4;
/* read message */
struct maple_frame frame, res;
frame.header.full = sh4_read32(mem, addr);
addr += 4;
/* read frame */
union maple_frame frame, res;
for (uint32_t i = 0; i < frame.header.num_words; i++) {
frame.params[i] = sh4_read32(mem, addr);
for (int i = 0; i < (int)desc.length + 1; i++) {
frame.data[i] = sh4_read32(mem, addr);
addr += 4;
}
/* process message */
int handled = maple_handle_command(mp, &frame, &res);
/* process frame and write response */
int handled = maple_handle_frame(mp, desc.port, &frame, &res);
/* write response */
if (handled) {
sh4_write32(mem, result_addr, res.header.full);
result_addr += 4;
for (uint32_t i = 0; i < res.header.num_words; i++) {
sh4_write32(mem, result_addr, res.params[i]);
for (int i = 0; i < (int)res.num_words + 1; i++) {
sh4_write32(mem, result_addr, res.data[i]);
result_addr += 4;
}
} else {
@ -139,11 +133,11 @@ static void holly_maple_dma(struct holly *hl) {
break;
default:
LOG_FATAL("unhandled maple pattern 0x%x", desc.pattern);
LOG_FATAL("holly_maple_dma unhandled pattern 0x%x", desc.pattern);
break;
}
if (desc.last) {
if (desc.end) {
break;
}
}

View File

@ -34,6 +34,72 @@ struct controller {
struct maple_cond cnd;
};
static void controller_frame(struct maple_device *dev,
const union maple_frame *req,
union maple_frame *res) {
struct controller *ctrl = (struct controller *)dev;
/* forward to sub-device if specified */
int port, unit;
maple_decode_addr(req->dst_addr, &port, &unit);
struct maple_device *sub = maple_get_device(dev->mp, port, unit);
if (sub != dev) {
sub->frame(sub, req, res);
return;
}
switch (req->command) {
case MAPLE_REQ_DEVINFO: {
/* based on captured result of real Dreamcast controller */
struct maple_device_info info = {0};
info.func = MAPLE_FUNC_CONTROLLER;
info.data[0] = 0xfe060f00;
info.region = 0xff;
strncpy_pad_spaces(info.name, "Dreamcast Controller", sizeof(info.name));
strncpy_pad_spaces(
info.license,
"Produced By or Under License From SEGA ENTERPRISES,LTD.",
sizeof(info.license));
info.standby_power = 0x01ae;
info.max_power = 0x01f4;
res->command = MAPLE_RES_DEVINFO;
res->dst_addr = req->src_addr;
res->src_addr = req->dst_addr;
res->num_words = sizeof(info) >> 2;
memcpy(res->params, &info, sizeof(info));
} break;
case MAPLE_REQ_GETCOND: {
res->command = MAPLE_RES_TRANSFER;
res->dst_addr = req->src_addr;
res->src_addr = req->dst_addr;
res->num_words = sizeof(ctrl->cnd) >> 2;
memcpy(res->params, &ctrl->cnd, sizeof(ctrl->cnd));
} break;
default: {
res->command = MAPLE_RES_BADCMD;
res->dst_addr = req->src_addr;
res->src_addr = req->dst_addr;
res->num_words = 0;
} break;
}
/* when a primary device identifies itself in the response to a command, it
sets the bit for each connected sub-device in addition to bit 5 */
for (int i = 0; i < MAPLE_MAX_UNITS - 1; i++) {
struct maple_device *sub = maple_get_device(dev->mp, port, i);
if (!sub) {
continue;
}
res->src_addr |= 1 << i;
}
}
static int controller_input(struct maple_device *dev, int button,
uint16_t value) {
struct controller *ctrl = (struct controller *)dev;
@ -61,58 +127,15 @@ static int controller_input(struct maple_device *dev, int button,
return 1;
}
static int controller_frame(struct maple_device *dev,
const struct maple_frame *frame,
struct maple_frame *res) {
struct controller *ctrl = (struct controller *)dev;
switch (frame->header.command) {
case MAPLE_REQ_DEVINFO: {
/* based on captured result of real Dreamcast controller */
struct maple_device_info info = {0};
info.func = MAPLE_FUNC_CONTROLLER;
info.data[0] = 0xfe060f00;
info.region = 0xff;
strncpy_pad_spaces(info.name, "Dreamcast Controller", sizeof(info.name));
strncpy_pad_spaces(
info.license,
"Produced By or Under License From SEGA ENTERPRISES,LTD.",
sizeof(info.license));
info.standby_power = 0x01ae;
info.max_power = 0x01f4;
res->header.command = MAPLE_RES_DEVINFO;
res->header.recv_addr = frame->header.send_addr;
res->header.send_addr = frame->header.recv_addr;
res->header.num_words = sizeof(info) >> 2;
memcpy(res->params, &info, sizeof(info));
return 1;
}
case MAPLE_REQ_GETCOND: {
res->header.command = MAPLE_RES_TRANSFER;
res->header.recv_addr = frame->header.send_addr;
res->header.send_addr = frame->header.recv_addr;
res->header.num_words = sizeof(ctrl->cnd) >> 2;
memcpy(res->params, &ctrl->cnd, sizeof(ctrl->cnd));
return 1;
}
}
return 0;
}
static void controller_destroy(struct maple_device *dev) {
struct controller *ctrl = (struct controller *)dev;
free(ctrl);
}
struct maple_device *controller_create(struct dreamcast *dc, int port,
int unit) {
struct maple_device *controller_create(struct maple *mp, int port) {
struct controller *ctrl = calloc(1, sizeof(struct controller));
ctrl->dc = dc;
ctrl->port = port;
ctrl->unit = unit;
ctrl->mp = mp;
ctrl->destroy = &controller_destroy;
ctrl->input = &controller_input;
ctrl->frame = &controller_frame;

View File

@ -5,11 +5,11 @@
struct maple {
struct device;
struct maple_device *devices[MAPLE_NUM_PORTS][MAPLE_MAX_UNITS];
struct maple_device *devs[MAPLE_NUM_PORTS][MAPLE_MAX_UNITS];
};
static void maple_unregister_device(struct maple *mp, int port, int unit) {
struct maple_device **dev = &mp->devices[port][unit];
static void maple_unregister_dev(struct maple *mp, int port, int unit) {
struct maple_device **dev = &mp->devs[port][unit];
if (!*dev) {
return;
@ -22,108 +22,57 @@ static void maple_unregister_device(struct maple *mp, int port, int unit) {
*dev = NULL;
}
static void maple_register_device(struct maple *mp, const char *device_type,
int port, int unit) {
static void maple_register_dev(struct maple *mp, const char *device_type,
int port, int unit) {
struct dreamcast *dc = mp->dc;
CHECK(!mp->devices[port][unit],
"Device already registered for port %d, unit %d", port, unit);
struct maple_device **dev = &mp->devices[port][unit];
CHECK(!mp->devs[port][unit],
"maple_register_dev already registered for port=%d unit=%d", port,
unit);
struct maple_device **dev = &mp->devs[port][unit];
if (!strcmp(device_type, "controller")) {
*dev = controller_create(dc, port, unit);
*dev = controller_create(mp, port);
} else if (!strcmp(device_type, "vmu")) {
*dev = vmu_create(dc, port, unit);
*dev = vmu_create(mp, port);
} else {
LOG_WARNING("Unsupported device type: %s", device_type);
}
}
/* on each maple port, there are up to 6 addressable units. there is one main
unit (controller, keyboard, etc.) that can have up to 5 sub-units connected
to it (vmu, microphone, etc.). each maple frame header contains an 8-bit
address specifying the port and unit it's intended for that looks like:
7-6 5 4 3 2 1 0
port main unit sub-unit 5 sub-unit 4 sub-unit 3 sub-unit 2 sub-unit 1 */
static uint8_t maple_encode_addr(int port, int unit) {
CHECK_LT(port, MAPLE_NUM_PORTS);
CHECK_LT(unit, MAPLE_MAX_UNITS);
uint8_t addr = port << 6;
if (unit) {
addr |= 1 << (unit - 1);
} else {
addr |= 1 << (MAPLE_MAX_UNITS - 1);
}
return addr;
}
static void maple_decode_addr(uint32_t addr, int *port, int *unit) {
*port = addr >> 6;
CHECK_LT(*port, MAPLE_NUM_PORTS);
/* prioritize the main unit, else return the first matching sub-unit */
*unit = -1;
if (addr & (1 << (MAPLE_MAX_UNITS - 1))) {
*unit = 0;
} else {
for (int i = 1; i < MAPLE_MAX_UNITS; i++) {
if (addr & (1 << (i - 1))) {
*unit = i;
break;
}
}
}
CHECK(*unit >= 0 && *unit < MAPLE_MAX_UNITS);
}
int maple_handle_command(struct maple *mp, struct maple_frame *frame,
struct maple_frame *res) {
int p, u;
maple_decode_addr(frame->header.recv_addr, &p, &u);
struct maple_device *dev = mp->devices[p][u];
int maple_handle_frame(struct maple *mp, int port, union maple_frame *frame,
union maple_frame *res) {
CHECK(port >= 0 && port < MAPLE_NUM_PORTS);
/* note, not all maple devices have the same frame format. for example, the
check-gd disc sends a command to some kind of debug device which has the
frame data bswap'd. for this reason, it's not valid to inspect the frame
data in order to send the frame directly to the correct sub-device */
struct maple_device *dev = mp->devs[port][MAPLE_MAX_UNITS - 1];
if (!dev) {
return 0;
}
if (!dev->frame(dev, frame, res)) {
LOG_INFO("unhandled maple cmd %d for port %d, unit %d",
frame->header.command, p, u);
return 0;
}
/* when a main peripheral identifies itself in the response to a command, it
sets the sub-peripheral bit for each sub-peripheral that is connected in
in addition to bit 5 */
if (u == 0) {
for (int i = 1; i < MAPLE_MAX_UNITS; i++) {
struct maple_device *sub = mp->devices[p][i];
if (sub) {
res->header.send_addr |= (1 << (i - 1));
}
}
}
dev->frame(dev, frame, res);
return 1;
}
void maple_handle_input(struct maple *mp, int port, int button,
uint16_t value) {
CHECK_LT(port, MAPLE_NUM_PORTS);
CHECK(port >= 0 && port < MAPLE_NUM_PORTS);
for (int i = 0; i < MAPLE_MAX_UNITS; i++) {
struct maple_device *dev = mp->devices[port][i];
/* send to primary device */
struct maple_device *dev = mp->devs[port][MAPLE_MAX_UNITS - 1];
if (dev && dev->input) {
dev->input(dev, button, value);
}
if (dev && dev->input) {
dev->input(dev, button, value);
}
}
struct maple_device *maple_get_device(struct maple *mp, int port, int unit) {
return mp->devs[port][unit];
}
static int maple_init(struct device *dev) {
return 1;
}
@ -131,7 +80,7 @@ static int maple_init(struct device *dev) {
void maple_destroy(struct maple *mp) {
for (int i = 0; i < MAPLE_NUM_PORTS; i++) {
for (int j = 0; j < MAPLE_MAX_UNITS; j++) {
maple_unregister_device(mp, i, j);
maple_unregister_dev(mp, i, j);
}
}
@ -144,9 +93,36 @@ struct maple *maple_create(struct dreamcast *dc) {
/* register a controller and vmu for all ports by default */
for (int i = 0; i < MAPLE_NUM_PORTS; i++) {
maple_register_device(mp, "controller", i, 0);
maple_register_device(mp, "vmu", i, 1);
maple_register_dev(mp, "controller", i, 5);
maple_register_dev(mp, "vmu", i, 0);
}
return mp;
}
/* on each maple port, there are up to 6 addressable units. there is one main
unit (controller, keyboard, etc.) that can have up to 5 sub-units connected
to it (vmu, microphone, etc.). each maple frame header contains an 8-bit
address specifying the port and unit it's intended for that looks like:
7-6 5 4 3 2 1 0
port main unit sub-unit 5 sub-unit 4 sub-unit 3 sub-unit 2 sub-unit 1 */
int maple_decode_addr(uint32_t addr, int *port, int *unit) {
*port = addr >> 6;
*unit = 0;
for (int i = 0; i < MAPLE_MAX_UNITS; i++) {
if (addr & (1 << i)) {
*unit = i;
}
}
return (*port >= 0 && *port < MAPLE_NUM_PORTS) &&
(*unit >= 0 && *unit < MAPLE_MAX_UNITS);
}
uint8_t maple_encode_addr(int port, int unit) {
CHECK(port >= 0 && port < MAPLE_NUM_PORTS);
CHECK(unit >= 0 && unit < MAPLE_MAX_UNITS);
return (port << 6) | (1 << unit);
}

View File

@ -8,24 +8,25 @@ struct maple;
struct maple_device;
struct maple_device {
struct dreamcast *dc;
int port;
int unit;
struct maple *mp;
void (*destroy)(struct maple_device *);
int (*input)(struct maple_device *, int, uint16_t);
int (*frame)(struct maple_device *, const struct maple_frame *,
struct maple_frame *);
void (*frame)(struct maple_device *, const union maple_frame *,
union maple_frame *);
};
uint8_t maple_encode_addr(int port, int unit);
int maple_decode_addr(uint32_t addr, int *port, int *unit);
struct maple *maple_create(struct dreamcast *dc);
void maple_destroy(struct maple *mp);
struct maple_device *maple_get_device(struct maple *mp, int port, int unit);
void maple_handle_input(struct maple *mp, int port, int button, uint16_t value);
int maple_handle_command(struct maple *mp, struct maple_frame *frame,
struct maple_frame *res);
int maple_handle_frame(struct maple *mp, int port, union maple_frame *frame,
union maple_frame *res);
struct maple_device *controller_create(struct dreamcast *dc, int port,
int unit);
struct maple_device *vmu_create(struct dreamcast *dc, int port, int unit);
struct maple_device *controller_create(struct maple *mp, int port);
struct maple_device *vmu_create(struct maple *mp, int port);
#endif

View File

@ -3,7 +3,10 @@
#include <stdint.h>
/* number of ports on the maple bus */
#define MAPLE_NUM_PORTS 4
/* number of addressable units on each maple port */
#define MAPLE_MAX_UNITS 6
/* maple pattern codes. indicate how to process the incoming instruction */
@ -61,10 +64,10 @@ union maple_transfer {
struct {
uint32_t length : 8;
uint32_t pattern : 3;
uint32_t reserved : 5;
uint32_t : 5;
uint32_t port : 2;
uint32_t reserved1 : 13;
uint32_t last : 1;
uint32_t : 13;
uint32_t end : 1;
};
uint32_t full;
};
@ -73,19 +76,25 @@ union maple_transfer {
union maple_header {
struct {
uint32_t command : 8;
uint32_t recv_addr : 8;
uint32_t send_addr : 8;
uint32_t dst_addr : 8;
uint32_t src_addr : 8;
uint32_t num_words : 8;
};
uint32_t full;
};
/* messages sent on the maple bus are sent as a "frame", with each frame
consisting of one or more 32-bit words. the first word in each frame
is the header */
struct maple_frame {
union maple_header header;
uint32_t params[0xff];
consisting of 1-256 32-bit words. the first word in each frame is the
header */
union maple_frame {
struct {
uint32_t command : 8;
uint32_t dst_addr : 8;
uint32_t src_addr : 8;
uint32_t num_words : 8;
uint32_t params[];
};
uint32_t data[0x100];
};
/* response to MAPLE_REQ_DEVINFO */

View File

@ -53,11 +53,11 @@ static void vmu_parse_block_param(uint32_t data, int *partition, int *block,
*phase = (data >> 8) & 0xff;
}
static int vmu_frame(struct maple_device *dev, const struct maple_frame *frame,
struct maple_frame *res) {
static void vmu_frame(struct maple_device *dev, const union maple_frame *req,
union maple_frame *res) {
struct vmu *vmu = (struct vmu *)dev;
switch (frame->header.command) {
switch (req->command) {
case MAPLE_REQ_DEVINFO: {
/* based on captured result of real Dreamcast VMU */
struct maple_device_info info;
@ -72,13 +72,12 @@ static int vmu_frame(struct maple_device *dev, const struct maple_frame *frame,
info.standby_power = 0x007c;
info.max_power = 0x0082;
res->header.command = MAPLE_RES_DEVINFO;
res->header.recv_addr = frame->header.send_addr;
res->header.send_addr = frame->header.recv_addr;
res->header.num_words = sizeof(info) >> 2;
res->command = MAPLE_RES_DEVINFO;
res->dst_addr = req->src_addr;
res->src_addr = req->dst_addr;
res->num_words = sizeof(info) >> 2;
memcpy(res->params, &info, sizeof(info));
return 1;
}
} break;
case MAPLE_REQ_GETMEMINFO: {
static struct maple_meminfo vmu_meminfo = {MAPLE_FUNC_MEMORYCARD,
@ -94,69 +93,68 @@ static int vmu_frame(struct maple_device *dev, const struct maple_frame *frame,
0x1f,
{0x0, 0x0}};
uint32_t func = frame->params[0];
uint32_t func = req->params[0];
CHECK_EQ(func, MAPLE_FUNC_MEMORYCARD);
uint32_t partition = frame->params[1] & 0xff;
uint32_t partition = req->params[1] & 0xff;
CHECK_EQ(partition, 0);
res->header.command = MAPLE_RES_TRANSFER;
res->header.recv_addr = frame->header.send_addr;
res->header.send_addr = frame->header.recv_addr;
res->header.num_words = sizeof(vmu_meminfo) >> 2;
res->command = MAPLE_RES_TRANSFER;
res->dst_addr = req->src_addr;
res->src_addr = req->dst_addr;
res->num_words = sizeof(vmu_meminfo) >> 2;
memcpy(res->params, &vmu_meminfo, sizeof(vmu_meminfo));
return 1;
}
} break;
case MAPLE_REQ_BLOCKREAD: {
uint32_t func = frame->params[0];
uint32_t func = req->params[0];
CHECK_EQ(func, MAPLE_FUNC_MEMORYCARD);
int partition, block, phase;
vmu_parse_block_param(frame->params[1], &partition, &block, &phase);
vmu_parse_block_param(req->params[1], &partition, &block, &phase);
CHECK_EQ(partition, 0);
CHECK_EQ(phase, 0);
struct maple_blockread vmu_read = {MAPLE_FUNC_MEMORYCARD,
frame->params[1]};
struct maple_blockread vmu_read = {MAPLE_FUNC_MEMORYCARD, req->params[1]};
res->header.command = MAPLE_RES_TRANSFER;
res->header.recv_addr = frame->header.send_addr;
res->header.send_addr = frame->header.recv_addr;
res->header.num_words = (sizeof(vmu_read) >> 2) + VMU_BLOCK_WORDS;
res->command = MAPLE_RES_TRANSFER;
res->dst_addr = req->src_addr;
res->src_addr = req->dst_addr;
res->num_words = (sizeof(vmu_read) >> 2) + VMU_BLOCK_WORDS;
memcpy(res->params, &vmu_read, sizeof(vmu_read));
vmu_read_bin(vmu, block, phase, &res->params[sizeof(vmu_read) >> 2],
VMU_BLOCK_WORDS);
return 1;
}
} break;
case MAPLE_REQ_BLOCKWRITE: {
uint32_t func = frame->params[0];
uint32_t func = req->params[0];
CHECK_EQ(func, MAPLE_FUNC_MEMORYCARD);
int partition, block, phase;
vmu_parse_block_param(frame->params[1], &partition, &block, &phase);
vmu_parse_block_param(req->params[1], &partition, &block, &phase);
CHECK_EQ(partition, 0);
vmu_write_bin(vmu, block, phase, &frame->params[2],
frame->header.num_words - 2);
vmu_write_bin(vmu, block, phase, &req->params[2], req->num_words - 2);
res->header.command = MAPLE_RES_ACK;
res->header.recv_addr = frame->header.send_addr;
res->header.send_addr = frame->header.recv_addr;
res->header.num_words = 0;
return 1;
}
res->command = MAPLE_RES_ACK;
res->dst_addr = req->src_addr;
res->src_addr = req->dst_addr;
res->num_words = 0;
} break;
case MAPLE_REQ_BLOCKSYNC: {
res->header.command = MAPLE_RES_ACK;
res->header.recv_addr = frame->header.send_addr;
res->header.send_addr = frame->header.recv_addr;
res->header.num_words = 0;
return 1;
}
}
res->command = MAPLE_RES_ACK;
res->dst_addr = req->src_addr;
res->src_addr = req->dst_addr;
res->num_words = 0;
} break;
return 0;
default: {
res->command = MAPLE_RES_BADCMD;
res->dst_addr = req->src_addr;
res->src_addr = req->dst_addr;
res->num_words = 0;
} break;
}
}
static void vmu_destroy(struct maple_device *dev) {
@ -164,22 +162,20 @@ static void vmu_destroy(struct maple_device *dev) {
free(vmu);
}
struct maple_device *vmu_create(struct dreamcast *dc, int port, int unit) {
struct maple_device *vmu_create(struct maple *mp, int port) {
struct vmu *vmu = calloc(1, sizeof(struct vmu));
vmu->dc = dc;
vmu->port = port;
vmu->unit = unit;
vmu->mp = mp;
vmu->destroy = &vmu_destroy;
vmu->frame = &vmu_frame;
/* intialize default vmu if one doesn't exist */
const char *appdir = fs_appdir();
snprintf(vmu->filename, sizeof(vmu->filename),
"%s" PATH_SEPARATOR "vmu_%d_%d.bin", appdir, vmu->port, vmu->unit);
"%s" PATH_SEPARATOR "vmu%d.bin", appdir, port);
/* intialize default vmu if one doesn't already exist */
if (!fs_exists(vmu->filename)) {
LOG_INFO("initializing vmu at %s", vmu->filename);
LOG_INFO("vmu_create initializing %s", vmu->filename);
FILE *file = fopen(vmu->filename, "wb");
CHECK_NOTNULL(file, "failed to open %s", vmu->filename);