trunk/src/mame/machine/snes.c
| r17641 | r17642 | |
| 291 | 291 | state->m_scanline_timer->adjust(machine.primary_screen->time_until_pos(nextscan)); |
| 292 | 292 | } |
| 293 | 293 | |
| 294 | | /* FIXME: multiplication should take 8 CPU cycles & division 16 CPU cycles, but |
| 295 | | using these timers breaks e.g. Chrono Trigger intro and Super Tennis gameplay. |
| 296 | | On the other hand, timers are needed for the translation of Breath of Fire 2 |
| 297 | | to work. More weirdness: we might need to leave 8 CPU cycles for division at |
| 298 | | first, since using 16 produces bugs (see e.g. Triforce pieces in Zelda 3 intro) */ |
| 299 | 294 | |
| 300 | | static TIMER_CALLBACK(snes_div_callback) |
| 301 | | { |
| 302 | | UINT16 value, dividend, remainder; |
| 303 | | dividend = remainder = 0; |
| 304 | | value = (snes_ram[WRDIVH] << 8) + snes_ram[WRDIVL]; |
| 305 | | if (snes_ram[WRDVDD] > 0) |
| 306 | | { |
| 307 | | dividend = value / snes_ram[WRDVDD]; |
| 308 | | remainder = value % snes_ram[WRDVDD]; |
| 309 | | } |
| 310 | | else |
| 311 | | { |
| 312 | | dividend = 0xffff; |
| 313 | | remainder = value; |
| 314 | | } |
| 315 | | snes_ram[RDDIVL] = dividend & 0xff; |
| 316 | | snes_ram[RDDIVH] = (dividend >> 8) & 0xff; |
| 317 | | snes_ram[RDMPYL] = remainder & 0xff; |
| 318 | | snes_ram[RDMPYH] = (remainder >> 8) & 0xff; |
| 319 | | } |
| 320 | | |
| 321 | | |
| 322 | | static TIMER_CALLBACK(snes_mult_callback) |
| 323 | | { |
| 324 | | UINT32 c = snes_ram[WRMPYA] * snes_ram[WRMPYB]; |
| 325 | | snes_ram[RDMPYL] = c & 0xff; |
| 326 | | snes_ram[RDMPYH] = (c >> 8) & 0xff; |
| 327 | | } |
| 328 | | |
| 329 | 295 | /************************************* |
| 330 | 296 | |
| 331 | 297 | Input Handlers |
| r17641 | r17642 | |
| 555 | 521 | return (snes_ram[offset] & 0xc1) | (snes_open_bus_r(space, 0) & 0x3e); |
| 556 | 522 | case RDIO: /* Programmable I/O port - echos back what's written to WRIO */ |
| 557 | 523 | return snes_ram[WRIO]; |
| 558 | | case RDDIVL: /* Quotient of divide result (low) */ |
| 559 | | case RDDIVH: /* Quotient of divide result (high) */ |
| 560 | | case RDMPYL: /* Product/Remainder of mult/div result (low) */ |
| 561 | | case RDMPYH: /* Product/Remainder of mult/div result (high) */ |
| 562 | | return snes_ram[offset]; |
| 563 | 524 | case JOY1L: /* Joypad 1 status register (low) */ |
| 564 | 525 | if(state->m_is_nss && state->m_input_disabled) |
| 565 | 526 | return 0; |
| r17641 | r17642 | |
| 740 | 701 | snes_latch_counters(space->machine()); |
| 741 | 702 | } |
| 742 | 703 | break; |
| 743 | | case WRMPYA: /* Multiplier A */ |
| 744 | | break; |
| 745 | | case WRMPYB: /* Multiplier B */ |
| 746 | | snes_ram[WRMPYB] = data; |
| 747 | | // state->m_mult_timer->adjust(state->m_maincpu->cycles_to_attotime(8)); |
| 748 | | { |
| 749 | | UINT32 c = snes_ram[WRMPYA] * snes_ram[WRMPYB]; |
| 750 | | snes_ram[RDMPYL] = c & 0xff; |
| 751 | | snes_ram[RDMPYH] = (c >> 8) & 0xff; |
| 752 | | } |
| 753 | | break; |
| 754 | | case WRDIVL: /* Dividend (low) */ |
| 755 | | case WRDIVH: /* Dividend (high) */ |
| 756 | | break; |
| 757 | | case WRDVDD: /* Divisor */ |
| 758 | | snes_ram[WRDVDD] = data; |
| 759 | | // state->m_div_timer->adjust(state->m_maincpu->cycles_to_attotime(16)); |
| 760 | | { |
| 761 | | UINT16 value, dividend, remainder; |
| 762 | | value = (snes_ram[WRDIVH] << 8) + snes_ram[WRDIVL]; |
| 763 | | if (snes_ram[WRDVDD] > 0) |
| 764 | | { |
| 765 | | dividend = value / data; |
| 766 | | remainder = value % data; |
| 767 | | } |
| 768 | | else |
| 769 | | { |
| 770 | | dividend = 0xffff; |
| 771 | | remainder = value; |
| 772 | | } |
| 773 | | snes_ram[RDDIVL] = dividend & 0xff; |
| 774 | | snes_ram[RDDIVH] = (dividend >> 8) & 0xff; |
| 775 | | snes_ram[RDMPYL] = remainder & 0xff; |
| 776 | | snes_ram[RDMPYH] = (remainder >> 8) & 0xff; |
| 777 | | } |
| 778 | | break; |
| 779 | 704 | case HTIMEL: /* H-Count timer settings (low) */ |
| 780 | 705 | state->m_htime = (state->m_htime & 0xff00) | (data << 0); |
| 781 | 706 | state->m_htime &= 0x1ff; |
| r17641 | r17642 | |
| 813 | 738 | case MPYM: /* Multiplication result (mid) */ |
| 814 | 739 | case MPYH: /* Multiplication result (high) */ |
| 815 | 740 | case RDIO: |
| 816 | | case RDDIVL: |
| 817 | | case RDDIVH: |
| 818 | | case RDMPYL: |
| 819 | | case RDMPYH: |
| 741 | // case RDDIVL: |
| 742 | // case RDDIVH: |
| 743 | // case RDMPYL: |
| 744 | // case RDMPYH: |
| 820 | 745 | case JOY1L: |
| 821 | 746 | case JOY1H: |
| 822 | 747 | case JOY2L: |
| r17641 | r17642 | |
| 1620 | 1545 | state->m_nmi_timer->adjust(attotime::never); |
| 1621 | 1546 | state->m_hirq_timer = machine.scheduler().timer_alloc(FUNC(snes_hirq_tick_callback)); |
| 1622 | 1547 | state->m_hirq_timer->adjust(attotime::never); |
| 1623 | | state->m_div_timer = machine.scheduler().timer_alloc(FUNC(snes_div_callback)); |
| 1624 | | state->m_div_timer->adjust(attotime::never); |
| 1625 | | state->m_mult_timer = machine.scheduler().timer_alloc(FUNC(snes_mult_callback)); |
| 1626 | | state->m_mult_timer->adjust(attotime::never); |
| 1548 | //state->m_div_timer = machine.scheduler().timer_alloc(FUNC(snes_div_callback)); |
| 1549 | //state->m_div_timer->adjust(attotime::never); |
| 1550 | //state->m_mult_timer = machine.scheduler().timer_alloc(FUNC(snes_mult_callback)); |
| 1551 | //state->m_mult_timer->adjust(attotime::never); |
| 1627 | 1552 | state->m_io_timer = machine.scheduler().timer_alloc(FUNC(snes_update_io)); |
| 1628 | 1553 | state->m_io_timer->adjust(attotime::never); |
| 1629 | 1554 | |
| r17641 | r17642 | |
| 1762 | 1687 | |
| 1763 | 1688 | // power-on sets these registers like this |
| 1764 | 1689 | snes_ram[WRIO] = 0xff; |
| 1765 | | snes_ram[WRMPYA] = 0xff; |
| 1766 | | snes_ram[WRDIVL] = 0xff; |
| 1767 | | snes_ram[WRDIVH] = 0xff; |
| 1690 | // snes_ram[WRMPYA] = 0xff; |
| 1691 | // snes_ram[WRDIVL] = 0xff; |
| 1692 | // snes_ram[WRDIVH] = 0xff; |
| 1768 | 1693 | |
| 1769 | 1694 | switch (state->m_has_addon_chip) |
| 1770 | 1695 | { |
trunk/src/emu/cpu/g65816/g65816.c
| r17641 | r17642 | |
| 571 | 571 | CPU_RESET_CALL(g65816); |
| 572 | 572 | |
| 573 | 573 | cpustate->fastROM = 0; |
| 574 | cpustate->wrmpya = 0xff; |
| 575 | cpustate->wrdiv = 0xffff; |
| 574 | 576 | } |
| 575 | 577 | |
| 578 | /* TODO: multiplication / division should actually occur inside CPU_EXECUTE */ |
| 579 | /* (Old note, for reference): multiplication should take 8 CPU cycles & |
| 580 | division 16 CPU cycles, but using these timers breaks e.g. Chrono Trigger |
| 581 | intro and Super Tennis gameplay. On the other hand, timers are needed for the |
| 582 | translation of Breath of Fire 2 to work. More weirdness: we might need to leave |
| 583 | 8 CPU cycles for division at first, since using 16 produces bugs (see e.g. |
| 584 | Triforce pieces in Zelda 3 intro) */ |
| 585 | |
| 586 | static WRITE8_HANDLER( wrmpya_w ) |
| 587 | { |
| 588 | g65816i_cpu_struct *cpustate = get_safe_token(&space->device()); |
| 589 | |
| 590 | cpustate->wrmpya = data; |
| 591 | } |
| 592 | |
| 593 | static WRITE8_HANDLER( wrmpyb_w ) |
| 594 | { |
| 595 | g65816i_cpu_struct *cpustate = get_safe_token(&space->device()); |
| 596 | |
| 597 | cpustate->wrmpyb = data; |
| 598 | cpustate->rdmpy = cpustate->wrmpya * cpustate->wrmpyb; |
| 599 | /* TODO: cpustate->rddiv == 0? */ |
| 600 | } |
| 601 | |
| 602 | static WRITE8_HANDLER( wrdivl_w ) |
| 603 | { |
| 604 | g65816i_cpu_struct *cpustate = get_safe_token(&space->device()); |
| 605 | |
| 606 | cpustate->wrdiv = (data) | (cpustate->wrdiv & 0xff00); |
| 607 | } |
| 608 | |
| 609 | static WRITE8_HANDLER( wrdivh_w ) |
| 610 | { |
| 611 | g65816i_cpu_struct *cpustate = get_safe_token(&space->device()); |
| 612 | |
| 613 | cpustate->wrdiv = (data << 8) | (cpustate->wrdiv & 0xff); |
| 614 | } |
| 615 | |
| 616 | static WRITE8_HANDLER( wrdvdd_w ) |
| 617 | { |
| 618 | g65816i_cpu_struct *cpustate = get_safe_token(&space->device()); |
| 619 | UINT16 quotient, remainder; |
| 620 | |
| 621 | cpustate->dvdd = data; |
| 622 | |
| 623 | if(cpustate->dvdd != 0) |
| 624 | { |
| 625 | quotient = cpustate->wrdiv / cpustate->dvdd; |
| 626 | remainder = cpustate->wrdiv % cpustate->dvdd; |
| 627 | } |
| 628 | else |
| 629 | { |
| 630 | quotient = 0xffff; |
| 631 | remainder = 0x000c; |
| 632 | } |
| 633 | |
| 634 | cpustate->rddiv = quotient; |
| 635 | cpustate->rdmpy = remainder; |
| 636 | } |
| 637 | |
| 638 | static READ8_HANDLER( rddivl_r ) |
| 639 | { |
| 640 | g65816i_cpu_struct *cpustate = get_safe_token(&space->device()); |
| 641 | return cpustate->rddiv & 0xff; |
| 642 | } |
| 643 | |
| 644 | static READ8_HANDLER( rddivh_r ) |
| 645 | { |
| 646 | g65816i_cpu_struct *cpustate = get_safe_token(&space->device()); |
| 647 | return cpustate->rddiv >> 8; |
| 648 | } |
| 649 | |
| 650 | static READ8_HANDLER( rdmpyl_r ) |
| 651 | { |
| 652 | g65816i_cpu_struct *cpustate = get_safe_token(&space->device()); |
| 653 | return cpustate->rdmpy & 0xff; |
| 654 | } |
| 655 | |
| 656 | static READ8_HANDLER( rdmpyh_r ) |
| 657 | { |
| 658 | g65816i_cpu_struct *cpustate = get_safe_token(&space->device()); |
| 659 | return cpustate->rdmpy >> 8; |
| 660 | } |
| 661 | |
| 662 | |
| 663 | static ADDRESS_MAP_START(_5a22_map, AS_PROGRAM, 8, legacy_cpu_device) |
| 664 | AM_RANGE(0x4202, 0x4202) AM_MIRROR(0x3f0000) AM_WRITE_LEGACY(wrmpya_w) |
| 665 | AM_RANGE(0x4203, 0x4203) AM_MIRROR(0x3f0000) AM_WRITE_LEGACY(wrmpyb_w) |
| 666 | AM_RANGE(0x4204, 0x4204) AM_MIRROR(0x3f0000) AM_WRITE_LEGACY(wrdivl_w) |
| 667 | AM_RANGE(0x4205, 0x4205) AM_MIRROR(0x3f0000) AM_WRITE_LEGACY(wrdivh_w) |
| 668 | AM_RANGE(0x4206, 0x4206) AM_MIRROR(0x3f0000) AM_WRITE_LEGACY(wrdvdd_w) |
| 669 | |
| 670 | AM_RANGE(0x4214, 0x4214) AM_MIRROR(0x3f0000) AM_READ_LEGACY(rddivl_r) |
| 671 | AM_RANGE(0x4215, 0x4215) AM_MIRROR(0x3f0000) AM_READ_LEGACY(rddivh_r) |
| 672 | AM_RANGE(0x4216, 0x4216) AM_MIRROR(0x3f0000) AM_READ_LEGACY(rdmpyl_r) |
| 673 | AM_RANGE(0x4217, 0x4217) AM_MIRROR(0x3f0000) AM_READ_LEGACY(rdmpyh_r) |
| 674 | |
| 675 | ADDRESS_MAP_END |
| 676 | |
| 576 | 677 | CPU_SET_INFO( _5a22 ) |
| 577 | 678 | { |
| 578 | 679 | g65816i_cpu_struct *cpustate = (device != NULL && device->token() != NULL) ? get_safe_token(device) : NULL; |
| r17641 | r17642 | |
| 600 | 701 | case CPUINFO_STR_NAME: strcpy(info->s, "5A22"); break; |
| 601 | 702 | case CPUINFO_INT_REGISTER + _5A22_FASTROM: info->i = g65816_get_reg(cpustate, _5A22_FASTROM); break; |
| 602 | 703 | case CPUINFO_STR_REGISTER + _5A22_FASTROM: sprintf(info->s, "fastROM:%d", cpustate->fastROM & 1 ? 1 : 0); break; |
| 704 | case CPUINFO_PTR_INTERNAL_MEMORY_MAP + AS_PROGRAM: info->internal_map8 = ADDRESS_MAP_NAME(_5a22_map); break; |
| 603 | 705 | |
| 604 | 706 | default: CPU_GET_INFO_CALL(g65816); break; |
| 605 | 707 | } |