mirror of https://github.com/inolen/redream.git
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:
parent
86d7b2a452
commit
a082e4733d
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue