mirror of https://github.com/inolen/redream.git
make vmu respond to device info request with LCD / Clock support
added stubs for LCD / Clock commands
This commit is contained in:
parent
944213db99
commit
a8501a2f62
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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[];
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue