diff --git a/src/guest/holly/holly.c b/src/guest/holly/holly.c index f5005374..93aaeb64 100644 --- a/src/guest/holly/holly.c +++ b/src/guest/holly/holly.c @@ -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; } } diff --git a/src/guest/maple/controller.c b/src/guest/maple/controller.c index 4eef839c..b0eaa158 100644 --- a/src/guest/maple/controller.c +++ b/src/guest/maple/controller.c @@ -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; diff --git a/src/guest/maple/maple.c b/src/guest/maple/maple.c index 8d22a709..bda9a4c8 100644 --- a/src/guest/maple/maple.c +++ b/src/guest/maple/maple.c @@ -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); +} diff --git a/src/guest/maple/maple.h b/src/guest/maple/maple.h index 65957787..1b7d090d 100644 --- a/src/guest/maple/maple.h +++ b/src/guest/maple/maple.h @@ -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 diff --git a/src/guest/maple/maple_types.h b/src/guest/maple/maple_types.h index 0ff7703c..1270e8ff 100644 --- a/src/guest/maple/maple_types.h +++ b/src/guest/maple/maple_types.h @@ -3,7 +3,10 @@ #include +/* 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 */ diff --git a/src/guest/maple/vmu.c b/src/guest/maple/vmu.c index 0cd9b0b8..90764cf2 100644 --- a/src/guest/maple/vmu.c +++ b/src/guest/maple/vmu.c @@ -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);