make vmu respond to device info request with LCD / Clock support

added stubs for LCD / Clock commands
This commit is contained in:
Anthony Pesch 2017-11-25 10:39:37 -05:00
parent 944213db99
commit a8501a2f62
5 changed files with 171 additions and 125 deletions

View File

@ -18,10 +18,6 @@ struct reg_cb holly_cb[NUM_HOLLY_REGS];
/*
* ch2 dma
*/
static void holly_ch2_dma_stop(struct holly *hl) {
/* nop as DMA is always performed synchronously */
}
static void holly_ch2_dma(struct holly *hl) {
struct sh4 *sh4 = hl->dc->sh4;
@ -517,8 +513,6 @@ REG_W32(holly_cb, SB_C2DST) {
if (*hl->SB_C2DST) {
holly_ch2_dma(hl);
} else {
holly_ch2_dma_stop(hl);
}
}

View File

@ -44,50 +44,45 @@ static int controller_frame(struct maple_device *dev,
maple_decode_addr(req->dst_addr, &port, &unit);
struct maple_device *sub = maple_get_device(dev->mp, port, unit);
if (sub != dev) {
return sub && sub->frame(sub, req, res);
if (!sub) {
return 0;
}
switch (req->command) {
if (sub != dev) {
return sub->frame(sub, req, res);
}
switch (req->cmd) {
case MAPLE_REQ_DEVINFO: {
/* based on captured result of real Dreamcast controller */
char *name = "Dreamcast Controller";
char *license = "Produced By or Under License From SEGA ENTERPRISES,LTD.";
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));
strncpy_pad_spaces(info.name, name, sizeof(info.name));
strncpy_pad_spaces(info.license, license, 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->cmd = MAPLE_RES_DEVINFO;
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->cmd = MAPLE_RES_TRANSFER;
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;
default:
res->cmd = MAPLE_RES_BADCMD;
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 */
/* when a primary device identifies itself in the response to a cmd, 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);
@ -142,7 +137,7 @@ struct maple_device *controller_create(struct maple *mp, int port) {
ctrl->frame = &controller_frame;
/* default state */
ctrl->cnd.function = MAPLE_FUNC_CONTROLLER;
ctrl->cnd.func = MAPLE_FUNC_CONTROLLER;
ctrl->cnd.buttons = 0xffff;
ctrl->cnd.rtrig = ctrl->cnd.ltrig = 0;
ctrl->cnd.joyy = ctrl->cnd.joyx = ctrl->cnd.joyx2 = ctrl->cnd.joyy2 = 0x80;

View File

@ -24,30 +24,45 @@ static void maple_unregister_dev(struct maple *mp, int port, int unit) {
static void maple_register_dev(struct maple *mp, const char *device_type,
int port, int 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];
CHECK(!*dev, "maple_register_dev already registered for port=%d unit=%d",
port, unit);
if (!strcmp(device_type, "controller")) {
*dev = controller_create(mp, port);
} else if (!strcmp(device_type, "vmu")) {
*dev = vmu_create(mp, port);
} else {
LOG_WARNING("Unsupported device type: %s", device_type);
LOG_WARNING("maple_register_dev unsupported device_type=%s", device_type);
}
}
int maple_handle_frame(struct maple *mp, int port, union maple_frame *frame,
int maple_handle_frame(struct maple *mp, int port, union maple_frame *req,
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];
return dev && dev->frame(dev, frame, res);
if (!dev) {
return 0;
}
/* initialize response */
memset(res, 0, sizeof(*res));
res->dst_addr = req->src_addr;
res->src_addr = req->dst_addr;
/* send to primary device */
if (!dev->frame(dev, req, res)) {
return 0;
}
/* in general, these errors are rare and more often a bug in the emulator */
if ((int8_t)res->cmd < 0) {
LOG_WARNING("maple_handle_frame port=%d error=0x%x", port, res->cmd);
}
return 1;
}
void maple_handle_input(struct maple *mp, int port, int button,

View File

@ -24,14 +24,14 @@ enum maple_pattern {
function code */
enum maple_fn {
MAPLE_FUNC_CONTROLLER = 0x01000000,
MAPLE_FUNC_MEMORYCARD = 0x02000000,
MAPLE_FUNC_LCDDISPLAY = 0x04000000,
MAPLE_FUNC_MEMCARD = 0x02000000,
MAPLE_FUNC_LCD = 0x04000000,
MAPLE_FUNC_CLOCK = 0x08000000,
MAPLE_FUNC_MICROPHONE = 0x10000000,
MAPLE_FUNC_ARGUN = 0x20000000,
MAPLE_FUNC_KEYBOARD = 0x40000000,
MAPLE_FUNC_LIGHTGUN = 0x80000000,
MAPLE_FUNC_PURUPURUPACK = 0x00010000,
MAPLE_FUNC_PURUPURU = 0x00010000,
MAPLE_FUNC_MOUSE = 0x00020000
};
@ -48,9 +48,9 @@ enum maple_cmd {
MAPLE_RES_TRANSFER = (uint8_t)8,
MAPLE_REQ_GETCOND = (uint8_t)9,
MAPLE_REQ_GETMEMINFO = (uint8_t)10,
MAPLE_REQ_BLOCKREAD = (uint8_t)11,
MAPLE_REQ_BLOCKWRITE = (uint8_t)12,
MAPLE_REQ_BLOCKSYNC = (uint8_t)13,
MAPLE_REQ_BLKREAD = (uint8_t)11,
MAPLE_REQ_BLKWRITE = (uint8_t)12,
MAPLE_REQ_BLKSYNC = (uint8_t)13,
MAPLE_REQ_SETCOND = (uint8_t)14,
MAPLE_RES_NONE = (uint8_t)-1,
MAPLE_RES_BADFUNC = (uint8_t)-2,
@ -77,7 +77,7 @@ union maple_transfer {
header */
union maple_frame {
struct {
uint32_t command : 8;
uint32_t cmd : 8;
uint32_t dst_addr : 8;
uint32_t src_addr : 8;
uint32_t num_words : 8;
@ -108,7 +108,7 @@ struct maple_device_info {
/* response MAPLE_REQ_GETCOND */
struct maple_cond {
uint32_t function;
uint32_t func;
/* buttons bitfield contains 0s for pressed buttons and 1s for unpressed */
uint16_t buttons;
/* opposite of the buttons, 0 is unpressed for the triggers */
@ -123,9 +123,9 @@ struct maple_cond {
/* response to MAPLE_REQ_GETMEMINFO */
struct maple_meminfo {
uint32_t function;
uint32_t func;
uint16_t num_blocks;
uint16_t partiion;
uint16_t partition;
uint16_t root_block;
uint16_t fat_block;
uint16_t fat_num_blocks;
@ -137,9 +137,9 @@ struct maple_meminfo {
uint16_t reserved[2];
};
/* response to MAPLE_REQ_BLOCKREAD */
struct maple_blockread {
uint32_t function;
/* response to MAPLE_REQ_BLKREAD */
struct maple_blkread {
uint32_t func;
uint32_t block;
uint32_t data[];
};

View File

@ -3,10 +3,12 @@
#include "guest/maple/maple.h"
#include "guest/maple/vmu_default.inc"
#define VMU_BLOCK_SIZE 512
#define VMU_BLOCK_WORDS (512 >> 2)
#define VMU_BLOCK_OFFSET(blk, phase) \
((blk)*VMU_BLOCK_SIZE + (phase) * (VMU_BLOCK_SIZE >> 2))
#define BLK_SIZE 512
#define BLK_WORDS (512 >> 2)
#define BLK_OFFSET(blk, phase) ((blk)*BLK_SIZE + (phase) * (BLK_SIZE >> 2))
#define LCD_WIDTH 48
#define LCD_HEIGHT 32
struct vmu {
struct maple_device;
@ -18,7 +20,7 @@ struct vmu {
static void vmu_write_bin(struct vmu *vmu, int block, int phase,
const void *buffer, int num_words) {
int offset = VMU_BLOCK_OFFSET(block, phase);
int offset = BLK_OFFSET(block, phase);
int size = num_words << 2;
FILE *file = fopen(vmu->filename, "r+b");
@ -32,7 +34,7 @@ static void vmu_write_bin(struct vmu *vmu, int block, int phase,
static void vmu_read_bin(struct vmu *vmu, int block, int phase, void *buffer,
int num_words) {
int offset = VMU_BLOCK_OFFSET(block, phase);
int offset = BLK_OFFSET(block, phase);
int size = num_words << 2;
FILE *file = fopen(vmu->filename, "rb");
@ -57,103 +59,143 @@ static int vmu_frame(struct maple_device *dev, const union maple_frame *req,
union maple_frame *res) {
struct vmu *vmu = (struct vmu *)dev;
switch (req->command) {
switch (req->cmd) {
case MAPLE_REQ_DEVINFO: {
/* based on captured result of real Dreamcast VMU */
char *name = "Visual Memory";
char *license = "Produced By or Under License From SEGA ENTERPRISES,LTD.";
struct maple_device_info info;
info.func = MAPLE_FUNC_MEMORYCARD;
info.data[0] = 0x00410f00;
info.func = MAPLE_FUNC_CLOCK | MAPLE_FUNC_LCD | MAPLE_FUNC_MEMCARD;
info.data[0] = 0x403f7e7e; /* clock */
info.data[1] = 0x00100500; /* lcd */
info.data[2] = 0x00410f00; /* memcard */
info.region = 0xff;
strncpy_pad_spaces(info.name, "Visual Memory", sizeof(info.name));
strncpy_pad_spaces(
info.license,
"Produced By or Under License From SEGA ENTERPRISES,LTD.",
sizeof(info.license));
strncpy_pad_spaces(info.name, name, sizeof(info.name));
strncpy_pad_spaces(info.license, license, sizeof(info.license));
info.standby_power = 0x007c;
info.max_power = 0x0082;
res->command = MAPLE_RES_DEVINFO;
res->dst_addr = req->src_addr;
res->src_addr = req->dst_addr;
res->cmd = MAPLE_RES_DEVINFO;
res->num_words = sizeof(info) >> 2;
memcpy(res->params, &info, sizeof(info));
} break;
case MAPLE_REQ_GETMEMINFO: {
static struct maple_meminfo vmu_meminfo = {MAPLE_FUNC_MEMORYCARD,
0xff,
0x0,
0xff,
0xfe,
0x1,
0xfd,
0xd,
0x0,
0xc8,
0x1f,
{0x0, 0x0}};
uint32_t func = req->params[0];
CHECK_EQ(func, MAPLE_FUNC_MEMORYCARD);
CHECK_EQ(func, MAPLE_FUNC_MEMCARD);
uint32_t partition = req->params[1] & 0xff;
CHECK_EQ(partition, 0);
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));
struct maple_meminfo meminfo = {0};
meminfo.func = MAPLE_FUNC_MEMCARD;
meminfo.num_blocks = 0xff;
meminfo.partition = 0x0;
meminfo.root_block = 0xff;
meminfo.fat_block = 0xfe;
meminfo.fat_num_blocks = 0x1;
meminfo.dir_block = 0xfd;
meminfo.dir_num_blocks = 0xd;
meminfo.icon = 0x0;
meminfo.data_block = 0xc8;
meminfo.data_num_blocks = 0x1f;
res->cmd = MAPLE_RES_TRANSFER;
res->num_words = sizeof(meminfo) >> 2;
memcpy(res->params, &meminfo, sizeof(meminfo));
} break;
case MAPLE_REQ_BLOCKREAD: {
case MAPLE_REQ_BLKREAD: {
uint32_t func = req->params[0];
CHECK_EQ(func, MAPLE_FUNC_MEMORYCARD);
CHECK_EQ(func, MAPLE_FUNC_MEMCARD);
int partition, block, phase;
vmu_parse_block_param(req->params[1], &partition, &block, &phase);
CHECK_EQ(partition, 0);
CHECK_EQ(phase, 0);
switch (func) {
case MAPLE_FUNC_MEMCARD: {
int 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, req->params[1]};
struct maple_blkread blkread = {0};
blkread.func = MAPLE_FUNC_MEMCARD;
blkread.block = req->params[1];
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);
res->cmd = MAPLE_RES_TRANSFER;
res->num_words = (sizeof(blkread) >> 2) + BLK_WORDS;
memcpy(res->params, &blkread, sizeof(blkread));
vmu_read_bin(vmu, block, phase, &res->params[sizeof(blkread) >> 2],
BLK_WORDS);
} break;
default:
res->cmd = MAPLE_RES_BADFUNC;
break;
}
} break;
case MAPLE_REQ_BLOCKWRITE: {
case MAPLE_REQ_BLKWRITE: {
uint32_t func = req->params[0];
CHECK_EQ(func, MAPLE_FUNC_MEMORYCARD);
int partition, block, phase;
vmu_parse_block_param(req->params[1], &partition, &block, &phase);
CHECK_EQ(partition, 0);
switch (func) {
case MAPLE_FUNC_MEMCARD: {
int partition, block, phase;
vmu_parse_block_param(req->params[1], &partition, &block, &phase);
CHECK_EQ(partition, 0);
vmu_write_bin(vmu, block, phase, &req->params[2], req->num_words - 2);
vmu_write_bin(vmu, block, phase, &req->params[2], req->num_words - 2);
res->command = MAPLE_RES_ACK;
res->dst_addr = req->src_addr;
res->src_addr = req->dst_addr;
res->num_words = 0;
res->cmd = MAPLE_RES_ACK;
} break;
case MAPLE_FUNC_LCD: {
#if 0
/* TODO print this somewhere */
const uint8_t *data = (const uint8_t *)&req->params[2];
for (int y = LCD_HEIGHT-1; y >= 0; y--) {
for (int x = LCD_WIDTH-1; x >= 0; x--) {
int byte = x / 8;
int mask = 0x80 >> (x % 8);
if (data[y * (LCD_WIDTH / 8) + byte] & mask) {
printf("*");
} else {
printf(" ");
}
}
printf("\n");
}
#endif
res->cmd = MAPLE_RES_ACK;
} break;
default:
res->cmd = MAPLE_RES_BADFUNC;
break;
}
} break;
case MAPLE_REQ_BLOCKSYNC: {
res->command = MAPLE_RES_ACK;
res->dst_addr = req->src_addr;
res->src_addr = req->dst_addr;
res->num_words = 0;
case MAPLE_REQ_BLKSYNC:
res->cmd = MAPLE_RES_ACK;
break;
case MAPLE_REQ_SETCOND: {
uint32_t func = req->params[0];
switch (func) {
case MAPLE_FUNC_CLOCK: {
#if 0
/* TODO emit a a beep sound */
uint32_t beep = req->params[1];
#endif
res->cmd = MAPLE_RES_ACK;
} break;
default:
res->cmd = MAPLE_RES_BADFUNC;
break;
}
} break;
default: {
res->command = MAPLE_RES_BADCMD;
res->dst_addr = req->src_addr;
res->src_addr = req->dst_addr;
res->num_words = 0;
} break;
default:
res->cmd = MAPLE_RES_BADCMD;
break;
}
return 1;