nkeynes@1034: /** nkeynes@1034: * $Id: vmu.c 858 2008-09-02 03:34:00Z nkeynes $ nkeynes@1034: * nkeynes@1034: * Implementation of the SEGA VMU device nkeynes@1034: * Part No. HKT-7000 nkeynes@1034: * nkeynes@1034: * The standard VMU implements 3 functions - Clock, LCD, and memory card, nkeynes@1034: * in addition to having it's own little CPU, buttons, etc. The CPU isn't nkeynes@1034: * implemented just yet. nkeynes@1034: * nkeynes@1034: * Copyright (c) 2009 Nathan Keynes. nkeynes@1034: * nkeynes@1034: * This program is free software; you can redistribute it and/or modify nkeynes@1034: * it under the terms of the GNU General Public License as published by nkeynes@1034: * the Free Software Foundation; either version 2 of the License, or nkeynes@1034: * (at your option) any later version. nkeynes@1034: * nkeynes@1034: * This program is distributed in the hope that it will be useful, nkeynes@1034: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@1034: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@1034: * GNU General Public License for more details. nkeynes@1034: */ nkeynes@1034: nkeynes@1034: #include nkeynes@1034: #include nkeynes@1034: #include nkeynes@1034: #include nkeynes@1034: #include nkeynes@1034: #include nkeynes@1034: #include nkeynes@1034: #include "display.h" nkeynes@1034: #include "maple/maple.h" nkeynes@1034: #include "vmu/vmuvol.h" nkeynes@1034: #include "vmu/vmulist.h" nkeynes@1034: nkeynes@1034: #define VMU_LCD_SIZE (48*4) nkeynes@1034: #define VMU_BLOCK_COUNT 256 nkeynes@1034: #define VMU_BLOCK_SIZE 512 nkeynes@1034: #define VMU_PHASE_SIZE 128 nkeynes@1034: #define VMU_CONFIG_ENTRIES 1 nkeynes@1034: nkeynes@1034: #define VMU_IDENT { 0x00, 0x00, 0x00, 0x0E, 0x7E, 0x7E, 0x3F, 0x40, 0x00, 0x05, 0x10, 0x00, \ nkeynes@1034: 0x00, 0x0F, 0x41, 0x00, 0xFF, 0x00, 0x56, 0x69, 0x73, 0x75, 0x61, 0x6C, 0x20, 0x4D, 0x65, 0x6D, \ nkeynes@1034: 0x6F, 0x72, 0x79, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, \ nkeynes@1034: 0x20, 0x20, 0x20, 0x20, 0x50, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x42, 0x79, 0x20, \ nkeynes@1034: 0x6F, 0x72, 0x20, 0x55, 0x6E, 0x64, 0x65, 0x72, 0x20, 0x4C, 0x69, 0x63, 0x65, 0x6E, 0x73, 0x65, \ nkeynes@1034: 0x20, 0x46, 0x72, 0x6F, 0x6D, 0x20, 0x53, 0x45, 0x47, 0x41, 0x20, 0x45, 0x4E, 0x54, 0x45, 0x52, \ nkeynes@1034: 0x50, 0x52, 0x49, 0x53, 0x45, 0x53, 0x2C, 0x4C, 0x54, 0x44, 0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, \ nkeynes@1034: 0x7C, 0x00, 0x82, 0x00 } nkeynes@1034: #define VMU_VERSION { 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x31, 0x2E, 0x30, 0x30, \ nkeynes@1034: 0x35, 0x2C, 0x31, 0x39, 0x39, 0x39, 0x2F, 0x30, 0x34, 0x2F, 0x31, 0x35, 0x2C, 0x33, 0x31, 0x35, \ nkeynes@1034: 0x2D, 0x36, 0x32, 0x30, 0x38, 0x2D, 0x30, 0x33, 0x2C, 0x53, 0x45, 0x47, 0x41, 0x20, 0x56, 0x69, \ nkeynes@1034: 0x73, 0x75, 0x61, 0x6C, 0x20, 0x4D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x20, 0x53, 0x79, 0x73, 0x74, \ nkeynes@1034: 0x65, 0x6D, 0x20, 0x42, 0x49, 0x4F, 0x53, 0x20, 0x50, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x65, 0x64, \ nkeynes@1034: 0x20, 0x62, 0x79, 0x20 } nkeynes@1034: nkeynes@1034: static void vmu_destroy( maple_device_t dev ); nkeynes@1034: static maple_device_t vmu_clone( maple_device_t dev ); nkeynes@1034: static maple_device_t vmu_new(); nkeynes@1034: static lxdream_config_entry_t vmu_get_config( maple_device_t dev ); nkeynes@1034: static void vmu_set_config_value( maple_device_t dev, unsigned int key, const gchar *value ); nkeynes@1034: static int vmu_get_condition( maple_device_t dev, int function, unsigned char *outbuf, nkeynes@1034: unsigned int *outlen ); nkeynes@1034: static int vmu_get_meminfo( maple_device_t dev, int function, unsigned int pt, nkeynes@1034: unsigned char *outbuf, unsigned int *outlen ); nkeynes@1034: static void vmu_attach(struct maple_device *dev); nkeynes@1034: static void vmu_detach(struct maple_device *dev); nkeynes@1034: static int vmu_read_block(struct maple_device *dev, int function, unsigned int pt, nkeynes@1034: uint32_t block, unsigned int phase, nkeynes@1034: unsigned char *outbuf, unsigned int *buflen); nkeynes@1034: static int vmu_write_block(struct maple_device *dev, int function, unsigned int pt, nkeynes@1034: uint32_t block, unsigned int phase, nkeynes@1034: unsigned char *inbuf, unsigned int buflen); nkeynes@1034: nkeynes@1034: typedef struct vmu_device { nkeynes@1034: struct maple_device dev; nkeynes@1034: vmu_volume_t vol; nkeynes@1034: char lcd_bitmap[VMU_LCD_SIZE]; /* 48x32 bitmap */ nkeynes@1034: struct lxdream_config_entry config[VMU_CONFIG_ENTRIES+1]; nkeynes@1034: } *vmu_device_t; nkeynes@1034: nkeynes@1034: struct maple_device_class vmu_class = { "Sega VMU", MAPLE_GRAB_DONTCARE, vmu_new }; nkeynes@1034: nkeynes@1034: static struct vmu_device base_vmu = { nkeynes@1034: { MAPLE_DEVICE_TAG, &vmu_class, nkeynes@1034: VMU_IDENT, VMU_VERSION, nkeynes@1034: vmu_get_config, vmu_set_config_value, nkeynes@1034: vmu_attach, vmu_detach, vmu_destroy, nkeynes@1034: vmu_clone, NULL, NULL, vmu_get_condition, NULL, nkeynes@1034: vmu_get_meminfo, vmu_read_block, vmu_write_block, NULL, NULL }, nkeynes@1034: NULL, {0}, nkeynes@1034: {{ "volume", N_("Volume"), CONFIG_TYPE_FILE }, nkeynes@1034: { NULL, CONFIG_TYPE_NONE }} }; nkeynes@1034: nkeynes@1034: static maple_device_t vmu_new( ) nkeynes@1034: { nkeynes@1034: vmu_device_t dev = malloc( sizeof(struct vmu_device) ); nkeynes@1034: memcpy( dev, &base_vmu, sizeof(base_vmu) ); nkeynes@1034: return MAPLE_DEVICE(dev); nkeynes@1034: } nkeynes@1034: nkeynes@1034: static maple_device_t vmu_clone( maple_device_t srcdevice ) nkeynes@1034: { nkeynes@1034: vmu_device_t src = (vmu_device_t)srcdevice; nkeynes@1034: vmu_device_t dev = (vmu_device_t)vmu_new(); nkeynes@1034: lxdream_copy_config_list( dev->config, src->config ); nkeynes@1034: return MAPLE_DEVICE(dev); nkeynes@1034: } nkeynes@1034: nkeynes@1034: static lxdream_config_entry_t vmu_get_config( maple_device_t mdev ) nkeynes@1034: { nkeynes@1034: vmu_device_t dev = (vmu_device_t)mdev; nkeynes@1034: return dev->config; nkeynes@1034: } nkeynes@1034: nkeynes@1034: static void vmu_set_config_value( maple_device_t dev, unsigned int key, const gchar *value ) nkeynes@1034: { nkeynes@1034: vmu_device_t vmu = (vmu_device_t)dev; nkeynes@1034: assert( key < VMU_CONFIG_ENTRIES ); nkeynes@1034: nkeynes@1034: if( value == vmu->config[key].value || nkeynes@1034: value != NULL && vmu->config[key].value != NULL && strcmp(vmu->config[key].value, value) == 0 ) { nkeynes@1034: return; /* Unchanged */ nkeynes@1034: } nkeynes@1034: nkeynes@1034: if( vmu->vol != NULL ) { nkeynes@1034: vmulist_detach_vmu(vmu->vol); nkeynes@1034: } nkeynes@1034: lxdream_set_config_value( &vmu->config[key], value ); nkeynes@1034: vmu->vol = vmulist_get_vmu_by_filename( value ); nkeynes@1034: if( vmu->vol != NULL ) { nkeynes@1034: vmulist_attach_vmu(vmu->vol, "MAPLE"); nkeynes@1034: } nkeynes@1034: } nkeynes@1034: nkeynes@1034: void vmu_attach(struct maple_device *dev) nkeynes@1034: { nkeynes@1034: vmu_device_t vmu = (vmu_device_t)dev; nkeynes@1034: if( vmu->config[0].value != NULL ) { nkeynes@1034: vmu->vol = vmulist_get_vmu_by_filename(vmu->config[0].value); nkeynes@1034: if( vmu->vol != NULL ) { nkeynes@1034: vmulist_attach_vmu(vmu->vol, "MAPLE"); nkeynes@1034: } nkeynes@1034: } nkeynes@1034: } nkeynes@1034: nkeynes@1034: static void vmu_detach(struct maple_device *dev) nkeynes@1034: { nkeynes@1034: vmu_device_t vmu = (vmu_device_t)dev; nkeynes@1034: if( vmu->vol != NULL ) { nkeynes@1034: vmulist_detach_vmu(vmu->vol); nkeynes@1034: vmu->vol = NULL; nkeynes@1034: } nkeynes@1034: } nkeynes@1034: nkeynes@1034: static void vmu_destroy( maple_device_t dev ) nkeynes@1034: { nkeynes@1034: vmu_device_t vmu = (vmu_device_t)dev; nkeynes@1034: free( dev ); nkeynes@1034: } nkeynes@1034: nkeynes@1034: static int vmu_get_condition(struct maple_device *dev, int function, nkeynes@1034: unsigned char *outbuf, unsigned int *buflen) nkeynes@1034: { nkeynes@1034: } nkeynes@1034: static int vmu_set_condition(struct maple_device *dev, int function, nkeynes@1034: unsigned char *inbuf, unsigned int buflen) nkeynes@1034: { nkeynes@1034: return MAPLE_ERR_NO_RESPONSE; /* CHECKME */ nkeynes@1034: } nkeynes@1034: nkeynes@1034: static int vmu_get_meminfo(struct maple_device *dev, int function, unsigned int pt, nkeynes@1034: unsigned char *outbuf, unsigned int *buflen) nkeynes@1034: { nkeynes@1034: struct vmu_device *vmu = (struct vmu_device *)dev; nkeynes@1034: switch(function) { nkeynes@1034: case MAPLE_FUNC_MEMORY: nkeynes@1034: if( vmu->vol != NULL ) { nkeynes@1034: const struct vmu_volume_metadata *md = vmu_volume_get_metadata( vmu->vol, pt ); nkeynes@1034: memcpy( outbuf, md, sizeof(struct vmu_volume_metadata) ); nkeynes@1034: *buflen = sizeof(struct vmu_volume_metadata); nkeynes@1034: return 0; nkeynes@1034: } // Else fallthrough nkeynes@1034: case MAPLE_FUNC_LCD: nkeynes@1034: case MAPLE_FUNC_CLOCK: nkeynes@1034: return MAPLE_ERR_NO_RESPONSE; nkeynes@1034: default: nkeynes@1034: return MAPLE_ERR_FUNC_UNSUP; nkeynes@1034: } nkeynes@1034: nkeynes@1034: } nkeynes@1034: static int vmu_read_block(struct maple_device *dev, int function, unsigned int pt, nkeynes@1034: uint32_t block, unsigned int phase, nkeynes@1034: unsigned char *outbuf, unsigned int *buflen) nkeynes@1034: { nkeynes@1034: struct vmu_device *vmu = (struct vmu_device *)dev; nkeynes@1034: switch( function ) { nkeynes@1034: case MAPLE_FUNC_LCD: nkeynes@1034: if( pt == 0 && block == 0 ) { nkeynes@1034: *buflen = VMU_LCD_SIZE/4; nkeynes@1034: memcpy( outbuf, vmu->lcd_bitmap, VMU_LCD_SIZE/4 ); nkeynes@1034: } nkeynes@1034: return 0; nkeynes@1034: break; nkeynes@1034: case MAPLE_FUNC_MEMORY: nkeynes@1034: if( vmu->vol != NULL ) { nkeynes@1034: vmu_volume_read_block( vmu->vol, pt, block, outbuf ); nkeynes@1034: return 0; nkeynes@1034: } nkeynes@1034: // Else fallthrough for now nkeynes@1034: case MAPLE_FUNC_CLOCK: nkeynes@1034: return MAPLE_ERR_NO_RESPONSE; /* CHECKME */ nkeynes@1034: default: nkeynes@1034: return MAPLE_ERR_FUNC_UNSUP; nkeynes@1034: } nkeynes@1034: } nkeynes@1034: nkeynes@1034: static int vmu_write_block(struct maple_device *dev, int function, unsigned int pt, nkeynes@1034: uint32_t block, unsigned int phase, nkeynes@1034: unsigned char *inbuf, unsigned int buflen) nkeynes@1034: { nkeynes@1034: struct vmu_device *vmu = (struct vmu_device *)dev; nkeynes@1034: switch( function ) { nkeynes@1034: case MAPLE_FUNC_LCD: nkeynes@1034: if( pt == 0 && block == 0 && buflen == (VMU_LCD_SIZE/4) ) { nkeynes@1034: memcpy( vmu->lcd_bitmap, inbuf, VMU_LCD_SIZE ); nkeynes@1034: } nkeynes@1034: return 0; nkeynes@1034: break; nkeynes@1034: case MAPLE_FUNC_MEMORY: nkeynes@1034: if( vmu->vol != NULL && buflen == (VMU_PHASE_SIZE/4) ) { nkeynes@1034: vmu_volume_write_phase( vmu->vol, pt, block, phase, inbuf ); nkeynes@1034: return 0; nkeynes@1034: } nkeynes@1034: // Else fallthrough for now nkeynes@1034: case MAPLE_FUNC_CLOCK: nkeynes@1034: return MAPLE_ERR_NO_RESPONSE; /* CHECKME */ nkeynes@1034: default: nkeynes@1034: return MAPLE_ERR_FUNC_UNSUP; nkeynes@1034: } nkeynes@1034: }