trunk/src/mess/drivers/svmu.c
| r0 | r19052 | |
| 1 | /*************************************************************************** |
| 2 | |
| 3 | Sega Visual Memory Unit |
| 4 | |
| 5 | driver by Sandro Ronco |
| 6 | |
| 7 | TODO: |
| 8 | - add more bios versions |
| 9 | - layout for LCD symbols |
| 10 | - serial |
| 11 | |
| 12 | ****************************************************************************/ |
| 13 | |
| 14 | #include "emu.h" |
| 15 | #include "cpu/lc8670/lc8670.h" |
| 16 | #include "imagedev/snapquik.h" |
| 17 | #include "machine/intelfsh.h" |
| 18 | #include "sound/speaker.h" |
| 19 | #include "rendlay.h" |
| 20 | |
| 21 | #define PIXEL_SIZE 7 |
| 22 | #define PIXEL_DISTANCE 1 |
| 23 | |
| 24 | class svmu_state : public driver_device |
| 25 | { |
| 26 | public: |
| 27 | svmu_state(const machine_config &mconfig, device_type type, const char *tag) |
| 28 | : driver_device(mconfig, type, tag), |
| 29 | m_maincpu(*this, "maincpu"), |
| 30 | m_flash(*this, "flash"), |
| 31 | m_speaker(*this, SPEAKER_TAG) |
| 32 | { } |
| 33 | |
| 34 | required_device<lc8670_cpu_device> m_maincpu; |
| 35 | required_device<intelfsh8_device> m_flash; |
| 36 | required_device<device_t> m_speaker; |
| 37 | |
| 38 | virtual void palette_init(); |
| 39 | virtual void machine_reset(); |
| 40 | |
| 41 | DECLARE_WRITE8_MEMBER(page_w); |
| 42 | DECLARE_READ8_MEMBER(flash_r); |
| 43 | DECLARE_WRITE8_MEMBER(flash_w); |
| 44 | DECLARE_READ8_MEMBER(prog_r); |
| 45 | DECLARE_WRITE8_MEMBER(prog_w); |
| 46 | DECLARE_READ8_MEMBER(p1_r); |
| 47 | DECLARE_WRITE8_MEMBER(p1_w); |
| 48 | DECLARE_READ8_MEMBER(p7_r); |
| 49 | |
| 50 | private: |
| 51 | UINT8 * m_bios; |
| 52 | UINT8 m_page; |
| 53 | }; |
| 54 | |
| 55 | |
| 56 | WRITE8_MEMBER(svmu_state::page_w) |
| 57 | { |
| 58 | m_page = data & 0x03; |
| 59 | } |
| 60 | |
| 61 | READ8_MEMBER(svmu_state::flash_r) |
| 62 | { |
| 63 | return m_flash->read(offset); |
| 64 | } |
| 65 | |
| 66 | WRITE8_MEMBER(svmu_state::flash_w) |
| 67 | { |
| 68 | m_flash->write(offset, data); |
| 69 | } |
| 70 | |
| 71 | READ8_MEMBER(svmu_state::prog_r) |
| 72 | { |
| 73 | if (m_page == 1) |
| 74 | return m_flash->read(offset); |
| 75 | else if (m_page == 2) |
| 76 | return m_flash->read(0x10000 + offset); |
| 77 | else |
| 78 | return m_bios[offset]; |
| 79 | } |
| 80 | |
| 81 | WRITE8_MEMBER(svmu_state::prog_w) |
| 82 | { |
| 83 | if (m_page == 1) |
| 84 | m_flash->write(offset, data); |
| 85 | else if (m_page == 2) |
| 86 | m_flash->write(0x10000 + offset, data); |
| 87 | } |
| 88 | |
| 89 | /* |
| 90 | Port 1 |
| 91 | |
| 92 | x--- ---- PWM output |
| 93 | -x-- ---- BUZ |
| 94 | --x- ---- SCK1 |
| 95 | ---x ---- SB1 |
| 96 | ---- x--- SO1 |
| 97 | ---- -x-- SCK0 |
| 98 | ---- --x- SB0 |
| 99 | ---- ---x SO0 |
| 100 | |
| 101 | */ |
| 102 | |
| 103 | READ8_MEMBER(svmu_state::p1_r) |
| 104 | { |
| 105 | return 0; |
| 106 | } |
| 107 | |
| 108 | WRITE8_MEMBER(svmu_state::p1_w) |
| 109 | { |
| 110 | speaker_level_w(m_speaker, BIT(data, 7)); |
| 111 | } |
| 112 | |
| 113 | |
| 114 | /* |
| 115 | Port 7 |
| 116 | |
| 117 | ---- x--- ID1 |
| 118 | ---- -x-- ID0 |
| 119 | ---- --x- battery low voltage |
| 120 | ---- ---x 5V detection |
| 121 | */ |
| 122 | |
| 123 | READ8_MEMBER(svmu_state::p7_r) |
| 124 | { |
| 125 | return (ioport("BATTERY")->read()<<1); |
| 126 | } |
| 127 | |
| 128 | |
| 129 | static ADDRESS_MAP_START(svmu_mem, AS_PROGRAM, 8, svmu_state) |
| 130 | AM_RANGE( 0x0000, 0xffff ) AM_READWRITE(prog_r, prog_w) |
| 131 | ADDRESS_MAP_END |
| 132 | |
| 133 | static ADDRESS_MAP_START(svmu_io_mem, AS_IO, 8, svmu_state) |
| 134 | AM_RANGE( LC8670_PORT1, LC8670_PORT1 ) AM_READWRITE(p1_r, p1_w) |
| 135 | AM_RANGE( LC8670_PORT3, LC8670_PORT3 ) AM_READ_PORT("P3") |
| 136 | AM_RANGE( LC8670_PORT7, LC8670_PORT7 ) AM_READ(p7_r) |
| 137 | ADDRESS_MAP_END |
| 138 | |
| 139 | /* Input ports */ |
| 140 | static INPUT_PORTS_START( svmu ) |
| 141 | PORT_START( "P3" ) |
| 142 | PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("UP") PORT_CODE( KEYCODE_UP ) |
| 143 | PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("DOWN") PORT_CODE( KEYCODE_DOWN ) |
| 144 | PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("LEFT") PORT_CODE( KEYCODE_LEFT ) |
| 145 | PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("RIGHT") PORT_CODE( KEYCODE_RIGHT ) |
| 146 | PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("A") PORT_CODE( KEYCODE_A ) |
| 147 | PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("B") PORT_CODE( KEYCODE_B ) |
| 148 | PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("MODE") PORT_CODE( KEYCODE_M ) |
| 149 | PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("SLEEP") PORT_CODE( KEYCODE_S ) |
| 150 | PORT_START("BATTERY") |
| 151 | PORT_CONFNAME( 0x01, 0x01, "Battery" ) |
| 152 | PORT_CONFSETTING( 0x01, "Good" ) |
| 153 | PORT_CONFSETTING( 0x00, "Poor" ) |
| 154 | INPUT_PORTS_END |
| 155 | |
| 156 | void svmu_state::machine_reset() |
| 157 | { |
| 158 | m_bios = (UINT8*)(*memregion("bios")); |
| 159 | m_page = 0; |
| 160 | } |
| 161 | |
| 162 | void svmu_state::palette_init() |
| 163 | { |
| 164 | palette_set_color(machine(), 0, MAKE_RGB(138, 146, 148)); |
| 165 | palette_set_color(machine(), 1, MAKE_RGB(92, 83, 88)); |
| 166 | } |
| 167 | |
| 168 | static LC8670_LCD_UPDATE( svmu_lcd_update ) |
| 169 | { |
| 170 | if (lcd_enabled) |
| 171 | { |
| 172 | for (int y=0; y<32; y++) |
| 173 | for (int x=0; x<6; x++) |
| 174 | { |
| 175 | int gfx = vram[y*6 + x]; |
| 176 | |
| 177 | for (int b=0; b<8; b++) |
| 178 | bitmap.plot_box((x*8 + b) * (PIXEL_SIZE + PIXEL_DISTANCE), y * (PIXEL_SIZE + PIXEL_DISTANCE), PIXEL_SIZE, PIXEL_SIZE, BIT(gfx,7-b)); |
| 179 | } |
| 180 | |
| 181 | popmessage("%c %c %c %c\n", BIT(vram[0xc1],6) ? 'F' : ' ', // File icon |
| 182 | BIT(vram[0xc2],4) ? 'G' : ' ', // Game icon |
| 183 | BIT(vram[0xc3],2) ? 'C' : ' ', // Clock icon |
| 184 | BIT(vram[0xc4],0) ? 'A' : ' '); // Flash icon |
| 185 | } |
| 186 | else |
| 187 | { |
| 188 | bitmap.fill(0, cliprect); |
| 189 | } |
| 190 | |
| 191 | output_set_value("file_icon" , lcd_enabled ? BIT(vram[0xc1],6) : 0); |
| 192 | output_set_value("game_icon" , lcd_enabled ? BIT(vram[0xc2],4) : 0); |
| 193 | output_set_value("clock_icon", lcd_enabled ? BIT(vram[0xc3],2) : 0); |
| 194 | output_set_value("flash_icon", lcd_enabled ? BIT(vram[0xc4],0) : 0); |
| 195 | |
| 196 | return 0; |
| 197 | } |
| 198 | |
| 199 | |
| 200 | inline void vmufat_write_byte(UINT8* flash, UINT8 block, offs_t offset, UINT8 data) |
| 201 | { |
| 202 | flash[(block * 512) + offset] = data; |
| 203 | } |
| 204 | |
| 205 | inline void vmufat_write_word(UINT8* flash, UINT8 block, offs_t offset, UINT16 data) |
| 206 | { |
| 207 | // 16-bit data are stored in little endian |
| 208 | flash[(block * 512) + offset + 0] = data & 0xff; |
| 209 | flash[(block * 512) + offset + 1] = (data>>8) & 0xff; |
| 210 | } |
| 211 | |
| 212 | static QUICKLOAD_LOAD( svmu ) |
| 213 | { |
| 214 | running_machine &machine = image.device().machine(); |
| 215 | svmu_state *state = machine.driver_data<svmu_state>(); |
| 216 | UINT32 size = image.length(); |
| 217 | UINT8 *flash = (UINT8*)state->m_flash->space().get_read_ptr(0); |
| 218 | |
| 219 | image.fread(flash, size); |
| 220 | |
| 221 | // verify if image is already a valid VMUFAT file system |
| 222 | bool valid_vmufat = true; |
| 223 | if (size == 0x20000) |
| 224 | { |
| 225 | for (int i=0; i<0x10; i++) |
| 226 | if (flash[255 * 512 + i] != 0x55) |
| 227 | { |
| 228 | valid_vmufat = false; |
| 229 | break; |
| 230 | } |
| 231 | } |
| 232 | else |
| 233 | { |
| 234 | valid_vmufat = false; |
| 235 | } |
| 236 | |
| 237 | if (!valid_vmufat) |
| 238 | { |
| 239 | // more info about the VMUFAT here: http://mc.pp.se/dc/vms/flashmem.html |
| 240 | |
| 241 | //-------------------------------- Formatting -------------------------------- |
| 242 | memset(flash + 241*512, 0, 15*512); // clears the last 15 blocks that contain file system information |
| 243 | |
| 244 | for (int i=0; i<0x10; i++) |
| 245 | vmufat_write_byte(flash, 255, i, 0x55); // first 16 bytes should be 0x55 to indicate a properly formatted card |
| 246 | |
| 247 | vmufat_write_byte(flash, 255, 0x10, 0x00); // custom VMS colour (1 = use custom colours, 0 = standard colour) |
| 248 | vmufat_write_byte(flash, 255, 0x11, 0x00); // VMS colour blue component |
| 249 | vmufat_write_byte(flash, 255, 0x12, 0x00); // VMS colour green component |
| 250 | vmufat_write_byte(flash, 255, 0x13, 0x00); // VMS colour red component |
| 251 | vmufat_write_byte(flash, 255, 0x14, 0x00); // VMS colour alpha component |
| 252 | vmufat_write_byte(flash, 255, 0x30, 0x19); // Century (BCD) |
| 253 | vmufat_write_byte(flash, 255, 0x31, 0x99); // Year (BCD) |
| 254 | vmufat_write_byte(flash, 255, 0x32, 0x01); // Month (BCD) |
| 255 | vmufat_write_byte(flash, 255, 0x33, 0x01); // Day (BCD) |
| 256 | vmufat_write_byte(flash, 255, 0x34, 0x00); // Hour (BCD) |
| 257 | vmufat_write_byte(flash, 255, 0x35, 0x00); // Minute (BCD) |
| 258 | vmufat_write_byte(flash, 255, 0x36, 0x00); // Second (BCD) |
| 259 | vmufat_write_byte(flash, 255, 0x37, 0x00); // Day of week (0 = Monday, 6 = Sunday) |
| 260 | vmufat_write_word(flash, 255, 0x44, 0x00ff); // location of Root |
| 261 | vmufat_write_word(flash, 255, 0x46, 0x00fe); // location of FAT (254) |
| 262 | vmufat_write_word(flash, 255, 0x48, 0x0001); // size of FAT in blocks (1) |
| 263 | vmufat_write_word(flash, 255, 0x4a, 0x00fd); // location of Directory (253) |
| 264 | vmufat_write_word(flash, 255, 0x4c, 0x000d); // size of Directory in blocks (13) |
| 265 | vmufat_write_word(flash, 255, 0x4e, 0x0000); // icon shape for this VMS (0-123) |
| 266 | vmufat_write_word(flash, 255, 0x50, 0x00c8); // number of user blocks (200) |
| 267 | |
| 268 | for (int i=0; i<256; i++) |
| 269 | vmufat_write_word(flash, 254, i<<1, 0xfffc); // marks all blocks as unallocated |
| 270 | |
| 271 | for (int i=253; i>241; --i) |
| 272 | vmufat_write_word(flash, 254, i<<1, i - 1); // marsk all Directory blocks as allocate |
| 273 | |
| 274 | vmufat_write_word(flash, 254, 0x1e2, 0xfffa); // marks last Directory block |
| 275 | vmufat_write_word(flash, 254, 0x1fc, 0xfffa); // marks FAT block as allocated |
| 276 | vmufat_write_word(flash, 254, 0x1fe, 0xfffa); // marks Root block as allocated |
| 277 | |
| 278 | //-------------------------------- Create the vms file -------------------------------- |
| 279 | int vms_blocks = (size / 512) + (size & 0x1ff ? 1 : 0); // number of blocks required for store the vms file |
| 280 | |
| 281 | for (int i=0; i<vms_blocks - 1; i++) |
| 282 | vmufat_write_word(flash, 254, i<<1, i + 1); // marks blocks where the file is allocated |
| 283 | |
| 284 | vmufat_write_word(flash, 254, (vms_blocks-1)<<1, 0xfffa); // last block for this file |
| 285 | |
| 286 | vmufat_write_byte(flash, 253, 0x00, 0xcc); // file type (0x00 = no file, 0x33 = data, 0xcc = game) |
| 287 | vmufat_write_byte(flash, 253, 0x01, 0x00); // copy protect (0x00 = no, 0xff = yes) |
| 288 | vmufat_write_word(flash, 253, 0x02, 0x0000); // location of first file block |
| 289 | |
| 290 | const char *vms_filename = image.basename_noext(); |
| 291 | for (int i=0; i<12; i++) |
| 292 | { |
| 293 | if (i < strlen(vms_filename)) |
| 294 | vmufat_write_byte(flash, 253, i + 4, vms_filename[i]); // 12 bytes filename |
| 295 | else |
| 296 | vmufat_write_byte(flash, 253, i + 4, 0x20); // space padded |
| 297 | } |
| 298 | |
| 299 | vmufat_write_byte(flash, 253, 0x10, 0x19); // Century (BCD) |
| 300 | vmufat_write_byte(flash, 253, 0x11, 0x99); // Year (BCD) |
| 301 | vmufat_write_byte(flash, 253, 0x12, 0x01); // Month (BCD) |
| 302 | vmufat_write_byte(flash, 253, 0x13, 0x01); // Day (BCD) |
| 303 | vmufat_write_byte(flash, 253, 0x14, 0x00); // Hour (BCD) |
| 304 | vmufat_write_byte(flash, 253, 0x15, 0x00); // Minute (BCD) |
| 305 | vmufat_write_byte(flash, 253, 0x16, 0x00); // Second (BCD) |
| 306 | vmufat_write_byte(flash, 253, 0x17, 0x00); // Day of week (0 = Monday, 6 = Sunday) |
| 307 | vmufat_write_word(flash, 253, 0x18, vms_blocks); // file size (in blocks) |
| 308 | vmufat_write_word(flash, 253, 0x1a, 0x0001); // offset of header (in blocks) from file start |
| 309 | } |
| 310 | |
| 311 | return IMAGE_INIT_PASS; |
| 312 | } |
| 313 | |
| 314 | |
| 315 | static MACHINE_CONFIG_START( svmu, svmu_state ) |
| 316 | /* basic machine hardware */ |
| 317 | MCFG_CPU_ADD("maincpu", LC8670, XTAL_32_768kHz) |
| 318 | MCFG_CPU_PROGRAM_MAP(svmu_mem) |
| 319 | MCFG_CPU_IO_MAP(svmu_io_mem) |
| 320 | |
| 321 | /* specific LC8670 configurations */ |
| 322 | MCFG_LC8670_SET_CLOCK_SOURCES(XTAL_32_768kHz, 600000, XTAL_6MHz) // tolerance range of the RC oscillator is 600kHz to 1200kHz |
| 323 | MCFG_LC8670_BANKSWITCH_CB(WRITE8(svmu_state, page_w)) |
| 324 | MCFG_LC8670_LCD_UPDATE_CB(svmu_lcd_update) |
| 325 | |
| 326 | /* video hardware */ |
| 327 | MCFG_SCREEN_ADD("screen", LCD) |
| 328 | MCFG_SCREEN_REFRESH_RATE(60) |
| 329 | MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) // not accurate |
| 330 | MCFG_SCREEN_SIZE(48 * (PIXEL_SIZE + PIXEL_DISTANCE), 32 * (PIXEL_SIZE + PIXEL_DISTANCE)) |
| 331 | MCFG_SCREEN_VISIBLE_AREA(0, 48*(PIXEL_SIZE + PIXEL_DISTANCE) - 1, 0, 32*(PIXEL_SIZE + PIXEL_DISTANCE) - 1) |
| 332 | MCFG_SCREEN_UPDATE_DEVICE("maincpu", lc8670_cpu_device, screen_update) |
| 333 | MCFG_DEFAULT_LAYOUT(layout_lcd) |
| 334 | MCFG_PALETTE_LENGTH(2) |
| 335 | |
| 336 | /* sound hardware */ |
| 337 | MCFG_SPEAKER_STANDARD_MONO("mono") |
| 338 | MCFG_SOUND_ADD(SPEAKER_TAG, SPEAKER_SOUND, 0) |
| 339 | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) |
| 340 | |
| 341 | /* devices */ |
| 342 | MCFG_ATMEL_29C010_ADD("flash") |
| 343 | MCFG_QUICKLOAD_ADD("quickload", svmu, "vms,bin", 0) |
| 344 | MACHINE_CONFIG_END |
| 345 | |
| 346 | |
| 347 | /* ROM definition */ |
| 348 | ROM_START( svmu ) |
| 349 | ROM_REGION( 0x10000, "bios", 0 ) |
| 350 | // these ROMs come from the Sega Katana SDK and are scrambled, a simple way to restore it is to remove the first 4 bytes and xor the whole file for the xor key. |
| 351 | ROM_SYSTEM_BIOS(0, "jp1004", "Japan v1.004") |
| 352 | ROMX_LOAD( "fbios.bin", 0x0000, 0x10000, CRC(8e0f867a) SHA1(dc2fa2963138a1049a43f7f36439ad0a416ee8b4), ROM_BIOS(1)) // from Sega Katana SDK (original file: fbios.sbf, CRC: c7c77b3c, xor key: 0x37) |
| 353 | ROM_SYSTEM_BIOS(1, "jp1004q", "Japan v1.004 (quick start)") // automatically boot the first game found in the flash |
| 354 | ROMX_LOAD( "qbios.bin", 0x0000, 0x10000, CRC(395e25f2) SHA1(37dea034322b5b80b35b2de784298d32c71ba7a3), ROM_BIOS(2)) // from Sega Katana SDK (original file: qbios.sbf, CRC: eed5524c, xor key: 0x43) |
| 355 | ROM_END |
| 356 | |
| 357 | |
| 358 | /* Driver */ |
| 359 | |
| 360 | /* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME FLAGS */ |
| 361 | COMP( 1998, svmu, 0, 0, svmu , svmu , driver_device, 0, "Sega", "Visual Memory Unit", GAME_NOT_WORKING | GAME_NO_SOUND) |