trunk/src/emu/cpu/i386/i386priv.h
| r20642 | r20643 | |
| 6 | 6 | #include "i386.h" |
| 7 | 7 | #include "../../../lib/softfloat/milieu.h" |
| 8 | 8 | #include "../../../lib/softfloat/softfloat.h" |
| 9 | #include "cpu/vtlb.h" |
| 9 | 10 | |
| 10 | 11 | //#define DEBUG_MISSING_OPCODE |
| 11 | 12 | |
| r20642 | r20643 | |
| 360 | 361 | UINT8 *cycle_table_pm; |
| 361 | 362 | UINT8 *cycle_table_rm; |
| 362 | 363 | |
| 364 | vtlb_state *vtlb; |
| 365 | |
| 363 | 366 | // bytes in current opcode, debug only |
| 364 | 367 | #ifdef DEBUG_MISSING_OPCODE |
| 365 | 368 | UINT8 opcode_bytes[16]; |
| r20642 | r20643 | |
| 475 | 478 | return cpustate->sreg[segment].base + ip; |
| 476 | 479 | } |
| 477 | 480 | |
| 478 | | // rwn; read = 0, write = 1, none = -1, read at PL 0 = -2 |
| 479 | | INLINE int translate_address(i386_state *cpustate, int rwn, UINT32 *address, UINT32 *error) |
| 481 | #define VTLB_FLAG_DIRTY 0x100 |
| 482 | |
| 483 | INLINE vtlb_entry get_permissions(UINT32 pte, int wp) |
| 480 | 484 | { |
| 485 | vtlb_entry ret = VTLB_READ_ALLOWED | ((pte & 4) ? VTLB_USER_READ_ALLOWED : 0); |
| 486 | if(!wp) |
| 487 | ret |= VTLB_WRITE_ALLOWED; |
| 488 | if(pte & 2) |
| 489 | ret |= VTLB_WRITE_ALLOWED | ((pte & 4) ? VTLB_USER_WRITE_ALLOWED : 0); |
| 490 | return ret; |
| 491 | } |
| 492 | |
| 493 | static int i386_translate_address(i386_state *cpustate, int intention, offs_t *address, vtlb_entry *entry) |
| 494 | { |
| 481 | 495 | UINT32 a = *address; |
| 482 | 496 | UINT32 pdbr = cpustate->cr[3] & 0xfffff000; |
| 483 | 497 | UINT32 directory = (a >> 22) & 0x3ff; |
| 484 | 498 | UINT32 table = (a >> 12) & 0x3ff; |
| 485 | | UINT32 offset = a & 0xfff; |
| 486 | | UINT32 page_entry; |
| 487 | | UINT32 ret = 1; |
| 488 | | bool user = (cpustate->CPL == 3) && (rwn >= 0); |
| 489 | | *error = 0; |
| 499 | vtlb_entry perm = 0; |
| 500 | int ret = FALSE; |
| 501 | bool user = (intention & TRANSLATE_USER_MASK) ? true : false; |
| 502 | bool write = (intention & TRANSLATE_WRITE) ? true : false; |
| 503 | bool debug = (intention & TRANSLATE_DEBUG_MASK) ? true : false; |
| 490 | 504 | |
| 505 | if(!(cpustate->cr[0] & 0x80000000)) |
| 506 | { |
| 507 | if(entry) |
| 508 | *entry = 0x77; |
| 509 | return TRUE; |
| 510 | } |
| 511 | |
| 491 | 512 | UINT32 page_dir = cpustate->program->read_dword(pdbr + directory * 4); |
| 492 | | if((page_dir & 1) && ((page_dir & 4) || !user)) |
| 513 | if(page_dir & 1) |
| 493 | 514 | { |
| 494 | | if (!(cpustate->cr[4] & 0x10)) |
| 515 | if ((page_dir & 0x80) && (cpustate->cr[4] & 0x10)) |
| 495 | 516 | { |
| 496 | | page_entry = cpustate->program->read_dword((page_dir & 0xfffff000) + (table * 4)); |
| 497 | | if(!(page_entry & 1)) |
| 498 | | ret = 0; |
| 499 | | else if((!(page_entry & 2) && (user || WP) && (rwn == 1)) || (!(page_entry & 4) && user)) |
| 517 | a = (page_dir & 0xffc00000) | (a & 0x003fffff); |
| 518 | if(debug) |
| 500 | 519 | { |
| 501 | | *error = 1; |
| 502 | | ret = 0; |
| 520 | *address = a; |
| 521 | return TRUE; |
| 503 | 522 | } |
| 523 | perm = get_permissions(page_dir, WP); |
| 524 | if(write && (!(perm & VTLB_WRITE_ALLOWED) || (user && !(perm & VTLB_USER_WRITE_ALLOWED)))) |
| 525 | ret = FALSE; |
| 526 | else if(user && !(perm & VTLB_USER_READ_ALLOWED)) |
| 527 | ret = FALSE; |
| 504 | 528 | else |
| 505 | 529 | { |
| 506 | | if(!(page_dir & 0x20) && (rwn != -1)) |
| 530 | if(!(page_dir & 0x40) && write) |
| 531 | { |
| 532 | cpustate->program->write_dword(pdbr + directory * 4, page_dir | 0x60); |
| 533 | perm |= VTLB_FLAG_DIRTY; |
| 534 | } |
| 535 | else if(!(page_dir & 0x20)) |
| 507 | 536 | cpustate->program->write_dword(pdbr + directory * 4, page_dir | 0x20); |
| 508 | | if(!(page_entry & 0x40) && (rwn == 1)) |
| 509 | | cpustate->program->write_dword((page_dir & 0xfffff000) + (table * 4), page_entry | 0x60); |
| 510 | | else if(!(page_entry & 0x20) && (rwn != -1)) |
| 511 | | cpustate->program->write_dword((page_dir & 0xfffff000) + (table * 4), page_entry | 0x20); |
| 512 | | *address = (page_entry & 0xfffff000) | offset; |
| 537 | ret = TRUE; |
| 513 | 538 | } |
| 514 | 539 | } |
| 515 | 540 | else |
| 516 | 541 | { |
| 517 | | if (page_dir & 0x80) |
| 518 | | { |
| 519 | | if(!(page_dir & 2) && (user || WP) && (rwn == 1)) |
| 520 | | { |
| 521 | | *error = 1; |
| 522 | | ret = 0; |
| 523 | | } |
| 524 | | else |
| 525 | | { |
| 526 | | if(!(page_dir & 0x40) && (rwn == 1)) |
| 527 | | cpustate->program->write_dword(pdbr + directory * 4, page_dir | 0x60); |
| 528 | | else if(!(page_dir & 0x20) && (rwn != -1)) |
| 529 | | cpustate->program->write_dword(pdbr + directory * 4, page_dir | 0x20); |
| 530 | | *address = (page_dir & 0xffc00000) | (a & 0x003fffff); |
| 531 | | } |
| 532 | | } |
| 542 | UINT32 page_entry = cpustate->program->read_dword((page_dir & 0xfffff000) + (table * 4)); |
| 543 | if(!(page_entry & 1)) |
| 544 | ret = FALSE; |
| 533 | 545 | else |
| 534 | 546 | { |
| 535 | | page_entry = cpustate->program->read_dword((page_dir & 0xfffff000) + (table * 4)); |
| 536 | | if(!(page_entry & 1)) |
| 537 | | ret = 0; |
| 538 | | else if((!(page_entry & 2) && (user || WP) && (rwn == 1)) || (!(page_entry & 4) && user)) |
| 547 | a = (page_entry & 0xfffff000) | (a & 0xfff); |
| 548 | if(debug) |
| 539 | 549 | { |
| 540 | | *error = 1; |
| 541 | | ret = 0; |
| 550 | *address = a; |
| 551 | return TRUE; |
| 542 | 552 | } |
| 553 | perm = get_permissions(page_entry, WP); |
| 554 | if(write && (!(perm & VTLB_WRITE_ALLOWED) || (user && !(perm & VTLB_USER_WRITE_ALLOWED)))) |
| 555 | ret = FALSE; |
| 556 | else if(user && !(perm & VTLB_USER_READ_ALLOWED)) |
| 557 | ret = FALSE; |
| 543 | 558 | else |
| 544 | 559 | { |
| 545 | | if(!(page_dir & 0x20) && (rwn != -1)) |
| 560 | if(!(page_dir & 0x20)) |
| 546 | 561 | cpustate->program->write_dword(pdbr + directory * 4, page_dir | 0x20); |
| 547 | | if(!(page_entry & 0x40) && (rwn == 1)) |
| 562 | if(!(page_entry & 0x40) && write) |
| 563 | { |
| 548 | 564 | cpustate->program->write_dword((page_dir & 0xfffff000) + (table * 4), page_entry | 0x60); |
| 549 | | else if(!(page_entry & 0x20) && (rwn != -1)) |
| 565 | perm |= VTLB_FLAG_DIRTY; |
| 566 | } |
| 567 | else if(!(page_entry & 0x20)) |
| 550 | 568 | cpustate->program->write_dword((page_dir & 0xfffff000) + (table * 4), page_entry | 0x20); |
| 551 | | *address = (page_entry & 0xfffff000) | offset; |
| 569 | ret = TRUE; |
| 552 | 570 | } |
| 553 | 571 | } |
| 554 | 572 | } |
| 555 | 573 | } |
| 556 | 574 | else |
| 575 | ret = FALSE; |
| 576 | if(entry) |
| 577 | *entry = perm; |
| 578 | if(ret) |
| 579 | *address = a; |
| 580 | return ret; |
| 581 | } |
| 582 | |
| 583 | //#define TEST_TLB |
| 584 | |
| 585 | INLINE int translate_address(i386_state *cpustate, int pl, int type, UINT32 *address, UINT32 *error) |
| 586 | { |
| 587 | const vtlb_entry *table = vtlb_table(cpustate->vtlb); |
| 588 | int index = *address >> 12; |
| 589 | vtlb_entry entry = table[index]; |
| 590 | if(type == TRANSLATE_FETCH) |
| 591 | type = TRANSLATE_READ; |
| 592 | if(pl == 3) |
| 593 | type |= TRANSLATE_USER_MASK; |
| 594 | #ifdef TEST_TLB |
| 595 | UINT32 test_addr = *address; |
| 596 | #endif |
| 597 | |
| 598 | if(!(entry & VTLB_FLAG_VALID) || ((type & TRANSLATE_WRITE) && !(entry & VTLB_FLAG_DIRTY))) |
| 557 | 599 | { |
| 558 | | if(page_dir & 1) |
| 559 | | *error = 1; |
| 560 | | ret = 0; |
| 600 | if(!i386_translate_address(cpustate, type, address, &entry)) |
| 601 | { |
| 602 | *error = ((type & TRANSLATE_WRITE) ? 2 : 0) | ((cpustate->CPL == 3) ? 4 : 0); |
| 603 | if(entry) |
| 604 | *error |= 1; |
| 605 | return FALSE; |
| 606 | } |
| 607 | vtlb_dynload(cpustate->vtlb, index, *address, entry); |
| 608 | return TRUE; |
| 561 | 609 | } |
| 562 | | if(!ret) |
| 610 | if(!(entry & (1 << type))) |
| 563 | 611 | { |
| 564 | | if(rwn != -1) |
| 565 | | *error |= ((rwn == 1)<<1) | ((cpustate->CPL == 3)<<2); |
| 566 | | return 0; |
| 612 | *error = ((type & TRANSLATE_WRITE) ? 2 : 0) | ((cpustate->CPL == 3) ? 4 : 0) | 1; |
| 613 | return FALSE; |
| 567 | 614 | } |
| 568 | | return 1; |
| 615 | *address = (entry & 0xfffff000) | (*address & 0xfff); |
| 616 | #ifdef TEST_TLB |
| 617 | int test_ret = i386_translate_address(cpustate, type | TRANSLATE_DEBUG_MASK, &test_addr, NULL); |
| 618 | if(!test_ret || (test_addr != *address)) |
| 619 | logerror("TLB-PTE mismatch! %06X %06X %06x\n", *address, test_addr, cpustate->pc); |
| 620 | #endif |
| 621 | return TRUE; |
| 569 | 622 | } |
| 570 | 623 | |
| 571 | 624 | INLINE void CHANGE_PC(i386_state *cpustate, UINT32 pc) |
| 572 | 625 | { |
| 573 | | UINT32 address, error; |
| 574 | 626 | cpustate->pc = i386_translate(cpustate, CS, pc, -1 ); |
| 575 | | |
| 576 | | address = cpustate->pc; |
| 577 | | |
| 578 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 579 | | { |
| 580 | | translate_address(cpustate,-1,&address,&error); |
| 581 | | } |
| 582 | 627 | } |
| 583 | 628 | |
| 584 | 629 | INLINE void NEAR_BRANCH(i386_state *cpustate, INT32 offs) |
| 585 | 630 | { |
| 586 | | UINT32 address, error; |
| 587 | 631 | /* TODO: limit */ |
| 588 | 632 | cpustate->eip += offs; |
| 589 | 633 | cpustate->pc += offs; |
| 590 | | |
| 591 | | address = cpustate->pc; |
| 592 | | |
| 593 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 594 | | { |
| 595 | | translate_address(cpustate,-1,&address,&error); |
| 596 | | } |
| 597 | 634 | } |
| 598 | 635 | |
| 599 | 636 | INLINE UINT8 FETCH(i386_state *cpustate) |
| r20642 | r20643 | |
| 601 | 638 | UINT8 value; |
| 602 | 639 | UINT32 address = cpustate->pc, error; |
| 603 | 640 | |
| 604 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 605 | | { |
| 606 | | if(!translate_address(cpustate,0,&address,&error)) |
| 607 | | PF_THROW(error); |
| 608 | | } |
| 641 | if(!translate_address(cpustate,cpustate->CPL,TRANSLATE_FETCH,&address,&error)) |
| 642 | PF_THROW(error); |
| 609 | 643 | |
| 610 | 644 | value = cpustate->direct->read_decrypted_byte(address & cpustate->a20_mask); |
| 611 | 645 | #ifdef DEBUG_MISSING_OPCODE |
| r20642 | r20643 | |
| 625 | 659 | value = (FETCH(cpustate) << 0) | |
| 626 | 660 | (FETCH(cpustate) << 8); |
| 627 | 661 | } else { |
| 628 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 629 | | { |
| 630 | | if(!translate_address(cpustate,0,&address,&error)) |
| 631 | | PF_THROW(error); |
| 632 | | } |
| 662 | if(!translate_address(cpustate,cpustate->CPL,TRANSLATE_FETCH,&address,&error)) |
| 663 | PF_THROW(error); |
| 633 | 664 | address &= cpustate->a20_mask; |
| 634 | 665 | value = cpustate->direct->read_decrypted_word(address); |
| 635 | 666 | cpustate->eip += 2; |
| r20642 | r20643 | |
| 648 | 679 | (FETCH(cpustate) << 16) | |
| 649 | 680 | (FETCH(cpustate) << 24); |
| 650 | 681 | } else { |
| 651 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 652 | | { |
| 653 | | if(!translate_address(cpustate,0,&address,&error)) |
| 654 | | PF_THROW(error); |
| 655 | | } |
| 682 | if(!translate_address(cpustate,cpustate->CPL,TRANSLATE_FETCH,&address,&error)) |
| 683 | PF_THROW(error); |
| 656 | 684 | |
| 657 | 685 | address &= cpustate->a20_mask; |
| 658 | 686 | value = cpustate->direct->read_decrypted_dword(address); |
| r20642 | r20643 | |
| 666 | 694 | { |
| 667 | 695 | UINT32 address = ea, error; |
| 668 | 696 | |
| 669 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 670 | | { |
| 671 | | if(!translate_address(cpustate,0,&address,&error)) |
| 672 | | PF_THROW(error); |
| 673 | | } |
| 697 | if(!translate_address(cpustate,cpustate->CPL,TRANSLATE_READ,&address, &error)) |
| 698 | PF_THROW(error); |
| 674 | 699 | |
| 675 | 700 | address &= cpustate->a20_mask; |
| 676 | 701 | return cpustate->program->read_byte(address); |
| r20642 | r20643 | |
| 684 | 709 | value = (READ8( cpustate, address+0 ) << 0) | |
| 685 | 710 | (READ8( cpustate, address+1 ) << 8); |
| 686 | 711 | } else { |
| 687 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 688 | | { |
| 689 | | if(!translate_address(cpustate,0,&address,&error)) |
| 690 | | PF_THROW(error); |
| 691 | | } |
| 712 | if(!translate_address(cpustate,cpustate->CPL,TRANSLATE_READ,&address,&error)) |
| 713 | PF_THROW(error); |
| 692 | 714 | |
| 693 | 715 | address &= cpustate->a20_mask; |
| 694 | 716 | value = cpustate->program->read_word( address ); |
| r20642 | r20643 | |
| 706 | 728 | (READ8( cpustate, address+2 ) << 16) | |
| 707 | 729 | (READ8( cpustate, address+3 ) << 24); |
| 708 | 730 | } else { |
| 709 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 710 | | { |
| 711 | | if(!translate_address(cpustate,0,&address,&error)) |
| 712 | | PF_THROW(error); |
| 713 | | } |
| 731 | if(!translate_address(cpustate,cpustate->CPL,TRANSLATE_READ,&address,&error)) |
| 732 | PF_THROW(error); |
| 714 | 733 | |
| 715 | 734 | address &= cpustate->a20_mask; |
| 716 | 735 | value = cpustate->program->read_dword( address ); |
| r20642 | r20643 | |
| 733 | 752 | (((UINT64) READ8( cpustate, address+6 )) << 48) | |
| 734 | 753 | (((UINT64) READ8( cpustate, address+7 )) << 56); |
| 735 | 754 | } else { |
| 736 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 737 | | { |
| 738 | | if(!translate_address(cpustate,0,&address,&error)) |
| 739 | | PF_THROW(error); |
| 740 | | } |
| 755 | if(!translate_address(cpustate,cpustate->CPL,TRANSLATE_READ,&address,&error)) |
| 756 | PF_THROW(error); |
| 741 | 757 | |
| 742 | 758 | address &= cpustate->a20_mask; |
| 743 | 759 | value = (((UINT64) cpustate->program->read_dword( address+0 )) << 0) | |
| r20642 | r20643 | |
| 749 | 765 | { |
| 750 | 766 | UINT32 address = ea, error; |
| 751 | 767 | |
| 752 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 753 | | { |
| 754 | | if(!translate_address(cpustate,-2,&address,&error)) |
| 755 | | PF_THROW(error); |
| 756 | | } |
| 768 | if(!translate_address(cpustate,0,TRANSLATE_READ,&address,&error)) |
| 769 | PF_THROW(error); |
| 757 | 770 | |
| 758 | 771 | address &= cpustate->a20_mask; |
| 759 | 772 | return cpustate->program->read_byte(address); |
| r20642 | r20643 | |
| 767 | 780 | value = (READ8PL0( cpustate, address+0 ) << 0) | |
| 768 | 781 | (READ8PL0( cpustate, address+1 ) << 8); |
| 769 | 782 | } else { |
| 770 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 771 | | { |
| 772 | | if(!translate_address(cpustate,-2,&address,&error)) |
| 773 | | PF_THROW(error); |
| 774 | | } |
| 783 | if(!translate_address(cpustate,0,TRANSLATE_READ,&address,&error)) |
| 784 | PF_THROW(error); |
| 775 | 785 | |
| 776 | 786 | address &= cpustate->a20_mask; |
| 777 | 787 | value = cpustate->program->read_word( address ); |
| r20642 | r20643 | |
| 789 | 799 | (READ8PL0( cpustate, address+2 ) << 16) | |
| 790 | 800 | (READ8PL0( cpustate, address+3 ) << 24); |
| 791 | 801 | } else { |
| 792 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 793 | | { |
| 794 | | if(!translate_address(cpustate,-2,&address,&error)) |
| 795 | | PF_THROW(error); |
| 796 | | } |
| 802 | if(!translate_address(cpustate,0,TRANSLATE_READ,&address,&error)) |
| 803 | PF_THROW(error); |
| 797 | 804 | |
| 798 | 805 | address &= cpustate->a20_mask; |
| 799 | 806 | value = cpustate->program->read_dword( address ); |
| r20642 | r20643 | |
| 804 | 811 | INLINE void WRITE_TEST(i386_state *cpustate,UINT32 ea) |
| 805 | 812 | { |
| 806 | 813 | UINT32 address = ea, error; |
| 807 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 808 | | { |
| 809 | | if(!translate_address(cpustate,1,&address,&error)) |
| 810 | | PF_THROW(error); |
| 811 | | } |
| 814 | if(!translate_address(cpustate,cpustate->CPL,TRANSLATE_WRITE,&address,&error)) |
| 815 | PF_THROW(error); |
| 812 | 816 | } |
| 813 | 817 | |
| 814 | 818 | INLINE void WRITE8(i386_state *cpustate,UINT32 ea, UINT8 value) |
| 815 | 819 | { |
| 816 | 820 | UINT32 address = ea, error; |
| 817 | 821 | |
| 818 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 819 | | { |
| 820 | | if(!translate_address(cpustate,1,&address,&error)) |
| 821 | | PF_THROW(error); |
| 822 | | } |
| 822 | if(!translate_address(cpustate,cpustate->CPL,TRANSLATE_WRITE,&address,&error)) |
| 823 | PF_THROW(error); |
| 823 | 824 | |
| 824 | 825 | address &= cpustate->a20_mask; |
| 825 | 826 | cpustate->program->write_byte(address, value); |
| r20642 | r20643 | |
| 832 | 833 | WRITE8( cpustate, address+0, value & 0xff ); |
| 833 | 834 | WRITE8( cpustate, address+1, (value >> 8) & 0xff ); |
| 834 | 835 | } else { |
| 835 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 836 | | { |
| 837 | | if(!translate_address(cpustate,1,&address,&error)) |
| 838 | | PF_THROW(error); |
| 839 | | } |
| 836 | if(!translate_address(cpustate,cpustate->CPL,TRANSLATE_WRITE,&address,&error)) |
| 837 | PF_THROW(error); |
| 840 | 838 | |
| 841 | 839 | address &= cpustate->a20_mask; |
| 842 | 840 | cpustate->program->write_word(address, value); |
| r20642 | r20643 | |
| 852 | 850 | WRITE8( cpustate, address+2, (value >> 16) & 0xff ); |
| 853 | 851 | WRITE8( cpustate, address+3, (value >> 24) & 0xff ); |
| 854 | 852 | } else { |
| 855 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 856 | | { |
| 857 | | if(!translate_address(cpustate,1,&address,&error)) |
| 858 | | PF_THROW(error); |
| 859 | | } |
| 853 | if(!translate_address(cpustate,cpustate->CPL,TRANSLATE_WRITE,&address,&error)) |
| 854 | PF_THROW(error); |
| 860 | 855 | |
| 861 | 856 | ea &= cpustate->a20_mask; |
| 862 | 857 | cpustate->program->write_dword(address, value); |
| r20642 | r20643 | |
| 877 | 872 | WRITE8( cpustate, address+6, (value >> 48) & 0xff ); |
| 878 | 873 | WRITE8( cpustate, address+7, (value >> 56) & 0xff ); |
| 879 | 874 | } else { |
| 880 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 881 | | { |
| 882 | | if(!translate_address(cpustate,1,&address,&error)) |
| 883 | | PF_THROW(error); |
| 884 | | } |
| 875 | if(!translate_address(cpustate,cpustate->CPL,TRANSLATE_WRITE,&address,&error)) |
| 876 | PF_THROW(error); |
| 885 | 877 | |
| 886 | 878 | ea &= cpustate->a20_mask; |
| 887 | 879 | cpustate->program->write_dword(address+0, value & 0xffffffff); |
trunk/src/emu/cpu/i386/i386.c
| r20642 | r20643 | |
| 108 | 108 | static void i386_set_descriptor_accessed(i386_state *cpustate, UINT16 selector) |
| 109 | 109 | { |
| 110 | 110 | // assume the selector is valid, we don't need to check it again |
| 111 | | UINT32 base, addr, error; |
| 111 | UINT32 base, addr; |
| 112 | 112 | UINT8 rights; |
| 113 | 113 | if(!(selector & ~3)) |
| 114 | 114 | return; |
| r20642 | r20643 | |
| 119 | 119 | base = cpustate->gdtr.base; |
| 120 | 120 | |
| 121 | 121 | addr = base + (selector & ~7) + 5; |
| 122 | | translate_address(cpustate, -2, &addr, &error); |
| 122 | i386_translate_address(cpustate, TRANSLATE_READ, &addr, NULL); |
| 123 | 123 | rights = cpustate->program->read_byte(addr); |
| 124 | 124 | // Should a fault be thrown if the table is read only? |
| 125 | 125 | cpustate->program->write_byte(addr, rights | 1); |
| r20642 | r20643 | |
| 1106 | 1106 | I386_SREG seg; |
| 1107 | 1107 | UINT16 old_task; |
| 1108 | 1108 | UINT8 ar_byte; // access rights byte |
| 1109 | UINT32 oldcr3 = cpustate->cr[3]; |
| 1109 | 1110 | |
| 1110 | 1111 | /* TODO: Task State Segment privilege checks */ |
| 1111 | 1112 | |
| r20642 | r20643 | |
| 1166 | 1167 | cpustate->ldtr.limit = seg.limit; |
| 1167 | 1168 | cpustate->ldtr.base = seg.base; |
| 1168 | 1169 | cpustate->ldtr.flags = seg.flags; |
| 1169 | | cpustate->cr[3] = READ32(cpustate,tss+0x1c); // CR3 (PDBR) |
| 1170 | 1170 | cpustate->eip = READ32(cpustate,tss+0x20); |
| 1171 | 1171 | set_flags(cpustate,READ32(cpustate,tss+0x24)); |
| 1172 | 1172 | REG32(EAX) = READ32(cpustate,tss+0x28); |
| r20642 | r20643 | |
| 1189 | 1189 | i386_load_segment_descriptor(cpustate, FS); |
| 1190 | 1190 | cpustate->sreg[GS].selector = READ32(cpustate,tss+0x5c) & 0xffff; |
| 1191 | 1191 | i386_load_segment_descriptor(cpustate, GS); |
| 1192 | /* For nested tasks, we write the outgoing task's selector to the back-link field of the new TSS, |
| 1193 | and set the NT flag in the EFLAGS register before settings cr3 as the old tss address might be gone */ |
| 1194 | if(nested != 0) |
| 1195 | { |
| 1196 | WRITE32(cpustate,tss+0,old_task); |
| 1197 | cpustate->NT = 1; |
| 1198 | } |
| 1199 | cpustate->cr[3] = READ32(cpustate,tss+0x1c); // CR3 (PDBR) |
| 1200 | if(oldcr3 != cpustate->cr[3]) |
| 1201 | vtlb_flush_dynamic(cpustate->vtlb); |
| 1192 | 1202 | |
| 1193 | 1203 | /* Set the busy bit in the new task's descriptor */ |
| 1194 | 1204 | if(selector & 0x0004) |
| r20642 | r20643 | |
| 1202 | 1212 | WRITE8(cpustate,cpustate->gdtr.base + (selector & ~0x0007) + 5,ar_byte | 0x02); |
| 1203 | 1213 | } |
| 1204 | 1214 | |
| 1205 | | /* For nested tasks, we write the outgoing task's selector to the back-link field of the new TSS, |
| 1206 | | and set the NT flag in the EFLAGS register */ |
| 1207 | | if(nested != 0) |
| 1208 | | { |
| 1209 | | WRITE32(cpustate,tss+0,old_task); |
| 1210 | | cpustate->NT = 1; |
| 1211 | | } |
| 1212 | 1215 | CHANGE_PC(cpustate,cpustate->eip); |
| 1213 | 1216 | |
| 1214 | 1217 | cpustate->CPL = cpustate->sreg[CS].selector & 0x03; |
| r20642 | r20643 | |
| 1344 | 1347 | return; |
| 1345 | 1348 | case 0x04: // 286 Call Gate |
| 1346 | 1349 | case 0x0c: // 386 Call Gate |
| 1347 | | logerror("JMP: Call gate at %08x\n",cpustate->pc); |
| 1350 | //logerror("JMP: Call gate at %08x\n",cpustate->pc); |
| 1348 | 1351 | SetRPL = 1; |
| 1349 | 1352 | memset(&call_gate, 0, sizeof(call_gate)); |
| 1350 | 1353 | call_gate.segment = segment; |
| r20642 | r20643 | |
| 1639 | 1642 | gate.segment = selector; |
| 1640 | 1643 | i386_load_call_gate(cpustate,&gate); |
| 1641 | 1644 | DPL = gate.dpl; |
| 1642 | | logerror("CALL: Call gate at %08x (%i parameters)\n",cpustate->pc,gate.dword_count); |
| 1645 | //logerror("CALL: Call gate at %08x (%i parameters)\n",cpustate->pc,gate.dword_count); |
| 1643 | 1646 | if(DPL < CPL) |
| 1644 | 1647 | { |
| 1645 | 1648 | logerror("CALL: Call gate DPL %i is less than CPL %i.\n",DPL,CPL); |
| r20642 | r20643 | |
| 2795 | 2798 | |
| 2796 | 2799 | static UINT8 read8_debug(i386_state *cpustate, UINT32 ea, UINT8 *data) |
| 2797 | 2800 | { |
| 2798 | | UINT32 address = ea, error; |
| 2801 | UINT32 address = ea; |
| 2799 | 2802 | |
| 2800 | | if (cpustate->cr[0] & 0x80000000) // page translation enabled |
| 2801 | | { |
| 2802 | | if(!translate_address(cpustate,-1,&address,&error)) |
| 2803 | | return 0; |
| 2804 | | } |
| 2803 | if(!i386_translate_address(cpustate,TRANSLATE_DEBUG_MASK,&address,NULL)) |
| 2804 | return 0; |
| 2805 | 2805 | |
| 2806 | | address &= cpustate->a20_mask; |
| 2807 | 2806 | *data = cpustate->program->read_byte(address); |
| 2808 | 2807 | return 1; |
| 2809 | 2808 | } |
| r20642 | r20643 | |
| 2941 | 2940 | { |
| 2942 | 2941 | legacy_cpu_device *device = (legacy_cpu_device *)ref; |
| 2943 | 2942 | i386_state *cpustate = get_safe_token(device); |
| 2944 | | UINT32 result = param[0], error; |
| 2943 | UINT32 result = param[0]; |
| 2945 | 2944 | |
| 2946 | | if (cpustate->cr[0] & 0x80000000) |
| 2947 | | { |
| 2948 | | if(!translate_address(cpustate,-1,&result,&error)) |
| 2949 | | return 0; |
| 2950 | | } |
| 2945 | if(!i386_translate_address(cpustate,TRANSLATE_DEBUG_MASK,&result,NULL)) |
| 2946 | return 0; |
| 2951 | 2947 | return result; |
| 2952 | 2948 | } |
| 2953 | 2949 | |
| r20642 | r20643 | |
| 2969 | 2965 | CHANGE_PC(cpustate,cpustate->eip); |
| 2970 | 2966 | } |
| 2971 | 2967 | |
| 2972 | | static CPU_INIT( i386 ) |
| 2968 | static void i386_common_init(legacy_cpu_device *device, device_irq_acknowledge_callback irqcallback, int tlbsize) |
| 2973 | 2969 | { |
| 2974 | 2970 | int i, j; |
| 2975 | 2971 | static const int regs8[8] = {AL,CL,DL,BL,AH,CH,DH,BH}; |
| r20642 | r20643 | |
| 3003 | 2999 | cpustate->program = &device->space(AS_PROGRAM); |
| 3004 | 3000 | cpustate->direct = &cpustate->program->direct(); |
| 3005 | 3001 | cpustate->io = &device->space(AS_IO); |
| 3002 | cpustate->vtlb = vtlb_alloc(device, AS_PROGRAM, 0, tlbsize); |
| 3006 | 3003 | |
| 3007 | 3004 | device->save_item(NAME( cpustate->reg.d)); |
| 3008 | 3005 | device->save_item(NAME(cpustate->sreg[ES].selector)); |
| r20642 | r20643 | |
| 3061 | 3058 | device->machine().save().register_postload(save_prepost_delegate(FUNC(i386_postload), cpustate)); |
| 3062 | 3059 | } |
| 3063 | 3060 | |
| 3061 | CPU_INIT( i386 ) |
| 3062 | { |
| 3063 | i386_common_init(device, irqcallback, 32); |
| 3064 | } |
| 3065 | |
| 3064 | 3066 | static void build_opcode_table(i386_state *cpustate, UINT32 features) |
| 3065 | 3067 | { |
| 3066 | 3068 | int i; |
| r20642 | r20643 | |
| 3119 | 3121 | { |
| 3120 | 3122 | i386_state *cpustate = get_safe_token(device); |
| 3121 | 3123 | device_irq_acknowledge_callback save_irqcallback; |
| 3124 | vtlb_state *save_vtlb = cpustate->vtlb; |
| 3122 | 3125 | |
| 3123 | 3126 | save_irqcallback = cpustate->irq_callback; |
| 3124 | 3127 | memset( cpustate, 0, sizeof(*cpustate) ); |
| 3128 | vtlb_flush_dynamic(save_vtlb); |
| 3129 | cpustate->vtlb = save_vtlb; |
| 3125 | 3130 | cpustate->irq_callback = save_irqcallback; |
| 3126 | 3131 | cpustate->device = device; |
| 3127 | 3132 | cpustate->program = &device->space(AS_PROGRAM); |
| r20642 | r20643 | |
| 3193 | 3198 | { |
| 3194 | 3199 | cpustate->a20_mask = ~(1 << 20); |
| 3195 | 3200 | } |
| 3201 | // TODO: how does A20M and the tlb interact |
| 3202 | vtlb_flush_dynamic(cpustate->vtlb); |
| 3196 | 3203 | } |
| 3197 | 3204 | |
| 3198 | 3205 | static CPU_EXECUTE( i386 ) |
| r20642 | r20643 | |
| 3260 | 3267 | static CPU_TRANSLATE( i386 ) |
| 3261 | 3268 | { |
| 3262 | 3269 | i386_state *cpustate = get_safe_token(device); |
| 3263 | | int result = 1; |
| 3264 | | UINT32 error; |
| 3265 | | if (space == AS_PROGRAM) |
| 3266 | | { |
| 3267 | | if (cpustate->cr[0] & 0x80000000) |
| 3268 | | result = translate_address(cpustate,-1,address,&error); |
| 3269 | | *address &= cpustate->a20_mask; |
| 3270 | | } |
| 3271 | | return result; |
| 3270 | int ret = TRUE; |
| 3271 | if(space == AS_PROGRAM) |
| 3272 | ret = i386_translate_address(cpustate, intention, address, NULL); |
| 3273 | *address &= cpustate->a20_mask; |
| 3274 | return ret; |
| 3272 | 3275 | } |
| 3273 | 3276 | |
| 3274 | 3277 | static CPU_DISASSEMBLE( i386 ) |
| r20642 | r20643 | |
| 3603 | 3606 | |
| 3604 | 3607 | static CPU_INIT( i486 ) |
| 3605 | 3608 | { |
| 3606 | | CPU_INIT_CALL(i386); |
| 3609 | i386_common_init(device, irqcallback, 32); |
| 3607 | 3610 | } |
| 3608 | 3611 | |
| 3609 | 3612 | static CPU_RESET( i486 ) |
| 3610 | 3613 | { |
| 3611 | 3614 | i386_state *cpustate = get_safe_token(device); |
| 3612 | 3615 | device_irq_acknowledge_callback save_irqcallback; |
| 3616 | vtlb_state *save_vtlb = cpustate->vtlb; |
| 3613 | 3617 | |
| 3614 | 3618 | save_irqcallback = cpustate->irq_callback; |
| 3615 | 3619 | memset( cpustate, 0, sizeof(*cpustate) ); |
| 3620 | vtlb_flush_dynamic(save_vtlb); |
| 3621 | cpustate->vtlb = save_vtlb; |
| 3616 | 3622 | cpustate->irq_callback = save_irqcallback; |
| 3617 | 3623 | cpustate->device = device; |
| 3618 | 3624 | cpustate->program = &device->space(AS_PROGRAM); |
| r20642 | r20643 | |
| 3710 | 3716 | |
| 3711 | 3717 | static CPU_INIT( pentium ) |
| 3712 | 3718 | { |
| 3713 | | CPU_INIT_CALL(i386); |
| 3719 | // 64 dtlb small, 8 dtlb large, 32 itlb |
| 3720 | i386_common_init(device, irqcallback, 96); |
| 3714 | 3721 | } |
| 3715 | 3722 | |
| 3716 | 3723 | static CPU_RESET( pentium ) |
| 3717 | 3724 | { |
| 3718 | 3725 | i386_state *cpustate = get_safe_token(device); |
| 3719 | 3726 | device_irq_acknowledge_callback save_irqcallback; |
| 3727 | vtlb_state *save_vtlb = cpustate->vtlb; |
| 3720 | 3728 | |
| 3721 | 3729 | save_irqcallback = cpustate->irq_callback; |
| 3722 | 3730 | memset( cpustate, 0, sizeof(*cpustate) ); |
| 3731 | vtlb_flush_dynamic(save_vtlb); |
| 3732 | cpustate->vtlb = save_vtlb; |
| 3723 | 3733 | cpustate->irq_callback = save_irqcallback; |
| 3724 | 3734 | cpustate->device = device; |
| 3725 | 3735 | cpustate->program = &device->space(AS_PROGRAM); |
| r20642 | r20643 | |
| 3833 | 3843 | |
| 3834 | 3844 | static CPU_INIT( mediagx ) |
| 3835 | 3845 | { |
| 3836 | | CPU_INIT_CALL(i386); |
| 3846 | // probably 32 unified |
| 3847 | i386_common_init(device, irqcallback, 32); |
| 3837 | 3848 | } |
| 3838 | 3849 | |
| 3839 | 3850 | static CPU_RESET( mediagx ) |
| 3840 | 3851 | { |
| 3841 | 3852 | i386_state *cpustate = get_safe_token(device); |
| 3842 | 3853 | device_irq_acknowledge_callback save_irqcallback; |
| 3854 | vtlb_state *save_vtlb = cpustate->vtlb; |
| 3843 | 3855 | |
| 3844 | 3856 | save_irqcallback = cpustate->irq_callback; |
| 3845 | 3857 | memset( cpustate, 0, sizeof(*cpustate) ); |
| 3858 | vtlb_flush_dynamic(save_vtlb); |
| 3859 | cpustate->vtlb = save_vtlb; |
| 3846 | 3860 | cpustate->irq_callback = save_irqcallback; |
| 3847 | 3861 | cpustate->device = device; |
| 3848 | 3862 | cpustate->program = &device->space(AS_PROGRAM); |
| r20642 | r20643 | |
| 3948 | 3962 | |
| 3949 | 3963 | static CPU_INIT( pentium_pro ) |
| 3950 | 3964 | { |
| 3951 | | CPU_INIT_CALL(pentium); |
| 3965 | // 64 dtlb small, 32 itlb |
| 3966 | i386_common_init(device, irqcallback, 96); |
| 3952 | 3967 | } |
| 3953 | 3968 | |
| 3954 | 3969 | static CPU_RESET( pentium_pro ) |
| 3955 | 3970 | { |
| 3956 | 3971 | i386_state *cpustate = get_safe_token(device); |
| 3957 | 3972 | device_irq_acknowledge_callback save_irqcallback; |
| 3973 | vtlb_state *save_vtlb = cpustate->vtlb; |
| 3958 | 3974 | |
| 3959 | 3975 | save_irqcallback = cpustate->irq_callback; |
| 3960 | 3976 | memset( cpustate, 0, sizeof(*cpustate) ); |
| 3977 | vtlb_flush_dynamic(save_vtlb); |
| 3978 | cpustate->vtlb = save_vtlb; |
| 3961 | 3979 | cpustate->irq_callback = save_irqcallback; |
| 3962 | 3980 | cpustate->device = device; |
| 3963 | 3981 | cpustate->program = &device->space(AS_PROGRAM); |
| r20642 | r20643 | |
| 4044 | 4062 | |
| 4045 | 4063 | static CPU_INIT( pentium_mmx ) |
| 4046 | 4064 | { |
| 4047 | | CPU_INIT_CALL(pentium); |
| 4065 | // 64 dtlb small, 8 dtlb large, 32 itlb small, 2 itlb large |
| 4066 | i386_common_init(device, irqcallback, 96); |
| 4048 | 4067 | } |
| 4049 | 4068 | |
| 4050 | 4069 | static CPU_RESET( pentium_mmx ) |
| 4051 | 4070 | { |
| 4052 | 4071 | i386_state *cpustate = get_safe_token(device); |
| 4053 | 4072 | device_irq_acknowledge_callback save_irqcallback; |
| 4073 | vtlb_state *save_vtlb = cpustate->vtlb; |
| 4054 | 4074 | |
| 4055 | 4075 | save_irqcallback = cpustate->irq_callback; |
| 4056 | 4076 | memset( cpustate, 0, sizeof(*cpustate) ); |
| 4077 | vtlb_flush_dynamic(save_vtlb); |
| 4078 | cpustate->vtlb = save_vtlb; |
| 4057 | 4079 | cpustate->irq_callback = save_irqcallback; |
| 4058 | 4080 | cpustate->device = device; |
| 4059 | 4081 | cpustate->program = &device->space(AS_PROGRAM); |
| r20642 | r20643 | |
| 4140 | 4162 | |
| 4141 | 4163 | static CPU_INIT( pentium2 ) |
| 4142 | 4164 | { |
| 4143 | | CPU_INIT_CALL(pentium); |
| 4165 | // 64 dtlb small, 8 dtlb large, 32 itlb small, 2 itlb large |
| 4166 | i386_common_init(device, irqcallback, 96); |
| 4144 | 4167 | } |
| 4145 | 4168 | |
| 4146 | 4169 | static CPU_RESET( pentium2 ) |
| 4147 | 4170 | { |
| 4148 | 4171 | i386_state *cpustate = get_safe_token(device); |
| 4149 | 4172 | device_irq_acknowledge_callback save_irqcallback; |
| 4173 | vtlb_state *save_vtlb = cpustate->vtlb; |
| 4150 | 4174 | |
| 4151 | 4175 | save_irqcallback = cpustate->irq_callback; |
| 4152 | 4176 | memset( cpustate, 0, sizeof(*cpustate) ); |
| 4177 | vtlb_flush_dynamic(save_vtlb); |
| 4178 | cpustate->vtlb = save_vtlb; |
| 4153 | 4179 | cpustate->irq_callback = save_irqcallback; |
| 4154 | 4180 | cpustate->device = device; |
| 4155 | 4181 | cpustate->program = &device->space(AS_PROGRAM); |
| r20642 | r20643 | |
| 4236 | 4262 | |
| 4237 | 4263 | static CPU_INIT( pentium3 ) |
| 4238 | 4264 | { |
| 4239 | | CPU_INIT_CALL(pentium); |
| 4265 | // 64 dtlb small, 8 dtlb large, 32 itlb small, 2 itlb large |
| 4266 | i386_common_init(device, irqcallback, 96); |
| 4240 | 4267 | } |
| 4241 | 4268 | |
| 4242 | 4269 | static CPU_RESET( pentium3 ) |
| 4243 | 4270 | { |
| 4244 | 4271 | i386_state *cpustate = get_safe_token(device); |
| 4245 | 4272 | device_irq_acknowledge_callback save_irqcallback; |
| 4273 | vtlb_state *save_vtlb = cpustate->vtlb; |
| 4246 | 4274 | |
| 4247 | 4275 | save_irqcallback = cpustate->irq_callback; |
| 4248 | 4276 | memset( cpustate, 0, sizeof(*cpustate) ); |
| 4277 | vtlb_flush_dynamic(save_vtlb); |
| 4278 | cpustate->vtlb = save_vtlb; |
| 4249 | 4279 | cpustate->irq_callback = save_irqcallback; |
| 4250 | 4280 | cpustate->device = device; |
| 4251 | 4281 | cpustate->program = &device->space(AS_PROGRAM); |
| r20642 | r20643 | |
| 4334 | 4364 | |
| 4335 | 4365 | static CPU_INIT( pentium4 ) |
| 4336 | 4366 | { |
| 4337 | | CPU_INIT_CALL(pentium); |
| 4367 | // 128 dtlb, 64 itlb |
| 4368 | i386_common_init(device, irqcallback, 196); |
| 4338 | 4369 | } |
| 4339 | 4370 | |
| 4340 | 4371 | static CPU_RESET( pentium4 ) |
| 4341 | 4372 | { |
| 4342 | 4373 | i386_state *cpustate = get_safe_token(device); |
| 4343 | 4374 | device_irq_acknowledge_callback save_irqcallback; |
| 4375 | vtlb_state *save_vtlb = cpustate->vtlb; |
| 4344 | 4376 | |
| 4345 | 4377 | save_irqcallback = cpustate->irq_callback; |
| 4346 | 4378 | memset( cpustate, 0, sizeof(*cpustate) ); |
| 4379 | vtlb_flush_dynamic(save_vtlb); |
| 4380 | cpustate->vtlb = save_vtlb; |
| 4347 | 4381 | cpustate->irq_callback = save_irqcallback; |
| 4348 | 4382 | cpustate->device = device; |
| 4349 | 4383 | cpustate->program = &device->space(AS_PROGRAM); |