src/mess/machine/upd1990a.c
| r4664 | r4665 | |
| 1 | | /********************************************************************* |
| 1 | /********************************************************************** |
| 2 | 2 | |
| 3 | | upd1990a.c |
| 3 | NEC uPD1990AC Serial I/O Calendar & Clock emulation |
| 4 | 4 | |
| 5 | | NEC uPD1990AC CMOS Digital Integrated Circuit |
| 6 | | Serial I/O Calendar & Clock |
| 7 | | CMOS LSI |
| 5 | Copyright MESS Team. |
| 6 | Visit http://mamedev.org for licensing and usage restrictions. |
| 8 | 7 | |
| 9 | 8 | *********************************************************************/ |
| 10 | 9 | |
| r4664 | r4665 | |
| 12 | 11 | |
| 13 | 12 | TODO: |
| 14 | 13 | |
| 15 | | - set clock mode |
| 16 | 14 | - test mode |
| 17 | | - clock advance (currently it just reads the system time) |
| 18 | | - divide frequencies from chip clock |
| 19 | 15 | |
| 20 | 16 | */ |
| 21 | 17 | |
| r4664 | r4665 | |
| 92 | 88 | return ((val / 10) << 4) | (val % 10); |
| 93 | 89 | } |
| 94 | 90 | |
| 91 | INLINE int bcd_to_integer(UINT8 val) |
| 92 | { |
| 93 | return (((val & 0xf0) >> 4) * 10) + (val & 0x0f); |
| 94 | } |
| 95 | |
| 95 | 96 | /*************************************************************************** |
| 96 | 97 | IMPLEMENTATION |
| 97 | 98 | ***************************************************************************/ |
| r4664 | r4665 | |
| 146 | 147 | |
| 147 | 148 | /* 1 Hz data out pulse */ |
| 148 | 149 | upd1990a->data_out = 1; |
| 149 | | timer_adjust_periodic(upd1990a->data_out_timer, attotime_zero, 0, ATTOTIME_IN_HZ(1*2)); |
| 150 | timer_adjust_periodic(upd1990a->data_out_timer, attotime_zero, 0, ATTOTIME_IN_HZ((device->clock/32768)*2)); |
| 150 | 151 | |
| 151 | 152 | /* 64 Hz time pulse */ |
| 152 | | timer_adjust_periodic(upd1990a->tp_timer, attotime_zero, 0, ATTOTIME_IN_HZ(64*2)); |
| 153 | timer_adjust_periodic(upd1990a->tp_timer, attotime_zero, 0, ATTOTIME_IN_HZ((device->clock/512)*2)); |
| 153 | 154 | break; |
| 154 | 155 | |
| 155 | 156 | case UPD1990A_MODE_SHIFT: |
| 156 | 157 | if (LOG) logerror("UPD1990A Shift Mode\n"); |
| 158 | |
| 157 | 159 | /* enable time counter */ |
| 158 | 160 | timer_enable(upd1990a->clock_timer, 1); |
| 159 | 161 | |
| r4664 | r4665 | |
| 165 | 167 | devcb_call_write_line(&upd1990a->out_data_func, upd1990a->data_out); |
| 166 | 168 | |
| 167 | 169 | /* 32 Hz time pulse */ |
| 168 | | timer_adjust_periodic(upd1990a->tp_timer, attotime_zero, 0, ATTOTIME_IN_HZ(32*2)); |
| 170 | timer_adjust_periodic(upd1990a->tp_timer, attotime_zero, 0, ATTOTIME_IN_HZ((device->clock/1024)*2)); |
| 169 | 171 | break; |
| 170 | 172 | |
| 171 | 173 | case UPD1990A_MODE_TIME_SET: |
| 172 | 174 | { |
| 173 | 175 | int i; |
| 174 | 176 | |
| 175 | | if (LOG) logerror("UPD1990A Time Set Mode not supported!\n"); |
| 177 | if (LOG) logerror("UPD1990A Time Set Mode\n"); |
| 176 | 178 | if (LOG) logerror("UPD1990A Shift Register %02x%02x%02x%02x%02x\n", upd1990a->shift_reg[4], upd1990a->shift_reg[3], upd1990a->shift_reg[2], upd1990a->shift_reg[1], upd1990a->shift_reg[0]); |
| 177 | 179 | |
| 178 | 180 | /* disable time counter */ |
| r4664 | r4665 | |
| 192 | 194 | } |
| 193 | 195 | |
| 194 | 196 | /* 32 Hz time pulse */ |
| 195 | | timer_adjust_periodic(upd1990a->tp_timer, attotime_zero, 0, ATTOTIME_IN_HZ(32*2)); |
| 197 | timer_adjust_periodic(upd1990a->tp_timer, attotime_zero, 0, ATTOTIME_IN_HZ((device->clock/1024)*2)); |
| 196 | 198 | } |
| 197 | 199 | break; |
| 198 | 200 | |
| 199 | 201 | case UPD1990A_MODE_TIME_READ: |
| 200 | 202 | { |
| 201 | | mame_system_time curtime, *systime = &curtime; |
| 202 | 203 | int i; |
| 203 | 204 | |
| 204 | 205 | if (LOG) logerror("UPD1990A Time Read Mode\n"); |
| r4664 | r4665 | |
| 206 | 207 | /* enable time counter */ |
| 207 | 208 | timer_enable(upd1990a->clock_timer, 1); |
| 208 | 209 | |
| 209 | | /* HACK: load time counter from system time */ |
| 210 | | mame_get_current_datetime(device->machine, &curtime); |
| 211 | | |
| 212 | | upd1990a->time_counter[0] = convert_to_bcd(systime->local_time.second); |
| 213 | | upd1990a->time_counter[1] = convert_to_bcd(systime->local_time.minute); |
| 214 | | upd1990a->time_counter[2] = convert_to_bcd(systime->local_time.hour); |
| 215 | | upd1990a->time_counter[3] = convert_to_bcd(systime->local_time.mday); |
| 216 | | upd1990a->time_counter[4] = systime->local_time.weekday; |
| 217 | | upd1990a->time_counter[4] |= (systime->local_time.month + 1) << 4; |
| 218 | | |
| 219 | 210 | /* load time counter data into shift register */ |
| 220 | 211 | for (i = 0; i < 5; i++) |
| 221 | 212 | { |
| r4664 | r4665 | |
| 226 | 217 | |
| 227 | 218 | /* 512 Hz data out pulse */ |
| 228 | 219 | upd1990a->data_out = 1; |
| 229 | | timer_adjust_periodic(upd1990a->data_out_timer, attotime_zero, 0, ATTOTIME_IN_HZ(512*2)); |
| 220 | timer_adjust_periodic(upd1990a->data_out_timer, attotime_zero, 0, ATTOTIME_IN_HZ((device->clock/64)*2)); |
| 230 | 221 | |
| 231 | 222 | /* 32 Hz time pulse */ |
| 232 | | timer_adjust_periodic(upd1990a->tp_timer, attotime_zero, 0, ATTOTIME_IN_HZ(32*2)); |
| 223 | timer_adjust_periodic(upd1990a->tp_timer, attotime_zero, 0, ATTOTIME_IN_HZ((device->clock/1024)*2)); |
| 233 | 224 | } |
| 234 | 225 | break; |
| 235 | 226 | |
| r4664 | r4665 | |
| 237 | 228 | if (LOG) logerror("UPD1990A TP = 64 Hz Set Mode\n"); |
| 238 | 229 | |
| 239 | 230 | /* 64 Hz time pulse */ |
| 240 | | timer_adjust_periodic(upd1990a->tp_timer, attotime_zero, 0, ATTOTIME_IN_HZ(64*2)); |
| 231 | timer_adjust_periodic(upd1990a->tp_timer, attotime_zero, 0, ATTOTIME_IN_HZ((device->clock/512)*2)); |
| 241 | 232 | break; |
| 242 | 233 | |
| 243 | 234 | case UPD1990A_MODE_TP_256HZ_SET: |
| 244 | 235 | if (LOG) logerror("UPD1990A TP = 256 Hz Set Mode\n"); |
| 245 | 236 | |
| 246 | 237 | /* 256 Hz time pulse */ |
| 247 | | timer_adjust_periodic(upd1990a->tp_timer, attotime_zero, 0, ATTOTIME_IN_HZ(256*2)); |
| 238 | timer_adjust_periodic(upd1990a->tp_timer, attotime_zero, 0, ATTOTIME_IN_HZ((device->clock/128)*2)); |
| 248 | 239 | break; |
| 249 | 240 | |
| 250 | 241 | case UPD1990A_MODE_TP_2048HZ_SET: |
| 251 | 242 | if (LOG) logerror("UPD1990A TP = 2048 Hz Set Mode\n"); |
| 252 | 243 | |
| 253 | 244 | /* 2048 Hz time pulse */ |
| 254 | | timer_adjust_periodic(upd1990a->tp_timer, attotime_zero, 0, ATTOTIME_IN_HZ(2048*2)); |
| 245 | timer_adjust_periodic(upd1990a->tp_timer, attotime_zero, 0, ATTOTIME_IN_HZ((device->clock/16)*2)); |
| 255 | 246 | break; |
| 256 | 247 | |
| 257 | 248 | case UPD1990A_MODE_TEST: |
| 258 | 249 | if (LOG) logerror("UPD1990A Test Mode not supported!\n"); |
| 250 | |
| 251 | if (upd1990a->oe) |
| 252 | { |
| 253 | /* time counter is advanced at 1024 Hz from "Second" counter input */ |
| 254 | } |
| 255 | else |
| 256 | { |
| 257 | /* each counter is advanced at 1024 Hz in parallel, overflow carry does not affect next counter */ |
| 258 | } |
| 259 | |
| 259 | 260 | break; |
| 260 | 261 | } |
| 261 | 262 | } |
| r4664 | r4665 | |
| 294 | 295 | { |
| 295 | 296 | upd1990a->data_out = BIT(upd1990a->shift_reg[0], 0); |
| 296 | 297 | |
| 297 | | logerror("UPD1990A DATA OUT %u\n", upd1990a->data_out); |
| 298 | if (LOG) logerror("UPD1990A DATA OUT %u\n", upd1990a->data_out); |
| 299 | |
| 298 | 300 | devcb_call_write_line(&upd1990a->out_data_func, upd1990a->data_out); |
| 299 | | |
| 300 | 301 | } |
| 301 | | |
| 302 | 302 | } |
| 303 | 303 | } |
| 304 | 304 | |
| r4664 | r4665 | |
| 358 | 358 | } |
| 359 | 359 | |
| 360 | 360 | /*------------------------------------------------- |
| 361 | advance_seconds - advance seconds counter |
| 362 | -------------------------------------------------*/ |
| 363 | |
| 364 | static void advance_seconds(upd1990a_t *upd1990a) |
| 365 | { |
| 366 | int days_per_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; |
| 367 | |
| 368 | int seconds = bcd_to_integer(upd1990a->time_counter[0]); |
| 369 | int minutes = bcd_to_integer(upd1990a->time_counter[1]); |
| 370 | int hours = bcd_to_integer(upd1990a->time_counter[2]); |
| 371 | int days = bcd_to_integer(upd1990a->time_counter[3]); |
| 372 | int day_of_week = upd1990a->time_counter[4] & 0x0f; |
| 373 | int month = (upd1990a->time_counter[4] & 0xf0) >> 4; |
| 374 | |
| 375 | seconds++; |
| 376 | |
| 377 | if (seconds > 59) |
| 378 | { |
| 379 | seconds = 0; |
| 380 | minutes++; |
| 381 | } |
| 382 | |
| 383 | if (minutes > 59) |
| 384 | { |
| 385 | minutes = 0; |
| 386 | hours++; |
| 387 | } |
| 388 | |
| 389 | if (hours > 23) |
| 390 | { |
| 391 | hours = 0; |
| 392 | days++; |
| 393 | day_of_week++; |
| 394 | } |
| 395 | |
| 396 | if (day_of_week > 6) |
| 397 | { |
| 398 | day_of_week++; |
| 399 | } |
| 400 | |
| 401 | if (days > days_per_month[month - 1]) |
| 402 | { |
| 403 | days = 1; |
| 404 | month++; |
| 405 | } |
| 406 | |
| 407 | if (month > 12) |
| 408 | { |
| 409 | month = 1; |
| 410 | } |
| 411 | |
| 412 | upd1990a->time_counter[0] = convert_to_bcd(seconds); |
| 413 | upd1990a->time_counter[1] = convert_to_bcd(minutes); |
| 414 | upd1990a->time_counter[2] = convert_to_bcd(hours); |
| 415 | upd1990a->time_counter[3] = convert_to_bcd(days); |
| 416 | upd1990a->time_counter[4] = (month << 4) | day_of_week; |
| 417 | } |
| 418 | |
| 419 | /*------------------------------------------------- |
| 361 | 420 | TIMER_CALLBACK( clock_tick ) |
| 362 | 421 | -------------------------------------------------*/ |
| 363 | 422 | |
| 364 | 423 | static TIMER_CALLBACK( clock_tick ) |
| 365 | 424 | { |
| 366 | | // const device_config *device = ptr; |
| 367 | | // upd1990a_t *upd1990a = get_safe_token(device); |
| 425 | const device_config *device = ptr; |
| 426 | upd1990a_t *upd1990a = get_safe_token(device); |
| 368 | 427 | |
| 369 | | // TODO: advance clock |
| 428 | advance_seconds(upd1990a); |
| 370 | 429 | } |
| 371 | 430 | |
| 372 | 431 | /*------------------------------------------------- |
| r4664 | r4665 | |
| 394 | 453 | const device_config *device = ptr; |
| 395 | 454 | upd1990a_t *upd1990a = get_safe_token(device); |
| 396 | 455 | |
| 397 | | logerror("UPD1990A DATA OUT TICK %u\n", upd1990a->data_out); |
| 456 | if (LOG) logerror("UPD1990A DATA OUT TICK %u\n", upd1990a->data_out); |
| 398 | 457 | |
| 399 | 458 | devcb_call_write_line(&upd1990a->out_data_func, upd1990a->data_out); |
| 400 | 459 | |
| r4664 | r4665 | |
| 416 | 475 | |
| 417 | 476 | /* create the timers */ |
| 418 | 477 | upd1990a->clock_timer = timer_alloc(device->machine, clock_tick, (void *)device); |
| 419 | | timer_adjust_periodic(upd1990a->clock_timer, attotime_zero, 0, ATTOTIME_IN_HZ(device->clock / 32768)); |
| 478 | timer_adjust_periodic(upd1990a->clock_timer, attotime_zero, 0, ATTOTIME_IN_HZ(1)); |
| 420 | 479 | |
| 421 | 480 | upd1990a->tp_timer = timer_alloc(device->machine, tp_tick, (void *)device); |
| 422 | 481 | |
| r4664 | r4665 | |
| 435 | 494 | state_save_register_global(device->machine, upd1990a->tp); |
| 436 | 495 | } |
| 437 | 496 | |
| 497 | static DEVICE_RESET( upd1990a ) |
| 498 | { |
| 499 | upd1990a_t *upd1990a = get_safe_token(device); |
| 500 | |
| 501 | mame_system_time curtime, *systime = &curtime; |
| 502 | |
| 503 | mame_get_current_datetime(device->machine, &curtime); |
| 504 | |
| 505 | /* HACK: load time counter from system time */ |
| 506 | upd1990a->time_counter[0] = convert_to_bcd(systime->local_time.second); |
| 507 | upd1990a->time_counter[1] = convert_to_bcd(systime->local_time.minute); |
| 508 | upd1990a->time_counter[2] = convert_to_bcd(systime->local_time.hour); |
| 509 | upd1990a->time_counter[3] = convert_to_bcd(systime->local_time.mday); |
| 510 | upd1990a->time_counter[4] = systime->local_time.weekday; |
| 511 | upd1990a->time_counter[4] |= (systime->local_time.month + 1) << 4; |
| 512 | } |
| 513 | |
| 438 | 514 | /*------------------------------------------------- |
| 439 | 515 | DEVICE_GET_INFO( upd1990a ) |
| 440 | 516 | -------------------------------------------------*/ |
| r4664 | r4665 | |
| 451 | 527 | /* --- the following bits of info are returned as pointers to data or functions --- */ |
| 452 | 528 | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(upd1990a); break; |
| 453 | 529 | case DEVINFO_FCT_STOP: /* Nothing */ break; |
| 454 | | case DEVINFO_FCT_RESET: /* Nothing */ break; |
| 530 | case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(upd1990a); break; |
| 455 | 531 | |
| 456 | 532 | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 457 | 533 | case DEVINFO_STR_NAME: strcpy(info->s, "NEC uPD1990AC"); break; |