diff options
Diffstat (limited to 'src/devices/cpu/i386/i386.cpp')
-rw-r--r-- | src/devices/cpu/i386/i386.cpp | 3738 |
1 files changed, 956 insertions, 2782 deletions
diff --git a/src/devices/cpu/i386/i386.cpp b/src/devices/cpu/i386/i386.cpp index 3c68bbf1471..c791e0b36a1 100644 --- a/src/devices/cpu/i386/i386.cpp +++ b/src/devices/cpu/i386/i386.cpp @@ -14,7 +14,7 @@ Intel Pentium Pro Intel Pentium II Intel Pentium III - Amd Athlon XP + Amd Athlon XP (athlon.cpp) Intel Pentium 4 */ @@ -41,7 +41,6 @@ DEFINE_DEVICE_TYPE(MEDIAGX, mediagx_device, "mediagx", "Cyrix MediaG DEFINE_DEVICE_TYPE(PENTIUM_PRO, pentium_pro_device, "pentium_pro", "Intel Pentium Pro") DEFINE_DEVICE_TYPE(PENTIUM2, pentium2_device, "pentium2", "Intel Pentium II") DEFINE_DEVICE_TYPE(PENTIUM3, pentium3_device, "pentium3", "Intel Pentium III") -DEFINE_DEVICE_TYPE(ATHLONXP, athlonxp_device, "athlonxp", "Amd Athlon XP") DEFINE_DEVICE_TYPE(PENTIUM4, pentium4_device, "pentium4", "Intel Pentium 4") @@ -131,15 +130,6 @@ pentium3_device::pentium3_device(const machine_config &mconfig, const char *tag, set_vtlb_dynamic_entries(96); } -athlonxp_device::athlonxp_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : pentium_device(mconfig, ATHLONXP, tag, owner, clock) - , m_data_config("mmio", ENDIANNESS_LITTLE, 32, 32, 0, 32, 12) - , m_opcodes_config("debugger", ENDIANNESS_LITTLE, 32, 32, 0, 32, 12) -{ - // TODO: put correct value - set_vtlb_dynamic_entries(256); -} - pentium4_device::pentium4_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : pentium_device(mconfig, PENTIUM4, tag, owner, clock) { @@ -319,173 +309,1015 @@ bool i386_device::translate_address(int pl, int type, uint32_t *address, uint32_ return true; } -uint32_t i386_device::i386_load_protected_mode_segment(I386_SREG *seg, uint64_t *desc ) +/***********************************************************************************/ + +void i386_device::CHANGE_PC(uint32_t pc) { - uint32_t v1,v2; - uint32_t base, limit; - int entry; + m_pc = i386_translate(CS, pc, -1 ); +} + +void i386_device::NEAR_BRANCH(int32_t offs) +{ + /* TODO: limit */ + m_eip += offs; + m_pc += offs; +} + +uint8_t i386_device::FETCH() +{ + uint8_t value; + uint32_t address = m_pc, error; + + if(!translate_address(m_CPL,TRANSLATE_FETCH,&address,&error)) + PF_THROW(error); + + value = mem_pr8(address & m_a20_mask); +#ifdef DEBUG_MISSING_OPCODE + m_opcode_bytes[m_opcode_bytes_length] = value; + m_opcode_bytes_length = (m_opcode_bytes_length + 1) & 15; +#endif + m_eip++; + m_pc++; + return value; +} +uint16_t i386_device::FETCH16() +{ + uint16_t value; + uint32_t address = m_pc, error; + + if( !WORD_ALIGNED(address) ) { /* Unaligned read */ + value = (FETCH() << 0); + value |= (FETCH() << 8); + } else { + if(!translate_address(m_CPL,TRANSLATE_FETCH,&address,&error)) + PF_THROW(error); + address &= m_a20_mask; + value = mem_pr16(address); + m_eip += 2; + m_pc += 2; + } + return value; +} +uint32_t i386_device::FETCH32() +{ + uint32_t value; + uint32_t address = m_pc, error; + + if( !DWORD_ALIGNED(m_pc) ) { /* Unaligned read */ + value = (FETCH() << 0); + value |= (FETCH() << 8); + value |= (FETCH() << 16); + value |= (FETCH() << 24); + } else { + if(!translate_address(m_CPL,TRANSLATE_FETCH,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + value = mem_pr32(address); + m_eip += 4; + m_pc += 4; + } + return value; +} + +uint8_t i386_device::READ8PL(uint32_t ea, uint8_t privilege) +{ + uint32_t address = ea, error; + + if(!translate_address(privilege,TRANSLATE_READ,&address,&error)) + PF_THROW(error); - if(!seg->selector) + address &= m_a20_mask; + return m_program->read_byte(address); +} + +uint16_t i386_device::READ16PL(uint32_t ea, uint8_t privilege) +{ + uint16_t value; + uint32_t address = ea, error; + + switch (ea & 3) { - seg->flags = 0; - seg->base = 0; - seg->limit = 0; - seg->d = 0; - seg->valid = false; - return 0; + case 0: + case 2: + if(!translate_address(privilege,TRANSLATE_READ,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + value = m_program->read_word(address); + break; + + case 1: + if(!translate_address(privilege,TRANSLATE_READ,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + value = m_program->read_dword(address - 1, 0x00ffff00) >> 8; + break; + + case 3: + value = READ8PL(ea, privilege); + value |= READ8PL(ea + 1, privilege) << 8; + break; } - if ( seg->selector & 0x4 ) + return value; +} + +uint32_t i386_device::READ32PL(uint32_t ea, uint8_t privilege) +{ + uint32_t value; + uint32_t address = ea, error; + + switch (ea & 3) { - base = m_ldtr.base; - limit = m_ldtr.limit; - } else { - base = m_gdtr.base; - limit = m_gdtr.limit; + case 0: + if(!translate_address(privilege,TRANSLATE_READ,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + value = m_program->read_dword(address); + break; + + case 1: + if(!translate_address(privilege,TRANSLATE_READ,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + value = m_program->read_dword(address - 1, 0xffffff00) >> 8; + value |= READ8PL(ea + 3, privilege) << 24; + break; + + case 2: + value = READ16PL(ea, privilege); + value |= READ16PL(ea + 2, privilege) << 16; + break; + + case 3: + value = READ8PL(ea, privilege); + + address = ea + 1; + if(!translate_address(privilege,TRANSLATE_READ,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + value |= m_program->read_dword(address, 0x00ffffff) << 8; + break; } - entry = seg->selector & ~0x7; - if (limit == 0 || entry + 7 > limit) - return 0; + return value; +} - v1 = READ32PL0(base + entry ); - v2 = READ32PL0(base + entry + 4 ); +uint64_t i386_device::READ64PL(uint32_t ea, uint8_t privilege) +{ + uint64_t value; + uint32_t address = ea, error; - seg->flags = (v2 >> 8) & 0xf0ff; - seg->base = (v2 & 0xff000000) | ((v2 & 0xff) << 16) | ((v1 >> 16) & 0xffff); - seg->limit = (v2 & 0xf0000) | (v1 & 0xffff); - if (seg->flags & 0x8000) - seg->limit = (seg->limit << 12) | 0xfff; - seg->d = (seg->flags & 0x4000) ? 1 : 0; - seg->valid = true; + switch (ea & 3) + { + case 0: + value = READ32PL(ea, privilege); + value |= uint64_t(READ32PL(ea + 2, privilege)) << 32; + break; - if(desc) - *desc = ((uint64_t)v2<<32)|v1; - return 1; + case 1: + if(!translate_address(privilege,TRANSLATE_READ,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + value = m_program->read_dword(address - 1, 0xffffff00) >> 8; + value |= uint64_t(READ32PL(ea + 3, privilege)) << 24; + value |= uint64_t(READ8PL(ea + 7, privilege)) << 56; + break; + + case 2: + value = READ16PL(ea, privilege); + value |= uint64_t(READ32PL(ea + 2, privilege)) << 16; + value |= uint64_t(READ16PL(ea + 6, privilege)) << 48; + break; + + case 3: + value = READ8PL(ea, privilege); + value |= uint64_t(READ32PL(ea + 1, privilege)) << 8; + + address = ea + 5; + if(!translate_address(privilege,TRANSLATE_READ,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + value |= uint64_t(m_program->read_dword(address, 0x00ffffff)) << 40; + break; + } + + return value; } -void i386_device::i386_load_call_gate(I386_CALL_GATE *gate) +uint16_t i386sx_device::READ16PL(uint32_t ea, uint8_t privilege) { - uint32_t v1,v2; - uint32_t base,limit; - int entry; + uint16_t value; + uint32_t address = ea, error; - if ( gate->segment & 0x4 ) + if (WORD_ALIGNED(ea)) { - base = m_ldtr.base; - limit = m_ldtr.limit; - } else { - base = m_gdtr.base; - limit = m_gdtr.limit; + if(!translate_address(privilege,TRANSLATE_READ,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + return m_program->read_word(address); + } + else + { + /* Unaligned read */ + value = READ8PL(ea, privilege); + value |= READ8PL(ea + 1, privilege) << 8; + return value; } +} - entry = gate->segment & ~0x7; - if (limit == 0 || entry + 7 > limit) - return; +uint32_t i386sx_device::READ32PL(uint32_t ea, uint8_t privilege) +{ + uint32_t value; + + if (WORD_ALIGNED(ea)) + { + value = READ16PL(ea, privilege); + value |= READ16PL(ea + 2, privilege) << 16; + return value; + } + else + { + value = READ8PL(ea, privilege); + value |= READ16PL(ea + 1, privilege) << 8; + value |= READ8PL(ea + 3, privilege) << 24; + return value; + } +} - v1 = READ32PL0(base + entry ); - v2 = READ32PL0(base + entry + 4 ); +uint64_t i386sx_device::READ64PL(uint32_t ea, uint8_t privilege) +{ + uint64_t value; - /* Note that for task gates, offset and dword_count are not used */ - gate->selector = (v1 >> 16) & 0xffff; - gate->offset = (v1 & 0x0000ffff) | (v2 & 0xffff0000); - gate->ar = (v2 >> 8) & 0xff; - gate->dword_count = v2 & 0x001f; - gate->present = (gate->ar >> 7) & 0x01; - gate->dpl = (gate->ar >> 5) & 0x03; + if (WORD_ALIGNED(ea)) + { + value = READ16PL(ea, privilege); + value |= uint64_t(READ16PL(ea + 2, privilege)) << 16; + value |= uint64_t(READ16PL(ea + 4, privilege)) << 32; + value |= uint64_t(READ16PL(ea + 6, privilege)) << 48; + return value; + } + else + { + value = READ8PL(ea, privilege); + value |= uint64_t(READ16PL(ea + 1, privilege)) << 8; + value |= uint64_t(READ16PL(ea + 3, privilege)) << 24; + value |= uint64_t(READ16PL(ea + 5, privilege)) << 40; + value |= uint64_t(READ8PL(ea + 7, privilege)) << 56; + return value; + } } -void i386_device::i386_set_descriptor_accessed(uint16_t selector) +void i386_device::WRITE_TEST(uint32_t ea) { - // assume the selector is valid, we don't need to check it again - uint32_t base, addr; - uint8_t rights; - if(!(selector & ~3)) - return; + uint32_t address = ea, error; + if(!translate_address(m_CPL,TRANSLATE_WRITE,&address,&error)) + PF_THROW(error); +} - if ( selector & 0x4 ) - base = m_ldtr.base; +void i386_device::WRITE8PL(uint32_t ea, uint8_t privilege, uint8_t value) +{ + uint32_t address = ea, error; + if(!translate_address(privilege,TRANSLATE_WRITE,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + m_program->write_byte(address, value); +} + +void i386_device::WRITE16PL(uint32_t ea, uint8_t privilege, uint16_t value) +{ + uint32_t address = ea, error; + + switch(ea & 3) + { + case 0: + case 2: + if(!translate_address(privilege,TRANSLATE_WRITE,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + m_program->write_word(address, value); + break; + + case 1: + if(!translate_address(privilege,TRANSLATE_WRITE,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + m_program->write_dword(address - 1, value << 8, 0x00ffff00); + break; + + case 3: + WRITE8PL(ea, privilege, value & 0xff); + WRITE8PL(ea + 1, privilege, (value >> 8) & 0xff); + break; + } +} + +void i386_device::WRITE32PL(uint32_t ea, uint8_t privilege, uint32_t value) +{ + uint32_t address = ea, error; + + switch(ea & 3) + { + case 0: + if(!translate_address(privilege,TRANSLATE_WRITE,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + m_program->write_dword(address, value); + break; + + case 1: + if(!translate_address(privilege,TRANSLATE_WRITE,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + m_program->write_dword(address - 1, (value << 8) & 0xffffff00, 0xffffff00); + WRITE8PL(ea + 3, privilege, (value >> 24) & 0xff); + break; + + case 2: + WRITE16PL(ea, privilege, value & 0xffff); + WRITE16PL(ea + 2, privilege, (value >> 16) & 0xffff); + break; + + case 3: + WRITE8PL(ea, privilege, value & 0xff); + + address = ea + 1; + if(!translate_address(privilege,TRANSLATE_WRITE,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + m_program->write_dword(address, value >> 8, 0x00ffffff); + break; + } +} + +void i386_device::WRITE64PL(uint32_t ea, uint8_t privilege, uint64_t value) +{ + uint32_t address = ea, error; + + switch(ea & 3) + { + case 0: + WRITE32PL(ea, privilege, value & 0xffffffff); + WRITE32PL(ea + 2, privilege, (value >> 32) & 0xffffffff); + break; + + case 1: + if(!translate_address(privilege,TRANSLATE_WRITE,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + m_program->write_dword(address - 1, value << 8, 0xffffff00); + WRITE32PL(ea + 3, privilege, (value >> 24) & 0xffffffff); + WRITE8PL(ea + 7, privilege, (value >> 56) & 0xff ); + break; + + case 2: + WRITE16PL(ea, privilege, value & 0xffff); + WRITE32PL(ea + 2, privilege, (value >> 16) & 0xffffffff); + WRITE16PL(ea + 6, privilege, (value >> 48) & 0xffff); + break; + + case 3: + WRITE8PL(ea, privilege, value & 0xff); + WRITE32PL(ea + 1, privilege, (value >> 8) & 0xffffffff); + + address = ea + 5; + if(!translate_address(privilege,TRANSLATE_WRITE,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + m_program->write_dword(address, (value >> 40) & 0x00ffffff, 0x00ffffff); + break; + } +} + +void i386sx_device::WRITE16PL(uint32_t ea, uint8_t privilege, uint16_t value) +{ + uint32_t address = ea, error; + + if (WORD_ALIGNED(ea)) + { + if(!translate_address(privilege,TRANSLATE_WRITE,&address,&error)) + PF_THROW(error); + + address &= m_a20_mask; + m_program->write_word(address, value); + } else - base = m_gdtr.base; + { + WRITE8PL(ea, privilege, value & 0xff); + WRITE8PL(ea + 1, privilege, (value >> 8) & 0xff); + } +} + +void i386sx_device::WRITE32PL(uint32_t ea, uint8_t privilege, uint32_t value) +{ + if (WORD_ALIGNED(ea)) + { + WRITE16PL(ea, privilege, value & 0xffff); + WRITE16PL(ea + 2, privilege, (value >> 16) & 0xffff); + } + else + { + WRITE8PL(ea, privilege, value & 0xff); + WRITE16PL(ea + 1, privilege, (value >> 8) & 0xffff); + WRITE8PL(ea + 3, privilege, (value >> 24) & 0xff); + } +} + +void i386sx_device::WRITE64PL(uint32_t ea, uint8_t privilege, uint64_t value) +{ + if (WORD_ALIGNED(ea)) + { + WRITE16PL(ea, privilege, value & 0xffff); + WRITE16PL(ea + 2, privilege, (value >> 16) & 0xffff); + WRITE16PL(ea + 4, privilege, (value >> 32) & 0xffff); + WRITE16PL(ea + 6, privilege, (value >> 48) & 0xffff); + } + else + { + WRITE8PL(ea, privilege, value & 0xff); + WRITE16PL(ea + 1, privilege, (value >> 8) & 0xffff); + WRITE16PL(ea + 3, privilege, (value >> 24) & 0xffff); + WRITE16PL(ea + 5, privilege, (value >> 40) & 0xffff); + WRITE8PL(ea + 7, privilege, (value >> 56) & 0xff); + } +} + +/***********************************************************************************/ + +uint8_t i386_device::OR8(uint8_t dst, uint8_t src) +{ + uint8_t res = dst | src; + m_CF = m_OF = 0; + SetSZPF8(res); + return res; +} +uint16_t i386_device::OR16(uint16_t dst, uint16_t src) +{ + uint16_t res = dst | src; + m_CF = m_OF = 0; + SetSZPF16(res); + return res; +} +uint32_t i386_device::OR32(uint32_t dst, uint32_t src) +{ + uint32_t res = dst | src; + m_CF = m_OF = 0; + SetSZPF32(res); + return res; +} - addr = base + (selector & ~7) + 5; - i386_translate_address(TRANSLATE_READ, &addr, nullptr); - rights = m_program->read_byte(addr); - // Should a fault be thrown if the table is read only? - m_program->write_byte(addr, rights | 1); +uint8_t i386_device::AND8(uint8_t dst, uint8_t src) +{ + uint8_t res = dst & src; + m_CF = m_OF = 0; + SetSZPF8(res); + return res; +} +uint16_t i386_device::AND16(uint16_t dst, uint16_t src) +{ + uint16_t res = dst & src; + m_CF = m_OF = 0; + SetSZPF16(res); + return res; +} +uint32_t i386_device::AND32(uint32_t dst, uint32_t src) +{ + uint32_t res = dst & src; + m_CF = m_OF = 0; + SetSZPF32(res); + return res; +} + +uint8_t i386_device::XOR8(uint8_t dst, uint8_t src) +{ + uint8_t res = dst ^ src; + m_CF = m_OF = 0; + SetSZPF8(res); + return res; +} +uint16_t i386_device::XOR16(uint16_t dst, uint16_t src) +{ + uint16_t res = dst ^ src; + m_CF = m_OF = 0; + SetSZPF16(res); + return res; +} +uint32_t i386_device::XOR32(uint32_t dst, uint32_t src) +{ + uint32_t res = dst ^ src; + m_CF = m_OF = 0; + SetSZPF32(res); + return res; +} + +#define SUB8(dst, src) SBB8(dst, src, 0) +uint8_t i386_device::SBB8(uint8_t dst, uint8_t src, uint8_t b) +{ + uint16_t res = (uint16_t)dst - (uint16_t)src - (uint8_t)b; + SetCF8(res); + SetOF_Sub8(res,src,dst); + SetAF(res,src,dst); + SetSZPF8(res); + return (uint8_t)res; } -void i386_device::i386_load_segment_descriptor(int segment ) +#define SUB16(dst, src) SBB16(dst, src, 0) +uint16_t i386_device::SBB16(uint16_t dst, uint16_t src, uint16_t b) +{ + uint32_t res = (uint32_t)dst - (uint32_t)src - (uint32_t)b; + SetCF16(res); + SetOF_Sub16(res,src,dst); + SetAF(res,src,dst); + SetSZPF16(res); + return (uint16_t)res; +} + +#define SUB32(dst, src) SBB32(dst, src, 0) +uint32_t i386_device::SBB32(uint32_t dst, uint32_t src, uint32_t b) +{ + uint64_t res = (uint64_t)dst - (uint64_t)src - (uint64_t) b; + SetCF32(res); + SetOF_Sub32(res,src,dst); + SetAF(res,src,dst); + SetSZPF32(res); + return (uint32_t)res; +} + +#define ADD8(dst, src) ADC8(dst, src, 0) +uint8_t i386_device::ADC8(uint8_t dst, uint8_t src, uint8_t c) +{ + uint16_t res = (uint16_t)dst + (uint16_t)src + (uint16_t)c; + SetCF8(res); + SetOF_Add8(res,src,dst); + SetAF(res,src,dst); + SetSZPF8(res); + return (uint8_t)res; +} + +#define ADD16(dst, src) ADC16(dst, src, 0) +uint16_t i386_device::ADC16(uint16_t dst, uint16_t src, uint8_t c) +{ + uint32_t res = (uint32_t)dst + (uint32_t)src + (uint32_t)c; + SetCF16(res); + SetOF_Add16(res,src,dst); + SetAF(res,src,dst); + SetSZPF16(res); + return (uint16_t)res; +} + +#define ADD32(dst, src) ADC32(dst, src, 0) +uint32_t i386_device::ADC32(uint32_t dst, uint32_t src, uint32_t c) +{ + uint64_t res = (uint64_t)dst + (uint64_t)src + (uint64_t) c; + SetCF32(res); + SetOF_Add32(res,src,dst); + SetAF(res,src,dst); + SetSZPF32(res); + return (uint32_t)res; +} + +uint8_t i386_device::INC8(uint8_t dst) +{ + uint16_t res = (uint16_t)dst + 1; + SetOF_Add8(res,1,dst); + SetAF(res,1,dst); + SetSZPF8(res); + return (uint8_t)res; +} +uint16_t i386_device::INC16(uint16_t dst) +{ + uint32_t res = (uint32_t)dst + 1; + SetOF_Add16(res,1,dst); + SetAF(res,1,dst); + SetSZPF16(res); + return (uint16_t)res; +} +uint32_t i386_device::INC32(uint32_t dst) +{ + uint64_t res = (uint64_t)dst + 1; + SetOF_Add32(res,1,dst); + SetAF(res,1,dst); + SetSZPF32(res); + return (uint32_t)res; +} + +uint8_t i386_device::DEC8(uint8_t dst) +{ + uint16_t res = (uint16_t)dst - 1; + SetOF_Sub8(res,1,dst); + SetAF(res,1,dst); + SetSZPF8(res); + return (uint8_t)res; +} +uint16_t i386_device::DEC16(uint16_t dst) +{ + uint32_t res = (uint32_t)dst - 1; + SetOF_Sub16(res,1,dst); + SetAF(res,1,dst); + SetSZPF16(res); + return (uint16_t)res; +} +uint32_t i386_device::DEC32(uint32_t dst) +{ + uint64_t res = (uint64_t)dst - 1; + SetOF_Sub32(res,1,dst); + SetAF(res,1,dst); + SetSZPF32(res); + return (uint32_t)res; +} + + + +void i386_device::PUSH16(uint16_t value) +{ + uint32_t ea, new_esp; + if( STACK_32BIT ) { + new_esp = REG32(ESP) - 2; + ea = i386_translate(SS, new_esp, 1); + WRITE16(ea, value ); + REG32(ESP) = new_esp; + } else { + new_esp = (REG16(SP) - 2) & 0xffff; + ea = i386_translate(SS, new_esp, 1); + WRITE16(ea, value ); + REG16(SP) = new_esp; + } +} +void i386_device::PUSH32(uint32_t value) +{ + uint32_t ea, new_esp; + if( STACK_32BIT ) { + new_esp = REG32(ESP) - 4; + ea = i386_translate(SS, new_esp, 1); + WRITE32(ea, value ); + REG32(ESP) = new_esp; + } else { + new_esp = (REG16(SP) - 4) & 0xffff; + ea = i386_translate(SS, new_esp, 1); + WRITE32(ea, value ); + REG16(SP) = new_esp; + } +} + +void i386_device::PUSH32SEG(uint32_t value) +{ + uint32_t ea, new_esp; + if( STACK_32BIT ) { + new_esp = REG32(ESP) - 4; + ea = i386_translate(SS, new_esp, 1); + ((m_cpu_version & 0xf00) == 0x300) ? WRITE16(ea, value) : WRITE32(ea, value ); // 486 also? + REG32(ESP) = new_esp; + } else { + new_esp = (REG16(SP) - 4) & 0xffff; + ea = i386_translate(SS, new_esp, 1); + ((m_cpu_version & 0xf00) == 0x300) ? WRITE16(ea, value) : WRITE32(ea, value ); + REG16(SP) = new_esp; + } +} + +void i386_device::PUSH8(uint8_t value) +{ + if( m_operand_size ) { + PUSH32((int32_t)(int8_t)value); + } else { + PUSH16((int16_t)(int8_t)value); + } +} + +uint8_t i386_device::POP8() +{ + uint8_t value; + uint32_t ea, new_esp; + if( STACK_32BIT ) { + new_esp = REG32(ESP) + 1; + ea = i386_translate(SS, new_esp - 1, 0); + value = READ8(ea ); + REG32(ESP) = new_esp; + } else { + new_esp = REG16(SP) + 1; + ea = i386_translate(SS, (new_esp - 1) & 0xffff, 0); + value = READ8(ea ); + REG16(SP) = new_esp; + } + return value; +} +uint16_t i386_device::POP16() +{ + uint16_t value; + uint32_t ea, new_esp; + if( STACK_32BIT ) { + new_esp = REG32(ESP) + 2; + ea = i386_translate(SS, new_esp - 2, 0); + value = READ16(ea ); + REG32(ESP) = new_esp; + } else { + new_esp = REG16(SP) + 2; + ea = i386_translate(SS, (new_esp - 2) & 0xffff, 0); + value = READ16(ea ); + REG16(SP) = new_esp; + } + return value; +} +uint32_t i386_device::POP32() +{ + uint32_t value; + uint32_t ea, new_esp; + if( STACK_32BIT ) { + new_esp = REG32(ESP) + 4; + ea = i386_translate(SS, new_esp - 4, 0); + value = READ32(ea ); + REG32(ESP) = new_esp; + } else { + new_esp = REG16(SP) + 4; + ea = i386_translate(SS, (new_esp - 4) & 0xffff, 0); + value = READ32(ea ); + REG16(SP) = new_esp; + } + return value; +} + +void i386_device::BUMP_SI(int adjustment) +{ + if ( m_address_size ) + REG32(ESI) += ((m_DF) ? -adjustment : +adjustment); + else + REG16(SI) += ((m_DF) ? -adjustment : +adjustment); +} + +void i386_device::BUMP_DI(int adjustment) +{ + if ( m_address_size ) + REG32(EDI) += ((m_DF) ? -adjustment : +adjustment); + else + REG16(DI) += ((m_DF) ? -adjustment : +adjustment); +} + +void i386_device::CYCLES(int x) { if (PROTECTED_MODE) { - uint16_t old_flags = m_sreg[segment].flags; - if (!V8086_MODE) + m_cycles -= m_cycle_table_pm[x]; + } + else + { + m_cycles -= m_cycle_table_rm[x]; + } +} + +void i386_device::CYCLES_RM(int modrm, int r, int m) +{ + if (modrm >= 0xc0) + { + if (PROTECTED_MODE) { - i386_load_protected_mode_segment(&m_sreg[segment], nullptr); - if (m_sreg[segment].selector) - { - i386_set_descriptor_accessed(m_sreg[segment].selector); - m_sreg[segment].flags |= 0x0001; - } + m_cycles -= m_cycle_table_pm[r]; } else { - m_sreg[segment].base = m_sreg[segment].selector << 4; - m_sreg[segment].limit = 0xffff; - m_sreg[segment].flags = (segment == CS) ? 0x00fb : 0x00f3; - m_sreg[segment].d = 0; - m_sreg[segment].valid = true; + m_cycles -= m_cycle_table_rm[r]; } - if (segment == CS && m_sreg[segment].flags != old_flags) - debugger_privilege_hook(); } else { - m_sreg[segment].base = m_sreg[segment].selector << 4; - m_sreg[segment].d = 0; - m_sreg[segment].valid = true; - - if (segment == CS) + if (PROTECTED_MODE) + { + m_cycles -= m_cycle_table_pm[m]; + } + else { - if (!m_performed_intersegment_jump) - m_sreg[segment].base |= 0xfff00000; - if (m_cpu_version < 0x500) - m_sreg[segment].flags = 0x93; + m_cycles -= m_cycle_table_rm[m]; } } } -/* Retrieves the stack selector located in the current TSS */ -uint32_t i386_device::i386_get_stack_segment(uint8_t privilege) +/**********************************************************************************/ + +void i386_device::check_ioperm(offs_t port, uint8_t mask) { - uint32_t ret; - if(privilege >= 3) - return 0; + uint8_t IOPL, map; + uint16_t IOPB; + uint32_t address; - if(m_task.flags & 8) - ret = READ32PL0((m_task.base+8) + (8*privilege)); - else - ret = READ16PL0((m_task.base+4) + (4*privilege)); + if(!PROTECTED_MODE) + return; - return ret; + IOPL = m_IOP1 | (m_IOP2 << 1); + if(!V8086_MODE && (m_CPL <= IOPL)) + return; + + if((m_task.limit < 0x67) || ((m_task.flags & 0xd) != 9)) + FAULT_THROW(FAULT_GP,0); + + address = m_task.base; + IOPB = READ16PL(address+0x66,0); + if((IOPB+(port/8)) > m_task.limit) + FAULT_THROW(FAULT_GP,0); + + map = READ8PL(address+IOPB+(port/8),0); + map >>= (port%8); + if(map & mask) + FAULT_THROW(FAULT_GP,0); } -/* Retrieves the stack pointer located in the current TSS */ -uint32_t i386_device::i386_get_stack_ptr(uint8_t privilege) +uint8_t i386_device::READPORT8(offs_t port) { - uint32_t ret; - if(privilege >= 3) - return 0; + check_ioperm(port, 1); + return m_io->read_byte(port); +} + +void i386_device::WRITEPORT8(offs_t port, uint8_t value) +{ + check_ioperm(port, 1); + m_io->write_byte(port, value); +} + +uint16_t i386_device::READPORT16(offs_t port) +{ + uint16_t value; + + switch (port & 3) + { + case 0: + case 2: + check_ioperm(port, 3); + value = m_io->read_word(port); + break; + + case 1: + check_ioperm(port, 3); + value = m_io->read_dword(port - 1, 0x00ffff00) >> 8; + break; + + case 3: + value = READPORT8(port); + value |= (READPORT8(port + 1) << 8); + break; + } + + return value; +} + +void i386_device::WRITEPORT16(offs_t port, uint16_t value) +{ + switch (port & 3) + { + case 0: + case 2: + check_ioperm(port, 3); + m_io->write_word(port, value); + break; + + case 1: + check_ioperm(port, 3); + m_io->write_dword(port - 1, value << 8, 0x00ffff00); + break; + + case 3: + WRITEPORT8(port, value & 0xff); + WRITEPORT8(port + 1, (value >> 8) & 0xff); + break; + } +} + +uint32_t i386_device::READPORT32(offs_t port) +{ + uint32_t value; + + switch (port & 3) + { + case 0: + check_ioperm(port, 0xf); + value = m_io->read_dword(port); + break; + + case 1: + check_ioperm(port, 7); + value = m_io->read_dword(port - 1, 0xffffff00) >> 8; + value |= READPORT8(port + 3) << 24; + break; + + case 2: + value = READPORT16(port); + value |= READPORT16(port + 2) << 16; + break; + + case 3: + value = READPORT8(port); + check_ioperm(port + 1, 7); + value |= m_io->read_dword(port + 1, 0x00ffffff) << 8; + break; + } + + return value; +} + +void i386_device::WRITEPORT32(offs_t port, uint32_t value) +{ + switch (port & 3) + { + case 0: + check_ioperm(port, 0xf); + m_io->write_dword(port, value); + break; - if(m_task.flags & 8) - ret = READ32PL0((m_task.base+4) + (8*privilege)); + case 1: + check_ioperm(port, 7); + m_io->write_dword(port - 1, value << 8, 0xffffff00); + WRITEPORT8(port + 3, (value >> 24) & 0xff); + break; + + case 2: + WRITEPORT16(port, value & 0xffff); + WRITEPORT16(port + 2, (value >> 16) & 0xffff); + break; + + case 3: + WRITEPORT8(port, value & 0xff); + check_ioperm(port + 1, 7); + m_io->write_dword(port + 1, value >> 8, 0x00ffffff); + break; + } +} + +uint16_t i386sx_device::READPORT16(offs_t port) +{ + if (port & 1) + { + uint16_t value = READPORT8(port); + value |= (READPORT8(port + 1) << 8); + return value; + } + else + { + check_ioperm(port, 3); + return m_io->read_word(port); + } +} + +void i386sx_device::WRITEPORT16(offs_t port, uint16_t value) +{ + if (port & 1) + { + WRITEPORT8(port, value & 0xff); + WRITEPORT8(port + 1, (value >> 8) & 0xff); + } else - ret = READ16PL0((m_task.base+2) + (4*privilege)); + { + check_ioperm(port, 3); + m_io->write_word(port, value); + } +} - return ret; +uint32_t i386sx_device::READPORT32(offs_t port) +{ + if (port & 1) + { + uint32_t value = READPORT8(port); + value |= (READPORT16(port + 1) << 8); + value |= (READPORT8(port + 3) << 24); + return value; + } + else + { + uint16_t value = READPORT16(port); + value |= (READPORT16(port + 2) << 16); + return value; + } +} + +void i386sx_device::WRITEPORT32(offs_t port, uint32_t value) +{ + if (port & 1) + { + WRITEPORT8(port, value & 0xff); + WRITEPORT16(port + 1, (value >> 8) & 0xffff); + WRITEPORT8(port + 3, (value >> 24) & 0xff); + } + else + { + WRITEPORT16(port, value & 0xffff); + WRITEPORT16((port + 2), (value >> 16) & 0xffff); + } } +/***********************************************************************************/ + uint32_t i386_device::get_flags() const { uint32_t f = 0x2; @@ -680,864 +1512,6 @@ uint32_t i386_device::GetEA(uint8_t modrm, int rwn) return i386_translate(segment, ea, rwn ); } -/* Check segment register for validity when changing privilege level after an RETF */ -void i386_device::i386_check_sreg_validity(int reg) -{ - uint16_t selector = m_sreg[reg].selector; - uint8_t CPL = m_CPL; - uint8_t DPL,RPL; - I386_SREG desc; - int invalid; - - memset(&desc, 0, sizeof(desc)); - desc.selector = selector; - i386_load_protected_mode_segment(&desc,nullptr); - DPL = (desc.flags >> 5) & 0x03; // descriptor privilege level - RPL = selector & 0x03; - - /* Must be within the relevant descriptor table limits */ - if(selector & 0x04) - { - if((selector & ~0x07) > m_ldtr.limit) - invalid = 1; - } - else - { - if((selector & ~0x07) > m_gdtr.limit) - invalid = 1; - } - - /* Must be either a data or readable code segment */ - if(((desc.flags & 0x0018) == 0x0018 && (desc.flags & 0x0002)) || (desc.flags & 0x0018) == 0x0010) - invalid = 0; - else - invalid = 1; - - /* If a data segment or non-conforming code segment, then either DPL >= CPL or DPL >= RPL */ - if(((desc.flags & 0x0018) == 0x0018 && (desc.flags & 0x0004) == 0) || (desc.flags & 0x0018) == 0x0010) - { - if((DPL < CPL) || (DPL < RPL)) - invalid = 1; - } - - /* if segment is invalid, then segment register is nulled */ - if(invalid != 0) - { - m_sreg[reg].selector = 0; - i386_load_segment_descriptor(reg); - } -} - -int i386_device::i386_limit_check(int seg, uint32_t offset) -{ - if(PROTECTED_MODE && !V8086_MODE) - { - if((m_sreg[seg].flags & 0x0018) == 0x0010 && m_sreg[seg].flags & 0x0004) // if expand-down data segment - { - // compare if greater then 0xffffffff when we're passed the access size - if((offset <= m_sreg[seg].limit) || ((m_sreg[seg].d)?0:(offset > 0xffff))) - { - logerror("Limit check at 0x%08x failed. Segment %04x, limit %08x, offset %08x (expand-down)\n",m_pc,m_sreg[seg].selector,m_sreg[seg].limit,offset); - return 1; - } - } - else - { - if(offset > m_sreg[seg].limit) - { - logerror("Limit check at 0x%08x failed. Segment %04x, limit %08x, offset %08x\n",m_pc,m_sreg[seg].selector,m_sreg[seg].limit,offset); - return 1; - } - } - } - return 0; -} - -void i386_device::i386_sreg_load(uint16_t selector, uint8_t reg, bool *fault) -{ - // Checks done when MOV changes a segment register in protected mode - uint8_t CPL,RPL,DPL; - - CPL = m_CPL; - RPL = selector & 0x0003; - - if(!PROTECTED_MODE || V8086_MODE) - { - m_sreg[reg].selector = selector; - i386_load_segment_descriptor(reg); - if(fault) *fault = false; - return; - } - - if(fault) *fault = true; - if(reg == SS) - { - I386_SREG stack; - - memset(&stack, 0, sizeof(stack)); - stack.selector = selector; - i386_load_protected_mode_segment(&stack,nullptr); - DPL = (stack.flags >> 5) & 0x03; - - if((selector & ~0x0003) == 0) - { - logerror("SReg Load (%08x): Selector is null.\n",m_pc); - FAULT(FAULT_GP,0) - } - if(selector & 0x0004) // LDT - { - if((selector & ~0x0007) > m_ldtr.limit) - { - logerror("SReg Load (%08x): Selector is out of LDT bounds.\n",m_pc); - FAULT(FAULT_GP,selector & ~0x03) - } - } - else // GDT - { - if((selector & ~0x0007) > m_gdtr.limit) - { - logerror("SReg Load (%08x): Selector is out of GDT bounds.\n",m_pc); - FAULT(FAULT_GP,selector & ~0x03) - } - } - if (RPL != CPL) - { - logerror("SReg Load (%08x): Selector RPL does not equal CPL.\n",m_pc); - FAULT(FAULT_GP,selector & ~0x03) - } - if(((stack.flags & 0x0018) != 0x10) && (stack.flags & 0x0002) != 0) - { - logerror("SReg Load (%08x): Segment is not a writable data segment.\n",m_pc); - FAULT(FAULT_GP,selector & ~0x03) - } - if(DPL != CPL) - { - logerror("SReg Load (%08x): Segment DPL does not equal CPL.\n",m_pc); - FAULT(FAULT_GP,selector & ~0x03) - } - if(!(stack.flags & 0x0080)) - { - logerror("SReg Load (%08x): Segment is not present.\n",m_pc); - FAULT(FAULT_SS,selector & ~0x03) - } - } - if(reg == DS || reg == ES || reg == FS || reg == GS) - { - I386_SREG desc; - - if((selector & ~0x0003) == 0) - { - m_sreg[reg].selector = selector; - i386_load_segment_descriptor(reg ); - if(fault) *fault = false; - return; - } - - memset(&desc, 0, sizeof(desc)); - desc.selector = selector; - i386_load_protected_mode_segment(&desc,nullptr); - DPL = (desc.flags >> 5) & 0x03; - - if(selector & 0x0004) // LDT - { - if((selector & ~0x0007) > m_ldtr.limit) - { - logerror("SReg Load (%08x): Selector is out of LDT bounds.\n",m_pc); - FAULT(FAULT_GP,selector & ~0x03) - } - } - else // GDT - { - if((selector & ~0x0007) > m_gdtr.limit) - { - logerror("SReg Load (%08x): Selector is out of GDT bounds.\n",m_pc); - FAULT(FAULT_GP,selector & ~0x03) - } - } - if((desc.flags & 0x0018) != 0x10) - { - if((((desc.flags & 0x0002) != 0) && ((desc.flags & 0x0018) != 0x18)) || !(desc.flags & 0x10)) - { - logerror("SReg Load (%08x): Segment is not a data segment or readable code segment.\n",m_pc); - FAULT(FAULT_GP,selector & ~0x03) - } - } - if(((desc.flags & 0x0018) == 0x10) || ((!(desc.flags & 0x0004)) && ((desc.flags & 0x0018) == 0x18))) - { - // if data or non-conforming code segment - if((RPL > DPL) || (CPL > DPL)) - { - logerror("SReg Load (%08x): Selector RPL or CPL is not less or equal to segment DPL.\n",m_pc); - FAULT(FAULT_GP,selector & ~0x03) - } - } - if(!(desc.flags & 0x0080)) - { - logerror("SReg Load (%08x): Segment is not present.\n",m_pc); - FAULT(FAULT_NP,selector & ~0x03) - } - } - - m_sreg[reg].selector = selector; - i386_load_segment_descriptor(reg ); - if(fault) *fault = false; -} - -void i386_device::i386_trap(int irq, int irq_gate, int trap_level) -{ - /* I386 Interrupts/Traps/Faults: - * - * 0x00 Divide by zero - * 0x01 Debug exception - * 0x02 NMI - * 0x03 Int3 - * 0x04 Overflow - * 0x05 Array bounds check - * 0x06 Illegal Opcode - * 0x07 FPU not available - * 0x08 Double fault - * 0x09 Coprocessor segment overrun - * 0x0a Invalid task state - * 0x0b Segment not present - * 0x0c Stack exception - * 0x0d General Protection Fault - * 0x0e Page fault - * 0x0f Reserved - * 0x10 Coprocessor error - */ - uint32_t v1, v2; - uint32_t offset, oldflags = get_flags(); - uint16_t segment; - int entry = irq * (PROTECTED_MODE ? 8 : 4); - int SetRPL; - m_lock = false; - - if( !(PROTECTED_MODE) ) - { - /* 16-bit */ - PUSH16(oldflags & 0xffff ); - PUSH16(m_sreg[CS].selector ); - if(irq == 3 || irq == 4 || irq == 9 || irq_gate == 1) - PUSH16(m_eip ); - else - PUSH16(m_prev_eip ); - - m_sreg[CS].selector = READ16(m_idtr.base + entry + 2 ); - m_eip = READ16(m_idtr.base + entry ); - - m_TF = 0; - m_IF = 0; - } - else - { - int type; - uint16_t flags; - I386_SREG desc; - uint8_t CPL = m_CPL, DPL; //, RPL = 0; - - /* 32-bit */ - v1 = READ32PL0(m_idtr.base + entry ); - v2 = READ32PL0(m_idtr.base + entry + 4 ); - offset = (v2 & 0xffff0000) | (v1 & 0xffff); - segment = (v1 >> 16) & 0xffff; - type = (v2>>8) & 0x1F; - flags = (v2>>8) & 0xf0ff; - - if(trap_level == 2) - { - logerror("IRQ: Double fault.\n"); - FAULT_EXP(FAULT_DF,0); - } - if(trap_level >= 3) - { - logerror("IRQ: Triple fault. CPU reset.\n"); - pulse_input_line(INPUT_LINE_RESET, attotime::zero); - return; - } - - /* segment privilege checks */ - if(entry >= m_idtr.limit) - { - logerror("IRQ (%08x): Vector %02xh is past IDT limit.\n",m_pc,entry); - FAULT_EXP(FAULT_GP,entry+2) - } - /* segment must be interrupt gate, trap gate, or task gate */ - if(type != 0x05 && type != 0x06 && type != 0x07 && type != 0x0e && type != 0x0f) - { - logerror("IRQ#%02x (%08x): Vector segment %04x is not an interrupt, trap or task gate.\n",irq,m_pc,segment); - FAULT_EXP(FAULT_GP,entry+2) - } - - if(m_ext == 0) // if software interrupt (caused by INT/INTO/INT3) - { - if(((flags >> 5) & 0x03) < CPL) - { - logerror("IRQ (%08x): Software IRQ - gate DPL is less than CPL.\n",m_pc); - FAULT_EXP(FAULT_GP,entry+2) - } - if(V8086_MODE) - { - if((!m_IOP1 || !m_IOP2) && (m_opcode != 0xcc)) - { - logerror("IRQ (%08x): Is in Virtual 8086 mode and IOPL != 3.\n",m_pc); - FAULT(FAULT_GP,0) - } - - } - } - - if((flags & 0x0080) == 0) - { - logerror("IRQ: Vector segment is not present.\n"); - FAULT_EXP(FAULT_NP,entry+2) - } - - if(type == 0x05) - { - /* Task gate */ - memset(&desc, 0, sizeof(desc)); - desc.selector = segment; - i386_load_protected_mode_segment(&desc,nullptr); - if(segment & 0x04) - { - logerror("IRQ: Task gate: TSS is not in the GDT.\n"); - FAULT_EXP(FAULT_TS,segment & ~0x03); - } - else - { - if(segment > m_gdtr.limit) - { - logerror("IRQ: Task gate: TSS is past GDT limit.\n"); - FAULT_EXP(FAULT_TS,segment & ~0x03); - } - } - if((desc.flags & 0x000f) != 0x09 && (desc.flags & 0x000f) != 0x01) - { - logerror("IRQ: Task gate: TSS is not an available TSS.\n"); - FAULT_EXP(FAULT_TS,segment & ~0x03); - } - if((desc.flags & 0x0080) == 0) - { - logerror("IRQ: Task gate: TSS is not present.\n"); - FAULT_EXP(FAULT_NP,segment & ~0x03); - } - if(!(irq == 3 || irq == 4 || irq == 9 || irq_gate == 1)) - m_eip = m_prev_eip; - if(desc.flags & 0x08) - i386_task_switch(desc.selector,1); - else - i286_task_switch(desc.selector,1); - return; - } - else - { - /* Interrupt or Trap gate */ - memset(&desc, 0, sizeof(desc)); - desc.selector = segment; - i386_load_protected_mode_segment(&desc,nullptr); - CPL = m_CPL; // current privilege level - DPL = (desc.flags >> 5) & 0x03; // descriptor privilege level -// RPL = segment & 0x03; // requested privilege level - - if((segment & ~0x03) == 0) - { - logerror("IRQ: Gate segment is null.\n"); - FAULT_EXP(FAULT_GP,m_ext) - } - if(segment & 0x04) - { - if((segment & ~0x07) > m_ldtr.limit) - { - logerror("IRQ: Gate segment is past LDT limit.\n"); - FAULT_EXP(FAULT_GP,(segment & 0x03)+m_ext) - } - } - else - { - if((segment & ~0x07) > m_gdtr.limit) - { - logerror("IRQ: Gate segment is past GDT limit.\n"); - FAULT_EXP(FAULT_GP,(segment & 0x03)+m_ext) - } - } - if((desc.flags & 0x0018) != 0x18) - { - logerror("IRQ: Gate descriptor is not a code segment.\n"); - FAULT_EXP(FAULT_GP,(segment & 0x03)+m_ext) - } - if((desc.flags & 0x0080) == 0) - { - logerror("IRQ: Gate segment is not present.\n"); - FAULT_EXP(FAULT_NP,(segment & 0x03)+m_ext) - } - if((desc.flags & 0x0004) == 0 && (DPL < CPL)) - { - /* IRQ to inner privilege */ - I386_SREG stack; - uint32_t newESP,oldSS,oldESP; - - if(V8086_MODE && DPL) - { - logerror("IRQ: Gate to CPL>0 from VM86 mode.\n"); - FAULT_EXP(FAULT_GP,segment & ~0x03); - } - /* Check new stack segment in TSS */ - memset(&stack, 0, sizeof(stack)); - stack.selector = i386_get_stack_segment(DPL); - i386_load_protected_mode_segment(&stack,nullptr); - oldSS = m_sreg[SS].selector; - if(flags & 0x0008) - oldESP = REG32(ESP); - else - oldESP = REG16(SP); - if((stack.selector & ~0x03) == 0) - { - logerror("IRQ: New stack selector is null.\n"); - FAULT_EXP(FAULT_GP,m_ext) - } - if(stack.selector & 0x04) - { - if((stack.selector & ~0x07) > m_ldtr.base) - { - logerror("IRQ: New stack selector is past LDT limit.\n"); - FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+m_ext) - } - } - else - { - if((stack.selector & ~0x07) > m_gdtr.base) - { - logerror("IRQ: New stack selector is past GDT limit.\n"); - FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+m_ext) - } - } - if((stack.selector & 0x03) != DPL) - { - logerror("IRQ: New stack selector RPL is not equal to code segment DPL.\n"); - FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+m_ext) - } - if(((stack.flags >> 5) & 0x03) != DPL) - { - logerror("IRQ: New stack segment DPL is not equal to code segment DPL.\n"); - FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+m_ext) - } - if(((stack.flags & 0x0018) != 0x10) && (stack.flags & 0x0002) != 0) - { - logerror("IRQ: New stack segment is not a writable data segment.\n"); - FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+m_ext) // #TS(stack selector + EXT) - } - if((stack.flags & 0x0080) == 0) - { - logerror("IRQ: New stack segment is not present.\n"); - FAULT_EXP(FAULT_SS,(stack.selector & ~0x03)+m_ext) // #TS(stack selector + EXT) - } - newESP = i386_get_stack_ptr(DPL); - if(type & 0x08) // 32-bit gate - { - if(((newESP < (V8086_MODE?36:20)) && !(stack.flags & 0x4)) || ((~stack.limit < (~(newESP - 1) + (V8086_MODE?36:20))) && (stack.flags & 0x4))) - { - logerror("IRQ: New stack has no space for return addresses.\n"); - FAULT_EXP(FAULT_SS,0) - } - } - else // 16-bit gate - { - newESP &= 0xffff; - if(((newESP < (V8086_MODE?18:10)) && !(stack.flags & 0x4)) || ((~stack.limit < (~(newESP - 1) + (V8086_MODE?18:10))) && (stack.flags & 0x4))) - { - logerror("IRQ: New stack has no space for return addresses.\n"); - FAULT_EXP(FAULT_SS,0) - } - } - if(offset > desc.limit) - { - logerror("IRQ: New EIP is past code segment limit.\n"); - FAULT_EXP(FAULT_GP,0) - } - /* change CPL before accessing the stack */ - m_CPL = DPL; - /* check for page fault at new stack TODO: check if stack frame crosses page boundary */ - WRITE_TEST(stack.base+newESP-1); - /* Load new stack segment descriptor */ - m_sreg[SS].selector = stack.selector; - i386_load_protected_mode_segment(&m_sreg[SS],nullptr); - i386_set_descriptor_accessed(stack.selector); - REG32(ESP) = newESP; - if(V8086_MODE) - { - //logerror("IRQ (%08x): Interrupt during V8086 task\n",m_pc); - if(type & 0x08) - { - PUSH32SEG(m_sreg[GS].selector & 0xffff); - PUSH32SEG(m_sreg[FS].selector & 0xffff); - PUSH32SEG(m_sreg[DS].selector & 0xffff); - PUSH32SEG(m_sreg[ES].selector & 0xffff); - } - else - { - PUSH16(m_sreg[GS].selector); - PUSH16(m_sreg[FS].selector); - PUSH16(m_sreg[DS].selector); - PUSH16(m_sreg[ES].selector); - } - m_sreg[GS].selector = 0; - m_sreg[FS].selector = 0; - m_sreg[DS].selector = 0; - m_sreg[ES].selector = 0; - m_VM = 0; - i386_load_segment_descriptor(GS); - i386_load_segment_descriptor(FS); - i386_load_segment_descriptor(DS); - i386_load_segment_descriptor(ES); - } - if(type & 0x08) - { - // 32-bit gate - PUSH32SEG(oldSS); - PUSH32(oldESP); - } - else - { - // 16-bit gate - PUSH16(oldSS); - PUSH16(oldESP); - } - SetRPL = 1; - } - else - { - int stack_limit; - if((desc.flags & 0x0004) || (DPL == CPL)) - { - /* IRQ to same privilege */ - if(V8086_MODE && !m_ext) - { - logerror("IRQ: Gate to same privilege from VM86 mode.\n"); - FAULT_EXP(FAULT_GP,segment & ~0x03); - } - if(type == 0x0e || type == 0x0f) // 32-bit gate - stack_limit = 10; - else - stack_limit = 6; - // TODO: Add check for error code (2 extra bytes) - if(REG32(ESP) < stack_limit) - { - logerror("IRQ: Stack has no space left (needs %i bytes).\n",stack_limit); - FAULT_EXP(FAULT_SS,0) - } - if(offset > desc.limit) - { - logerror("IRQ: Gate segment offset is past segment limit.\n"); - FAULT_EXP(FAULT_GP,0) - } - SetRPL = 1; - } - else - { - logerror("IRQ: Gate descriptor is non-conforming, and DPL does not equal CPL.\n"); - FAULT_EXP(FAULT_GP,segment) - } - } - } - uint32_t tempSP = REG32(ESP); - try - { - // this is ugly but the alternative is worse - if(type != 0x0e && type != 0x0f) // if not 386 interrupt or trap gate - { - PUSH16(oldflags & 0xffff ); - PUSH16(m_sreg[CS].selector ); - if(irq == 3 || irq == 4 || irq == 9 || irq_gate == 1) - PUSH16(m_eip ); - else - PUSH16(m_prev_eip ); - } - else - { - PUSH32(oldflags & 0x00ffffff ); - PUSH32SEG(m_sreg[CS].selector ); - if(irq == 3 || irq == 4 || irq == 9 || irq_gate == 1) - PUSH32(m_eip ); - else - PUSH32(m_prev_eip ); - } - } - catch(uint64_t e) - { - REG32(ESP) = tempSP; - throw e; - } - if(SetRPL != 0) - segment = (segment & ~0x03) | m_CPL; - m_sreg[CS].selector = segment; - m_eip = offset; - - if(type == 0x0e || type == 0x06) - m_IF = 0; - m_TF = 0; - m_NT = 0; - } - - i386_load_segment_descriptor(CS); - CHANGE_PC(m_eip); - -} - -void i386_device::i386_trap_with_error(int irq, int irq_gate, int trap_level, uint32_t error) -{ - i386_trap(irq,irq_gate,trap_level); - if(irq == 8 || irq == 10 || irq == 11 || irq == 12 || irq == 13 || irq == 14) - { - // for these exceptions, an error code is pushed onto the stack by the processor. - // no error code is pushed for software interrupts, either. - if(PROTECTED_MODE) - { - uint32_t entry = irq * 8; - uint32_t v2,type; - v2 = READ32PL0(m_idtr.base + entry + 4 ); - type = (v2>>8) & 0x1F; - if(type == 5) - { - v2 = READ32PL0(m_idtr.base + entry); - v2 = READ32PL0(m_gdtr.base + ((v2 >> 16) & 0xfff8) + 4); - type = (v2>>8) & 0x1F; - } - if(type >= 9) - PUSH32(error); - else - PUSH16(error); - } - else - PUSH16(error); - } -} - - -void i386_device::i286_task_switch(uint16_t selector, uint8_t nested) -{ - uint32_t tss; - I386_SREG seg; - uint16_t old_task; - uint8_t ar_byte; // access rights byte - - /* TODO: Task State Segment privilege checks */ - - /* For tasks that aren't nested, clear the busy bit in the task's descriptor */ - if(nested == 0) - { - if(m_task.segment & 0x0004) - { - ar_byte = READ8(m_ldtr.base + (m_task.segment & ~0x0007) + 5); - WRITE8(m_ldtr.base + (m_task.segment & ~0x0007) + 5,ar_byte & ~0x02); - } - else - { - ar_byte = READ8(m_gdtr.base + (m_task.segment & ~0x0007) + 5); - WRITE8(m_gdtr.base + (m_task.segment & ~0x0007) + 5,ar_byte & ~0x02); - } - } - - /* Save the state of the current task in the current TSS (TR register base) */ - tss = m_task.base; - WRITE16(tss+0x0e,m_eip & 0x0000ffff); - WRITE16(tss+0x10,get_flags() & 0x0000ffff); - WRITE16(tss+0x12,REG16(AX)); - WRITE16(tss+0x14,REG16(CX)); - WRITE16(tss+0x16,REG16(DX)); - WRITE16(tss+0x18,REG16(BX)); - WRITE16(tss+0x1a,REG16(SP)); - WRITE16(tss+0x1c,REG16(BP)); - WRITE16(tss+0x1e,REG16(SI)); - WRITE16(tss+0x20,REG16(DI)); - WRITE16(tss+0x22,m_sreg[ES].selector); - WRITE16(tss+0x24,m_sreg[CS].selector); - WRITE16(tss+0x26,m_sreg[SS].selector); - WRITE16(tss+0x28,m_sreg[DS].selector); - - old_task = m_task.segment; - - /* Load task register with the selector of the incoming task */ - m_task.segment = selector; - memset(&seg, 0, sizeof(seg)); - seg.selector = m_task.segment; - i386_load_protected_mode_segment(&seg,nullptr); - m_task.limit = seg.limit; - m_task.base = seg.base; - m_task.flags = seg.flags; - - /* Set TS bit in CR0 */ - m_cr[0] |= 0x08; - - /* Load incoming task state from the new task's TSS */ - tss = m_task.base; - m_ldtr.segment = READ16(tss+0x2a) & 0xffff; - seg.selector = m_ldtr.segment; - i386_load_protected_mode_segment(&seg,nullptr); - m_ldtr.limit = seg.limit; - m_ldtr.base = seg.base; - m_ldtr.flags = seg.flags; - m_eip = READ16(tss+0x0e); - set_flags(READ16(tss+0x10)); - REG16(AX) = READ16(tss+0x12); - REG16(CX) = READ16(tss+0x14); - REG16(DX) = READ16(tss+0x16); - REG16(BX) = READ16(tss+0x18); - REG16(SP) = READ16(tss+0x1a); - REG16(BP) = READ16(tss+0x1c); - REG16(SI) = READ16(tss+0x1e); - REG16(DI) = READ16(tss+0x20); - m_sreg[ES].selector = READ16(tss+0x22) & 0xffff; - i386_load_segment_descriptor(ES); - m_sreg[CS].selector = READ16(tss+0x24) & 0xffff; - i386_load_segment_descriptor(CS); - m_sreg[SS].selector = READ16(tss+0x26) & 0xffff; - i386_load_segment_descriptor(SS); - m_sreg[DS].selector = READ16(tss+0x28) & 0xffff; - i386_load_segment_descriptor(DS); - - /* Set the busy bit in the new task's descriptor */ - if(selector & 0x0004) - { - ar_byte = READ8(m_ldtr.base + (selector & ~0x0007) + 5); - WRITE8(m_ldtr.base + (selector & ~0x0007) + 5,ar_byte | 0x02); - } - else - { - ar_byte = READ8(m_gdtr.base + (selector & ~0x0007) + 5); - WRITE8(m_gdtr.base + (selector & ~0x0007) + 5,ar_byte | 0x02); - } - - /* For nested tasks, we write the outgoing task's selector to the back-link field of the new TSS, - and set the NT flag in the EFLAGS register */ - if(nested != 0) - { - WRITE16(tss+0,old_task); - m_NT = 1; - } - CHANGE_PC(m_eip); - - m_CPL = (m_sreg[SS].flags >> 5) & 3; -// printf("286 Task Switch from selector %04x to %04x\n",old_task,selector); -} - -void i386_device::i386_task_switch(uint16_t selector, uint8_t nested) -{ - uint32_t tss; - I386_SREG seg; - uint16_t old_task; - uint8_t ar_byte; // access rights byte - uint32_t oldcr3 = m_cr[3]; - - /* TODO: Task State Segment privilege checks */ - - /* For tasks that aren't nested, clear the busy bit in the task's descriptor */ - if(nested == 0) - { - if(m_task.segment & 0x0004) - { - ar_byte = READ8(m_ldtr.base + (m_task.segment & ~0x0007) + 5); - WRITE8(m_ldtr.base + (m_task.segment & ~0x0007) + 5,ar_byte & ~0x02); - } - else - { - ar_byte = READ8(m_gdtr.base + (m_task.segment & ~0x0007) + 5); - WRITE8(m_gdtr.base + (m_task.segment & ~0x0007) + 5,ar_byte & ~0x02); - } - } - - /* Save the state of the current task in the current TSS (TR register base) */ - tss = m_task.base; - WRITE32(tss+0x1c,m_cr[3]); // correct? - WRITE32(tss+0x20,m_eip); - WRITE32(tss+0x24,get_flags()); - WRITE32(tss+0x28,REG32(EAX)); - WRITE32(tss+0x2c,REG32(ECX)); - WRITE32(tss+0x30,REG32(EDX)); - WRITE32(tss+0x34,REG32(EBX)); - WRITE32(tss+0x38,REG32(ESP)); - WRITE32(tss+0x3c,REG32(EBP)); - WRITE32(tss+0x40,REG32(ESI)); - WRITE32(tss+0x44,REG32(EDI)); - WRITE32(tss+0x48,m_sreg[ES].selector); - WRITE32(tss+0x4c,m_sreg[CS].selector); - WRITE32(tss+0x50,m_sreg[SS].selector); - WRITE32(tss+0x54,m_sreg[DS].selector); - WRITE32(tss+0x58,m_sreg[FS].selector); - WRITE32(tss+0x5c,m_sreg[GS].selector); - - old_task = m_task.segment; - - /* Load task register with the selector of the incoming task */ - m_task.segment = selector; - memset(&seg, 0, sizeof(seg)); - seg.selector = m_task.segment; - i386_load_protected_mode_segment(&seg,nullptr); - m_task.limit = seg.limit; - m_task.base = seg.base; - m_task.flags = seg.flags; - - /* Set TS bit in CR0 */ - m_cr[0] |= 0x08; - - /* Load incoming task state from the new task's TSS */ - tss = m_task.base; - m_ldtr.segment = READ32(tss+0x60) & 0xffff; - seg.selector = m_ldtr.segment; - i386_load_protected_mode_segment(&seg,nullptr); - m_ldtr.limit = seg.limit; - m_ldtr.base = seg.base; - m_ldtr.flags = seg.flags; - m_eip = READ32(tss+0x20); - set_flags(READ32(tss+0x24)); - REG32(EAX) = READ32(tss+0x28); - REG32(ECX) = READ32(tss+0x2c); - REG32(EDX) = READ32(tss+0x30); - REG32(EBX) = READ32(tss+0x34); - REG32(ESP) = READ32(tss+0x38); - REG32(EBP) = READ32(tss+0x3c); - REG32(ESI) = READ32(tss+0x40); - REG32(EDI) = READ32(tss+0x44); - m_sreg[ES].selector = READ32(tss+0x48) & 0xffff; - i386_load_segment_descriptor(ES); - m_sreg[CS].selector = READ32(tss+0x4c) & 0xffff; - i386_load_segment_descriptor(CS); - m_sreg[SS].selector = READ32(tss+0x50) & 0xffff; - i386_load_segment_descriptor(SS); - m_sreg[DS].selector = READ32(tss+0x54) & 0xffff; - i386_load_segment_descriptor(DS); - m_sreg[FS].selector = READ32(tss+0x58) & 0xffff; - i386_load_segment_descriptor(FS); - m_sreg[GS].selector = READ32(tss+0x5c) & 0xffff; - i386_load_segment_descriptor(GS); - /* For nested tasks, we write the outgoing task's selector to the back-link field of the new TSS, - and set the NT flag in the EFLAGS register before setting cr3 as the old tss address might be gone */ - if(nested != 0) - { - WRITE32(tss+0,old_task); - m_NT = 1; - } - m_cr[3] = READ32(tss+0x1c); // CR3 (PDBR) - if(oldcr3 != m_cr[3]) - vtlb_flush_dynamic(); - - /* Set the busy bit in the new task's descriptor */ - if(selector & 0x0004) - { - ar_byte = READ8(m_ldtr.base + (selector & ~0x0007) + 5); - WRITE8(m_ldtr.base + (selector & ~0x0007) + 5,ar_byte | 0x02); - } - else - { - ar_byte = READ8(m_gdtr.base + (selector & ~0x0007) + 5); - WRITE8(m_gdtr.base + (selector & ~0x0007) + 5,ar_byte | 0x02); - } - - CHANGE_PC(m_eip); - - m_CPL = (m_sreg[SS].flags >> 5) & 3; -// printf("386 Task Switch from selector %04x to %04x\n",old_task,selector); -} - void i386_device::i386_check_irq_line() { if(!m_smm && m_smi) @@ -1554,1442 +1528,6 @@ void i386_device::i386_check_irq_line() } } -void i386_device::i386_protected_mode_jump(uint16_t seg, uint32_t off, int indirect, int operand32) -{ - I386_SREG desc; - I386_CALL_GATE call_gate; - uint8_t CPL,DPL,RPL; - uint8_t SetRPL; - uint16_t segment = seg; - uint32_t offset = off; - - /* Check selector is not null */ - if((segment & ~0x03) == 0) - { - logerror("JMP: Segment is null.\n"); - FAULT(FAULT_GP,0) - } - /* Selector is within descriptor table limit */ - if((segment & 0x04) == 0) - { - /* check GDT limit */ - if((segment & ~0x07) > (m_gdtr.limit)) - { - logerror("JMP: Segment is past GDT limit.\n"); - FAULT(FAULT_GP,segment & 0xfffc) - } - } - else - { - /* check LDT limit */ - if((segment & ~0x07) > (m_ldtr.limit)) - { - logerror("JMP: Segment is past LDT limit.\n"); - FAULT(FAULT_GP,segment & 0xfffc) - } - } - /* Determine segment type */ - memset(&desc, 0, sizeof(desc)); - desc.selector = segment; - i386_load_protected_mode_segment(&desc,nullptr); - CPL = m_CPL; // current privilege level - DPL = (desc.flags >> 5) & 0x03; // descriptor privilege level - RPL = segment & 0x03; // requested privilege level - if((desc.flags & 0x0018) == 0x0018) - { - /* code segment */ - if((desc.flags & 0x0004) == 0) - { - /* non-conforming */ - if(RPL > CPL) - { - logerror("JMP: RPL %i is less than CPL %i\n",RPL,CPL); - FAULT(FAULT_GP,segment & 0xfffc) - } - if(DPL != CPL) - { - logerror("JMP: DPL %i is not equal CPL %i\n",DPL,CPL); - FAULT(FAULT_GP,segment & 0xfffc) - } - } - else - { - /* conforming */ - if(DPL > CPL) - { - logerror("JMP: DPL %i is less than CPL %i\n",DPL,CPL); - FAULT(FAULT_GP,segment & 0xfffc) - } - } - SetRPL = 1; - if((desc.flags & 0x0080) == 0) - { - logerror("JMP: Segment is not present\n"); - FAULT(FAULT_NP,segment & 0xfffc) - } - if(offset > desc.limit) - { - logerror("JMP: Offset is past segment limit\n"); - FAULT(FAULT_GP,0) - } - } - else - { - if((desc.flags & 0x0010) != 0) - { - logerror("JMP: Segment is a data segment\n"); - FAULT(FAULT_GP,segment & 0xfffc) // #GP (cannot execute code in a data segment) - } - else - { - switch(desc.flags & 0x000f) - { - case 0x01: // 286 Available TSS - case 0x09: // 386 Available TSS - logerror("JMP: Available 386 TSS at %08x\n",m_pc); - memset(&desc, 0, sizeof(desc)); - desc.selector = segment; - i386_load_protected_mode_segment(&desc,nullptr); - DPL = (desc.flags >> 5) & 0x03; // descriptor privilege level - if(DPL < CPL) - { - logerror("JMP: TSS: DPL %i is less than CPL %i\n",DPL,CPL); - FAULT(FAULT_GP,segment & 0xfffc) - } - if(DPL < RPL) - { - logerror("JMP: TSS: DPL %i is less than TSS RPL %i\n",DPL,RPL); - FAULT(FAULT_GP,segment & 0xfffc) - } - if((desc.flags & 0x0080) == 0) - { - logerror("JMP: TSS: Segment is not present\n"); - FAULT(FAULT_GP,segment & 0xfffc) - } - if(desc.flags & 0x0008) - i386_task_switch(desc.selector,0); - else - i286_task_switch(desc.selector,0); - return; - case 0x04: // 286 Call Gate - case 0x0c: // 386 Call Gate - //logerror("JMP: Call gate at %08x\n",m_pc); - SetRPL = 1; - memset(&call_gate, 0, sizeof(call_gate)); - call_gate.segment = segment; - i386_load_call_gate(&call_gate); - DPL = call_gate.dpl; - if(DPL < CPL) - { - logerror("JMP: Call Gate: DPL %i is less than CPL %i\n",DPL,CPL); - FAULT(FAULT_GP,segment & 0xfffc) - } - if(DPL < RPL) - { - logerror("JMP: Call Gate: DPL %i is less than RPL %i\n",DPL,RPL); - FAULT(FAULT_GP,segment & 0xfffc) - } - if((desc.flags & 0x0080) == 0) - { - logerror("JMP: Call Gate: Segment is not present\n"); - FAULT(FAULT_NP,segment & 0xfffc) - } - /* Now we examine the segment that the call gate refers to */ - if(call_gate.selector == 0) - { - logerror("JMP: Call Gate: Gate selector is null\n"); - FAULT(FAULT_GP,0) - } - if(call_gate.selector & 0x04) - { - if((call_gate.selector & ~0x07) > m_ldtr.limit) - { - logerror("JMP: Call Gate: Gate Selector is past LDT segment limit\n"); - FAULT(FAULT_GP,call_gate.selector & 0xfffc) - } - } - else - { - if((call_gate.selector & ~0x07) > m_gdtr.limit) - { - logerror("JMP: Call Gate: Gate Selector is past GDT segment limit\n"); - FAULT(FAULT_GP,call_gate.selector & 0xfffc) - } - } - desc.selector = call_gate.selector; - i386_load_protected_mode_segment(&desc,nullptr); - DPL = (desc.flags >> 5) & 0x03; - if((desc.flags & 0x0018) != 0x18) - { - logerror("JMP: Call Gate: Gate does not point to a code segment\n"); - FAULT(FAULT_GP,call_gate.selector & 0xfffc) - } - if((desc.flags & 0x0004) == 0) - { // non-conforming - if(DPL != CPL) - { - logerror("JMP: Call Gate: Gate DPL does not equal CPL\n"); - FAULT(FAULT_GP,call_gate.selector & 0xfffc) - } - } - else - { // conforming - if(DPL > CPL) - { - logerror("JMP: Call Gate: Gate DPL is greater than CPL\n"); - FAULT(FAULT_GP,call_gate.selector & 0xfffc) - } - } - if((desc.flags & 0x0080) == 0) - { - logerror("JMP: Call Gate: Gate Segment is not present\n"); - FAULT(FAULT_NP,call_gate.selector & 0xfffc) - } - if(call_gate.offset > desc.limit) - { - logerror("JMP: Call Gate: Gate offset is past Gate segment limit\n"); - FAULT(FAULT_GP,call_gate.selector & 0xfffc) - } - segment = call_gate.selector; - offset = call_gate.offset; - break; - case 0x05: // Task Gate - logerror("JMP: Task gate at %08x\n",m_pc); - memset(&call_gate, 0, sizeof(call_gate)); - call_gate.segment = segment; - i386_load_call_gate(&call_gate); - DPL = call_gate.dpl; - if(DPL < CPL) - { - logerror("JMP: Task Gate: Gate DPL %i is less than CPL %i\n",DPL,CPL); - FAULT(FAULT_GP,segment & 0xfffc) - } - if(DPL < RPL) - { - logerror("JMP: Task Gate: Gate DPL %i is less than CPL %i\n",DPL,CPL); - FAULT(FAULT_GP,segment & 0xfffc) - } - if(call_gate.present == 0) - { - logerror("JMP: Task Gate: Gate is not present.\n"); - FAULT(FAULT_GP,segment & 0xfffc) - } - /* Check the TSS that the task gate points to */ - desc.selector = call_gate.selector; - i386_load_protected_mode_segment(&desc,nullptr); - DPL = (desc.flags >> 5) & 0x03; // descriptor privilege level - RPL = call_gate.selector & 0x03; // requested privilege level - if(call_gate.selector & 0x04) - { - logerror("JMP: Task Gate TSS: TSS must be global.\n"); - FAULT(FAULT_GP,call_gate.selector & 0xfffc) - } - else - { - if((call_gate.selector & ~0x07) > m_gdtr.limit) - { - logerror("JMP: Task Gate TSS: TSS is past GDT limit.\n"); - FAULT(FAULT_GP,call_gate.selector & 0xfffc) - } - } - if((call_gate.ar & 0x000f) == 0x0009 || (call_gate.ar & 0x000f) == 0x0001) - { - logerror("JMP: Task Gate TSS: Segment is not an available TSS.\n"); - FAULT(FAULT_GP,call_gate.selector & 0xfffc) - } - if(call_gate.present == 0) - { - logerror("JMP: Task Gate TSS: TSS is not present.\n"); - FAULT(FAULT_NP,call_gate.selector & 0xfffc) - } - if(call_gate.ar & 0x08) - i386_task_switch(call_gate.selector,0); - else - i286_task_switch(call_gate.selector,0); - return; - default: // invalid segment type - logerror("JMP: Invalid segment type (%i) to jump to.\n",desc.flags & 0x000f); - FAULT(FAULT_GP,segment & 0xfffc) - } - } - } - - if(SetRPL != 0) - segment = (segment & ~0x03) | m_CPL; - if(operand32 == 0) - m_eip = offset & 0x0000ffff; - else - m_eip = offset; - m_sreg[CS].selector = segment; - m_performed_intersegment_jump = 1; - i386_load_segment_descriptor(CS); - CHANGE_PC(m_eip); -} - -void i386_device::i386_protected_mode_call(uint16_t seg, uint32_t off, int indirect, int operand32) -{ - I386_SREG desc; - I386_CALL_GATE gate; - uint8_t SetRPL; - uint8_t CPL, DPL, RPL; - uint16_t selector = seg; - uint32_t offset = off; - int x; - - if((selector & ~0x03) == 0) - { - logerror("CALL (%08x): Selector is null.\n",m_pc); - FAULT(FAULT_GP,0) // #GP(0) - } - if(selector & 0x04) - { - if((selector & ~0x07) > m_ldtr.limit) - { - logerror("CALL: Selector is past LDT limit.\n"); - FAULT(FAULT_GP,selector & ~0x03) // #GP(selector) - } - } - else - { - if((selector & ~0x07) > m_gdtr.limit) - { - logerror("CALL: Selector is past GDT limit.\n"); - FAULT(FAULT_GP,selector & ~0x03) // #GP(selector) - } - } - - /* Determine segment type */ - memset(&desc, 0, sizeof(desc)); - desc.selector = selector; - i386_load_protected_mode_segment(&desc,nullptr); - CPL = m_CPL; // current privilege level - DPL = (desc.flags >> 5) & 0x03; // descriptor privilege level - RPL = selector & 0x03; // requested privilege level - if((desc.flags & 0x0018) == 0x18) // is a code segment - { - if(desc.flags & 0x0004) - { - /* conforming */ - if(DPL > CPL) - { - logerror("CALL: Code segment DPL %i is greater than CPL %i\n",DPL,CPL); - FAULT(FAULT_GP,selector & ~0x03) // #GP(selector) - } - } - else - { - /* non-conforming */ - if(RPL > CPL) - { - logerror("CALL: RPL %i is greater than CPL %i\n",RPL,CPL); - FAULT(FAULT_GP,selector & ~0x03) // #GP(selector) - } - if(DPL != CPL) - { - logerror("CALL: Code segment DPL %i is not equal to CPL %i\n",DPL,CPL); - FAULT(FAULT_GP,selector & ~0x03) // #GP(selector) - } - } - SetRPL = 1; - if((desc.flags & 0x0080) == 0) - { - logerror("CALL (%08x): Code segment is not present.\n",m_pc); - FAULT(FAULT_NP,selector & ~0x03) // #NP(selector) - } - if (operand32 != 0) // if 32-bit - { - uint32_t offset = (STACK_32BIT ? REG32(ESP) - 8 : (REG16(SP) - 8) & 0xffff); - if(i386_limit_check(SS, offset)) - { - logerror("CALL (%08x): Stack has no room for return address.\n",m_pc); - FAULT(FAULT_SS,0) // #SS(0) - } - } - else - { - uint32_t offset = (STACK_32BIT ? REG32(ESP) - 4 : (REG16(SP) - 4) & 0xffff); - if(i386_limit_check(SS, offset)) - { - logerror("CALL (%08x): Stack has no room for return address.\n",m_pc); - FAULT(FAULT_SS,0) // #SS(0) - } - } - if(offset > desc.limit) - { - logerror("CALL: EIP is past segment limit.\n"); - FAULT(FAULT_GP,0) // #GP(0) - } - } - else - { - /* special segment type */ - if(desc.flags & 0x0010) - { - logerror("CALL: Segment is a data segment.\n"); - FAULT(FAULT_GP,desc.selector & ~0x03) // #GP(selector) - } - else - { - switch(desc.flags & 0x000f) - { - case 0x01: // Available 286 TSS - case 0x09: // Available 386 TSS - logerror("CALL: Available TSS at %08x\n",m_pc); - if(DPL < CPL) - { - logerror("CALL: TSS: DPL is less than CPL.\n"); - FAULT(FAULT_TS,selector & ~0x03) // #TS(selector) - } - if(DPL < RPL) - { - logerror("CALL: TSS: DPL is less than RPL.\n"); - FAULT(FAULT_TS,selector & ~0x03) // #TS(selector) - } - if(desc.flags & 0x0002) - { - logerror("CALL: TSS: TSS is busy.\n"); - FAULT(FAULT_TS,selector & ~0x03) // #TS(selector) - } - if((desc.flags & 0x0080) == 0) - { - logerror("CALL: TSS: Segment %02x is not present.\n",selector); - FAULT(FAULT_NP,selector & ~0x03) // #NP(selector) - } - if(desc.flags & 0x08) - i386_task_switch(desc.selector,1); - else - i286_task_switch(desc.selector,1); - return; - case 0x04: // 286 call gate - case 0x0c: // 386 call gate - if((desc.flags & 0x000f) == 0x04) - operand32 = 0; - else - operand32 = 1; - memset(&gate, 0, sizeof(gate)); - gate.segment = selector; - i386_load_call_gate(&gate); - DPL = gate.dpl; - //logerror("CALL: Call gate at %08x (%i parameters)\n",m_pc,gate.dword_count); - if(DPL < CPL) - { - logerror("CALL: Call gate DPL %i is less than CPL %i.\n",DPL,CPL); - FAULT(FAULT_GP,desc.selector & ~0x03) // #GP(selector) - } - if(DPL < RPL) - { - logerror("CALL: Call gate DPL %i is less than RPL %i.\n",DPL,RPL); - FAULT(FAULT_GP,desc.selector & ~0x03) // #GP(selector) - } - if(gate.present == 0) - { - logerror("CALL: Call gate is not present.\n"); - FAULT(FAULT_NP,desc.selector & ~0x03) // #GP(selector) - } - desc.selector = gate.selector; - if((gate.selector & ~0x03) == 0) - { - logerror("CALL: Call gate: Segment is null.\n"); - FAULT(FAULT_GP,0) // #GP(0) - } - if(desc.selector & 0x04) - { - if((desc.selector & ~0x07) > m_ldtr.limit) - { - logerror("CALL: Call gate: Segment is past LDT limit\n"); - FAULT(FAULT_GP,desc.selector & ~0x03) // #GP(selector) - } - } - else - { - if((desc.selector & ~0x07) > m_gdtr.limit) - { - logerror("CALL: Call gate: Segment is past GDT limit\n"); - FAULT(FAULT_GP,desc.selector & ~0x03) // #GP(selector) - } - } - i386_load_protected_mode_segment(&desc,nullptr); - if((desc.flags & 0x0018) != 0x18) - { - logerror("CALL: Call gate: Segment is not a code segment.\n"); - FAULT(FAULT_GP,desc.selector & ~0x03) // #GP(selector) - } - DPL = ((desc.flags >> 5) & 0x03); - if(DPL > CPL) - { - logerror("CALL: Call gate: Segment DPL %i is greater than CPL %i.\n",DPL,CPL); - FAULT(FAULT_GP,desc.selector & ~0x03) // #GP(selector) - } - if((desc.flags & 0x0080) == 0) - { - logerror("CALL (%08x): Code segment is not present.\n",m_pc); - FAULT(FAULT_NP,desc.selector & ~0x03) // #NP(selector) - } - if(DPL < CPL && (desc.flags & 0x0004) == 0) - { - I386_SREG stack; - I386_SREG temp; - uint32_t oldSS,oldESP; - /* more privilege */ - /* Check new SS segment for privilege level from TSS */ - memset(&stack, 0, sizeof(stack)); - stack.selector = i386_get_stack_segment(DPL); - i386_load_protected_mode_segment(&stack,nullptr); - if((stack.selector & ~0x03) == 0) - { - logerror("CALL: Call gate: TSS selector is null\n"); - FAULT(FAULT_TS,0) // #TS(0) - } - if(stack.selector & 0x04) - { - if((stack.selector & ~0x07) > m_ldtr.limit) - { - logerror("CALL: Call gate: TSS selector is past LDT limit\n"); - FAULT(FAULT_TS,stack.selector) // #TS(SS selector) - } - } - else - { - if((stack.selector & ~0x07) > m_gdtr.limit) - { - logerror("CALL: Call gate: TSS selector is past GDT limit\n"); - FAULT(FAULT_TS,stack.selector) // #TS(SS selector) - } - } - if((stack.selector & 0x03) != DPL) - { - logerror("CALL: Call gate: Stack selector RPL does not equal code segment DPL %i\n",DPL); - FAULT(FAULT_TS,stack.selector) // #TS(SS selector) - } - if(((stack.flags >> 5) & 0x03) != DPL) - { - logerror("CALL: Call gate: Stack DPL does not equal code segment DPL %i\n",DPL); - FAULT(FAULT_TS,stack.selector) // #TS(SS selector) - } - if((stack.flags & 0x0018) != 0x10 && (stack.flags & 0x0002)) - { - logerror("CALL: Call gate: Stack segment is not a writable data segment\n"); - FAULT(FAULT_TS,stack.selector) // #TS(SS selector) - } - if((stack.flags & 0x0080) == 0) - { - logerror("CALL: Call gate: Stack segment is not present\n"); - FAULT(FAULT_SS,stack.selector) // #SS(SS selector) - } - uint32_t newESP = i386_get_stack_ptr(DPL); - if(!stack.d) - { - newESP &= 0xffff; - } - if(operand32 != 0) - { - if(newESP < ((gate.dword_count & 0x1f) + 16)) - { - logerror("CALL: Call gate: New stack has no room for 32-bit return address and parameters.\n"); - FAULT(FAULT_SS,0) // #SS(0) - } - if(gate.offset > desc.limit) - { - logerror("CALL: Call gate: EIP is past segment limit.\n"); - FAULT(FAULT_GP,0) // #GP(0) - } - } - else - { - if(newESP < ((gate.dword_count & 0x1f) + 8)) - { - logerror("CALL: Call gate: New stack has no room for 16-bit return address and parameters.\n"); - FAULT(FAULT_SS,0) // #SS(0) - } - if((gate.offset & 0xffff) > desc.limit) - { - logerror("CALL: Call gate: IP is past segment limit.\n"); - FAULT(FAULT_GP,0) // #GP(0) - } - } - selector = gate.selector; - offset = gate.offset; - - m_CPL = (stack.flags >> 5) & 0x03; - /* check for page fault at new stack */ - WRITE_TEST(stack.base+newESP-1); - /* switch to new stack */ - oldSS = m_sreg[SS].selector; - m_sreg[SS].selector = i386_get_stack_segment(m_CPL); - if(operand32 != 0) - { - oldESP = REG32(ESP); - } - else - { - oldESP = REG16(SP); - } - i386_load_segment_descriptor(SS ); - REG32(ESP) = newESP; - - if(operand32 != 0) - { - PUSH32SEG(oldSS); - PUSH32(oldESP); - } - else - { - PUSH16(oldSS); - PUSH16(oldESP & 0xffff); - } - - memset(&temp, 0, sizeof(temp)); - temp.selector = oldSS; - i386_load_protected_mode_segment(&temp,nullptr); - /* copy parameters from old stack to new stack */ - for(x=(gate.dword_count & 0x1f)-1;x>=0;x--) - { - uint32_t addr = oldESP + (operand32?(x*4):(x*2)); - addr = temp.base + (temp.d?addr:(addr&0xffff)); - if(operand32) - PUSH32(READ32(addr)); - else - PUSH16(READ16(addr)); - } - SetRPL = 1; - } - else - { - /* same privilege */ - if (operand32 != 0) // if 32-bit - { - uint32_t stkoff = (STACK_32BIT ? REG32(ESP) - 8 : (REG16(SP) - 8) & 0xffff); - if(i386_limit_check(SS, stkoff)) - { - logerror("CALL: Stack has no room for return address.\n"); - FAULT(FAULT_SS,0) // #SS(0) - } - selector = gate.selector; - offset = gate.offset; - } - else - { - uint32_t stkoff = (STACK_32BIT ? REG32(ESP) - 4 : (REG16(SP) - 4) & 0xffff); - if(i386_limit_check(SS, stkoff)) - { - logerror("CALL: Stack has no room for return address.\n"); - FAULT(FAULT_SS,0) // #SS(0) - } - selector = gate.selector; - offset = gate.offset & 0xffff; - } - if(offset > desc.limit) - { - logerror("CALL: EIP is past segment limit.\n"); - FAULT(FAULT_GP,0) // #GP(0) - } - SetRPL = 1; - } - break; - case 0x05: // task gate - logerror("CALL: Task gate at %08x\n",m_pc); - memset(&gate, 0, sizeof(gate)); - gate.segment = selector; - i386_load_call_gate(&gate); - DPL = gate.dpl; - if(DPL < CPL) - { - logerror("CALL: Task Gate: Gate DPL is less than CPL.\n"); - FAULT(FAULT_TS,selector & ~0x03) // #TS(selector) - } - if(DPL < RPL) - { - logerror("CALL: Task Gate: Gate DPL is less than RPL.\n"); - FAULT(FAULT_TS,selector & ~0x03) // #TS(selector) - } - if((gate.ar & 0x0080) == 0) - { - logerror("CALL: Task Gate: Gate is not present.\n"); - FAULT(FAULT_NP,selector & ~0x03) // #NP(selector) - } - /* Check the TSS that the task gate points to */ - desc.selector = gate.selector; - i386_load_protected_mode_segment(&desc,nullptr); - if(gate.selector & 0x04) - { - logerror("CALL: Task Gate: TSS is not global.\n"); - FAULT(FAULT_TS,gate.selector & ~0x03) // #TS(selector) - } - else - { - if((gate.selector & ~0x07) > m_gdtr.limit) - { - logerror("CALL: Task Gate: TSS is past GDT limit.\n"); - FAULT(FAULT_TS,gate.selector & ~0x03) // #TS(selector) - } - } - if(desc.flags & 0x0002) - { - logerror("CALL: Task Gate: TSS is busy.\n"); - FAULT(FAULT_TS,gate.selector & ~0x03) // #TS(selector) - } - if((desc.flags & 0x0080) == 0) - { - logerror("CALL: Task Gate: TSS is not present.\n"); - FAULT(FAULT_NP,gate.selector & ~0x03) // #TS(selector) - } - if(desc.flags & 0x08) - i386_task_switch(desc.selector,1); // with nesting - else - i286_task_switch(desc.selector,1); - return; - default: - logerror("CALL: Invalid special segment type (%i) to jump to.\n",desc.flags & 0x000f); - FAULT(FAULT_GP,selector & ~0x07) // #GP(selector) - } - } - } - - if(SetRPL != 0) - selector = (selector & ~0x03) | m_CPL; - - uint32_t tempSP = REG32(ESP); - try - { - // this is ugly but the alternative is worse - if(operand32 == 0) - { - /* 16-bit operand size */ - PUSH16(m_sreg[CS].selector ); - PUSH16(m_eip & 0x0000ffff ); - m_sreg[CS].selector = selector; - m_performed_intersegment_jump = 1; - m_eip = offset; - i386_load_segment_descriptor(CS); - } - else - { - /* 32-bit operand size */ - PUSH32SEG(m_sreg[CS].selector ); - PUSH32(m_eip ); - m_sreg[CS].selector = selector; - m_performed_intersegment_jump = 1; - m_eip = offset; - i386_load_segment_descriptor(CS ); - } - } - catch(uint64_t e) - { - REG32(ESP) = tempSP; - throw e; - } - - CHANGE_PC(m_eip); -} - -void i386_device::i386_protected_mode_retf(uint8_t count, uint8_t operand32) -{ - uint32_t newCS, newEIP; - I386_SREG desc; - uint8_t CPL, RPL, DPL; - - uint32_t ea = i386_translate(SS, (STACK_32BIT)?REG32(ESP):REG16(SP), 0); - - if(operand32 == 0) - { - newEIP = READ16(ea) & 0xffff; - newCS = READ16(ea+2) & 0xffff; - } - else - { - newEIP = READ32(ea); - newCS = READ32(ea+4) & 0xffff; - } - - memset(&desc, 0, sizeof(desc)); - desc.selector = newCS; - i386_load_protected_mode_segment(&desc,nullptr); - CPL = m_CPL; // current privilege level - DPL = (desc.flags >> 5) & 0x03; // descriptor privilege level - RPL = newCS & 0x03; - - if(RPL < CPL) - { - logerror("RETF (%08x): Return segment RPL is less than CPL.\n",m_pc); - FAULT(FAULT_GP,newCS & ~0x03) - } - - if(RPL == CPL) - { - /* same privilege level */ - if((newCS & ~0x03) == 0) - { - logerror("RETF: Return segment is null.\n"); - FAULT(FAULT_GP,0) - } - if(newCS & 0x04) - { - if((newCS & ~0x07) >= m_ldtr.limit) - { - logerror("RETF: Return segment is past LDT limit.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - } - else - { - if((newCS & ~0x07) >= m_gdtr.limit) - { - logerror("RETF: Return segment is past GDT limit.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - } - if((desc.flags & 0x0018) != 0x0018) - { - logerror("RETF: Return segment is not a code segment.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - if(desc.flags & 0x0004) - { - if(DPL > RPL) - { - logerror("RETF: Conforming code segment DPL is greater than CS RPL.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - } - else - { - if(DPL != RPL) - { - logerror("RETF: Non-conforming code segment DPL does not equal CS RPL.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - } - if((desc.flags & 0x0080) == 0) - { - logerror("RETF (%08x): Code segment is not present.\n",m_pc); - FAULT(FAULT_NP,newCS & ~0x03) - } - if(newEIP > desc.limit) - { - logerror("RETF: EIP is past code segment limit.\n"); - FAULT(FAULT_GP,0) - } - if(operand32 == 0) - { - uint32_t offset = (STACK_32BIT ? REG32(ESP) : REG16(SP)); - if(i386_limit_check(SS,offset+count+3) != 0) - { - logerror("RETF (%08x): SP is past stack segment limit.\n",m_pc); - FAULT(FAULT_SS,0) - } - } - else - { - uint32_t offset = (STACK_32BIT ? REG32(ESP) : REG16(SP)); - if(i386_limit_check(SS,offset+count+7) != 0) - { - logerror("RETF: ESP is past stack segment limit.\n"); - FAULT(FAULT_SS,0) - } - } - if(STACK_32BIT) - REG32(ESP) += (operand32 ? 8 : 4) + count; - else - REG16(SP) += (operand32 ? 8 : 4) + count; - } - else if(RPL > CPL) - { - uint32_t newSS, newESP; // when changing privilege - /* outer privilege level */ - if(operand32 == 0) - { - uint32_t offset = (STACK_32BIT ? REG32(ESP) : REG16(SP)); - if(i386_limit_check(SS,offset+count+7) != 0) - { - logerror("RETF (%08x): SP is past stack segment limit.\n",m_pc); - FAULT(FAULT_SS,0) - } - } - else - { - uint32_t offset = (STACK_32BIT ? REG32(ESP) : REG16(SP)); - if(i386_limit_check(SS,offset+count+15) != 0) - { - logerror("RETF: ESP is past stack segment limit.\n"); - FAULT(FAULT_SS,0) - } - } - /* Check CS selector and descriptor */ - if((newCS & ~0x03) == 0) - { - logerror("RETF: CS segment is null.\n"); - FAULT(FAULT_GP,0) - } - if(newCS & 0x04) - { - if((newCS & ~0x07) >= m_ldtr.limit) - { - logerror("RETF: CS segment selector is past LDT limit.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - } - else - { - if((newCS & ~0x07) >= m_gdtr.limit) - { - logerror("RETF: CS segment selector is past GDT limit.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - } - if((desc.flags & 0x0018) != 0x0018) - { - logerror("RETF: CS segment is not a code segment.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - if(desc.flags & 0x0004) - { - if(DPL > RPL) - { - logerror("RETF: Conforming CS segment DPL is greater than return selector RPL.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - } - else - { - if(DPL != RPL) - { - logerror("RETF: Non-conforming CS segment DPL is not equal to return selector RPL.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - } - if((desc.flags & 0x0080) == 0) - { - logerror("RETF: CS segment is not present.\n"); - FAULT(FAULT_NP,newCS & ~0x03) - } - if(newEIP > desc.limit) - { - logerror("RETF: EIP is past return CS segment limit.\n"); - FAULT(FAULT_GP,0) - } - - if(operand32 == 0) - { - ea += count+4; - newESP = READ16(ea) & 0xffff; - newSS = READ16(ea+2) & 0xffff; - } - else - { - ea += count+8; - newESP = READ32(ea); - newSS = READ32(ea+4) & 0xffff; - } - - /* Check SS selector and descriptor */ - desc.selector = newSS; - i386_load_protected_mode_segment(&desc,nullptr); - DPL = (desc.flags >> 5) & 0x03; // descriptor privilege level - if((newSS & ~0x07) == 0) - { - logerror("RETF: SS segment is null.\n"); - FAULT(FAULT_GP,0) - } - if(newSS & 0x04) - { - if((newSS & ~0x07) > m_ldtr.limit) - { - logerror("RETF (%08x): SS segment selector is past LDT limit.\n",m_pc); - FAULT(FAULT_GP,newSS & ~0x03) - } - } - else - { - if((newSS & ~0x07) > m_gdtr.limit) - { - logerror("RETF (%08x): SS segment selector is past GDT limit.\n",m_pc); - FAULT(FAULT_GP,newSS & ~0x03) - } - } - if((newSS & 0x03) != RPL) - { - logerror("RETF: SS segment RPL is not equal to CS segment RPL.\n"); - FAULT(FAULT_GP,newSS & ~0x03) - } - if((desc.flags & 0x0018) != 0x0010 || (desc.flags & 0x0002) == 0) - { - logerror("RETF: SS segment is not a writable data segment.\n"); - FAULT(FAULT_GP,newSS & ~0x03) - } - if(((desc.flags >> 5) & 0x03) != RPL) - { - logerror("RETF: SS DPL is not equal to CS segment RPL.\n"); - FAULT(FAULT_GP,newSS & ~0x03) - } - if((desc.flags & 0x0080) == 0) - { - logerror("RETF: SS segment is not present.\n"); - FAULT(FAULT_GP,newSS & ~0x03) - } - m_CPL = newCS & 0x03; - - /* Load new SS:(E)SP */ - if(operand32 == 0) - REG16(SP) = (newESP+count) & 0xffff; - else - REG32(ESP) = newESP+count; - m_sreg[SS].selector = newSS; - i386_load_segment_descriptor(SS ); - - /* Check that DS, ES, FS and GS are valid for the new privilege level */ - i386_check_sreg_validity(DS); - i386_check_sreg_validity(ES); - i386_check_sreg_validity(FS); - i386_check_sreg_validity(GS); - } - - /* Load new CS:(E)IP */ - if(operand32 == 0) - m_eip = newEIP & 0xffff; - else - m_eip = newEIP; - m_sreg[CS].selector = newCS; - i386_load_segment_descriptor(CS ); - CHANGE_PC(m_eip); -} - -void i386_device::i386_protected_mode_iret(int operand32) -{ - uint32_t newCS, newEIP; - uint32_t newSS, newESP; // when changing privilege - I386_SREG desc,stack; - uint8_t CPL, RPL, DPL; - uint32_t newflags; - uint8_t IOPL = m_IOP1 | (m_IOP2 << 1); - - CPL = m_CPL; - uint32_t ea = i386_translate(SS, (STACK_32BIT)?REG32(ESP):REG16(SP), 0); - if(operand32 == 0) - { - newEIP = READ16(ea) & 0xffff; - newCS = READ16(ea+2) & 0xffff; - newflags = READ16(ea+4) & 0xffff; - } - else - { - newEIP = READ32(ea); - newCS = READ32(ea+4) & 0xffff; - newflags = READ32(ea+8); - } - - if(V8086_MODE) - { - uint32_t oldflags = get_flags(); - if(IOPL != 3) - { - logerror("IRET (%08x): Is in Virtual 8086 mode and IOPL != 3.\n",m_pc); - FAULT(FAULT_GP,0) - } - if(operand32 == 0) - { - m_eip = newEIP & 0xffff; - m_sreg[CS].selector = newCS & 0xffff; - newflags &= ~(3<<12); - newflags |= (((oldflags>>12)&3)<<12); // IOPL cannot be changed in V86 mode - set_flags((newflags & 0xffff) | (oldflags & ~0xffff)); - REG16(SP) += 6; - } - else - { - m_eip = newEIP; - m_sreg[CS].selector = newCS & 0xffff; - newflags &= ~(3<<12); - newflags |= 0x20000 | (((oldflags>>12)&3)<<12); // IOPL and VM cannot be changed in V86 mode - set_flags(newflags); - REG32(ESP) += 12; - } - } - else if(NESTED_TASK) - { - uint32_t task = READ32(m_task.base); - /* Task Return */ - logerror("IRET (%08x): Nested task return.\n",m_pc); - /* Check back-link selector in TSS */ - if(task & 0x04) - { - logerror("IRET: Task return: Back-linked TSS is not in GDT.\n"); - FAULT(FAULT_TS,task & ~0x03) - } - if((task & ~0x07) >= m_gdtr.limit) - { - logerror("IRET: Task return: Back-linked TSS is not in GDT.\n"); - FAULT(FAULT_TS,task & ~0x03) - } - memset(&desc, 0, sizeof(desc)); - desc.selector = task; - i386_load_protected_mode_segment(&desc,nullptr); - if((desc.flags & 0x001f) != 0x000b) - { - logerror("IRET (%08x): Task return: Back-linked TSS is not a busy TSS.\n",m_pc); - FAULT(FAULT_TS,task & ~0x03) - } - if((desc.flags & 0x0080) == 0) - { - logerror("IRET: Task return: Back-linked TSS is not present.\n"); - FAULT(FAULT_NP,task & ~0x03) - } - if(desc.flags & 0x08) - i386_task_switch(desc.selector,0); - else - i286_task_switch(desc.selector,0); - return; - } - else - { - if(newflags & 0x00020000) // if returning to virtual 8086 mode - { - // 16-bit iret can't reach here - newESP = READ32(ea+12); - newSS = READ32(ea+16) & 0xffff; - /* Return to v86 mode */ - //logerror("IRET (%08x): Returning to Virtual 8086 mode.\n",m_pc); - if(CPL != 0) - { - uint32_t oldflags = get_flags(); - newflags = (newflags & ~0x00003000) | (oldflags & 0x00003000); - if(CPL > IOPL) - newflags = (newflags & ~0x200 ) | (oldflags & 0x200); - } - set_flags(newflags); - m_eip = POP32() & 0xffff; // high 16 bits are ignored - m_sreg[CS].selector = POP32() & 0xffff; - POP32(); // already set flags - newESP = POP32(); - newSS = POP32() & 0xffff; - m_sreg[ES].selector = POP32() & 0xffff; - m_sreg[DS].selector = POP32() & 0xffff; - m_sreg[FS].selector = POP32() & 0xffff; - m_sreg[GS].selector = POP32() & 0xffff; - REG32(ESP) = newESP; // all 32 bits are loaded - m_sreg[SS].selector = newSS; - i386_load_segment_descriptor(ES); - i386_load_segment_descriptor(DS); - i386_load_segment_descriptor(FS); - i386_load_segment_descriptor(GS); - i386_load_segment_descriptor(SS); - m_CPL = 3; // Virtual 8086 tasks are always run at CPL 3 - } - else - { - if(operand32 == 0) - { - uint32_t offset = (STACK_32BIT ? REG32(ESP) : REG16(SP)); - if(i386_limit_check(SS,offset+3) != 0) - { - logerror("IRET: Data on stack is past SS limit.\n"); - FAULT(FAULT_SS,0) - } - } - else - { - uint32_t offset = (STACK_32BIT ? REG32(ESP) : REG16(SP)); - if(i386_limit_check(SS,offset+7) != 0) - { - logerror("IRET: Data on stack is past SS limit.\n"); - FAULT(FAULT_SS,0) - } - } - RPL = newCS & 0x03; - if(RPL < CPL) - { - logerror("IRET (%08x): Return CS RPL is less than CPL.\n",m_pc); - FAULT(FAULT_GP,newCS & ~0x03) - } - if(RPL == CPL) - { - /* return to same privilege level */ - if(operand32 == 0) - { - uint32_t offset = (STACK_32BIT ? REG32(ESP) : REG16(SP)); - if(i386_limit_check(SS,offset+5) != 0) - { - logerror("IRET (%08x): Data on stack is past SS limit.\n",m_pc); - FAULT(FAULT_SS,0) - } - } - else - { - uint32_t offset = (STACK_32BIT ? REG32(ESP) : REG16(SP)); - if(i386_limit_check(SS,offset+11) != 0) - { - logerror("IRET (%08x): Data on stack is past SS limit.\n",m_pc); - FAULT(FAULT_SS,0) - } - } - if((newCS & ~0x03) == 0) - { - logerror("IRET: Return CS selector is null.\n"); - FAULT(FAULT_GP,0) - } - if(newCS & 0x04) - { - if((newCS & ~0x07) >= m_ldtr.limit) - { - logerror("IRET: Return CS selector (%04x) is past LDT limit.\n",newCS); - FAULT(FAULT_GP,newCS & ~0x03) - } - } - else - { - if((newCS & ~0x07) >= m_gdtr.limit) - { - logerror("IRET: Return CS selector is past GDT limit.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - } - memset(&desc, 0, sizeof(desc)); - desc.selector = newCS; - i386_load_protected_mode_segment(&desc,nullptr); - DPL = (desc.flags >> 5) & 0x03; // descriptor privilege level - RPL = newCS & 0x03; - if((desc.flags & 0x0018) != 0x0018) - { - logerror("IRET (%08x): Return CS segment is not a code segment.\n",m_pc); - FAULT(FAULT_GP,newCS & ~0x07) - } - if(desc.flags & 0x0004) - { - if(DPL > RPL) - { - logerror("IRET: Conforming return CS DPL is greater than CS RPL.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - } - else - { - if(DPL != RPL) - { - logerror("IRET: Non-conforming return CS DPL is not equal to CS RPL.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - } - if((desc.flags & 0x0080) == 0) - { - logerror("IRET: (%08x) Return CS segment is not present.\n", m_pc); - FAULT(FAULT_NP,newCS & ~0x03) - } - if(newEIP > desc.limit) - { - logerror("IRET: Return EIP is past return CS limit.\n"); - FAULT(FAULT_GP,0) - } - - if(CPL != 0) - { - uint32_t oldflags = get_flags(); - newflags = (newflags & ~0x00003000) | (oldflags & 0x00003000); - if(CPL > IOPL) - newflags = (newflags & ~0x200 ) | (oldflags & 0x200); - } - - if(operand32 == 0) - { - m_eip = newEIP; - m_sreg[CS].selector = newCS; - set_flags(newflags); - REG16(SP) += 6; - } - else - { - m_eip = newEIP; - m_sreg[CS].selector = newCS & 0xffff; - set_flags(newflags); - REG32(ESP) += 12; - } - } - else if(RPL > CPL) - { - /* return to outer privilege level */ - memset(&desc, 0, sizeof(desc)); - desc.selector = newCS; - i386_load_protected_mode_segment(&desc,nullptr); - DPL = (desc.flags >> 5) & 0x03; // descriptor privilege level - RPL = newCS & 0x03; - if(operand32 == 0) - { - uint32_t offset = (STACK_32BIT ? REG32(ESP) : REG16(SP)); - if(i386_limit_check(SS,offset+9) != 0) - { - logerror("IRET: SP is past SS limit.\n"); - FAULT(FAULT_SS,0) - } - } - else - { - uint32_t offset = (STACK_32BIT ? REG32(ESP) : REG16(SP)); - if(i386_limit_check(SS,offset+19) != 0) - { - logerror("IRET: ESP is past SS limit.\n"); - FAULT(FAULT_SS,0) - } - } - /* Check CS selector and descriptor */ - if((newCS & ~0x03) == 0) - { - logerror("IRET: Return CS selector is null.\n"); - FAULT(FAULT_GP,0) - } - if(newCS & 0x04) - { - if((newCS & ~0x07) >= m_ldtr.limit) - { - logerror("IRET: Return CS selector is past LDT limit.\n"); - FAULT(FAULT_GP,newCS & ~0x03); - } - } - else - { - if((newCS & ~0x07) >= m_gdtr.limit) - { - logerror("IRET: Return CS selector is past GDT limit.\n"); - FAULT(FAULT_GP,newCS & ~0x03); - } - } - if((desc.flags & 0x0018) != 0x0018) - { - logerror("IRET: Return CS segment is not a code segment.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - if(desc.flags & 0x0004) - { - if(DPL > RPL) - { - logerror("IRET: Conforming return CS DPL is greater than CS RPL.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - } - else - { - if(DPL != RPL) - { - logerror("IRET: Non-conforming return CS DPL does not equal CS RPL.\n"); - FAULT(FAULT_GP,newCS & ~0x03) - } - } - if((desc.flags & 0x0080) == 0) - { - logerror("IRET: Return CS segment is not present.\n"); - FAULT(FAULT_NP,newCS & ~0x03) - } - - /* Check SS selector and descriptor */ - if(operand32 == 0) - { - newESP = READ16(ea+6) & 0xffff; - newSS = READ16(ea+8) & 0xffff; - } - else - { - newESP = READ32(ea+12); - newSS = READ32(ea+16) & 0xffff; - } - memset(&stack, 0, sizeof(stack)); - stack.selector = newSS; - i386_load_protected_mode_segment(&stack,nullptr); - DPL = (stack.flags >> 5) & 0x03; - if((newSS & ~0x03) == 0) - { - logerror("IRET: Return SS selector is null.\n"); - FAULT(FAULT_GP,0) - } - if(newSS & 0x04) - { - if((newSS & ~0x07) >= m_ldtr.limit) - { - logerror("IRET: Return SS selector is past LDT limit.\n"); - FAULT(FAULT_GP,newSS & ~0x03); - } - } - else - { - if((newSS & ~0x07) >= m_gdtr.limit) - { - logerror("IRET: Return SS selector is past GDT limit.\n"); - FAULT(FAULT_GP,newSS & ~0x03); - } - } - if((newSS & 0x03) != RPL) - { - logerror("IRET: Return SS RPL is not equal to return CS RPL.\n"); - FAULT(FAULT_GP,newSS & ~0x03) - } - if((stack.flags & 0x0018) != 0x0010) - { - logerror("IRET: Return SS segment is not a data segment.\n"); - FAULT(FAULT_GP,newSS & ~0x03) - } - if((stack.flags & 0x0002) == 0) - { - logerror("IRET: Return SS segment is not writable.\n"); - FAULT(FAULT_GP,newSS & ~0x03) - } - if(DPL != RPL) - { - logerror("IRET: Return SS DPL does not equal SS RPL.\n"); - FAULT(FAULT_GP,newSS & ~0x03) - } - if((stack.flags & 0x0080) == 0) - { - logerror("IRET: Return SS segment is not present.\n"); - FAULT(FAULT_NP,newSS & ~0x03) - } - if(newEIP > desc.limit) - { - logerror("IRET: EIP is past return CS limit.\n"); - FAULT(FAULT_GP,0) - } - -// if(operand32 == 0) -// REG16(SP) += 10; -// else -// REG32(ESP) += 20; - - // IOPL can only change if CPL is zero - if(CPL != 0) - { - uint32_t oldflags = get_flags(); - newflags = (newflags & ~0x00003000) | (oldflags & 0x00003000); - if(CPL > IOPL) - newflags = (newflags & ~0x200 ) | (oldflags & 0x200); - } - - if(operand32 == 0) - { - m_eip = newEIP & 0xffff; - m_sreg[CS].selector = newCS; - set_flags(newflags); - REG16(SP) = newESP & 0xffff; - m_sreg[SS].selector = newSS; - } - else - { - m_eip = newEIP; - m_sreg[CS].selector = newCS & 0xffff; - set_flags(newflags); - REG32(ESP) = newESP; - m_sreg[SS].selector = newSS & 0xffff; - } - m_CPL = newCS & 0x03; - i386_load_segment_descriptor(SS); - - /* Check that DS, ES, FS and GS are valid for the new privilege level */ - i386_check_sreg_validity(DS); - i386_check_sreg_validity(ES); - i386_check_sreg_validity(FS); - i386_check_sreg_validity(GS); - } - } - } - - i386_load_segment_descriptor(CS); - CHANGE_PC(m_eip); -} - void i386_device::build_cycle_table() { int i, j; @@ -3040,6 +1578,7 @@ void i386_device::report_invalid_modrm(const char* opcode, uint8_t modrm) #include "pentops.hxx" #include "x87ops.hxx" #include "cpuidmsrs.hxx" +#include "i386segs.hxx" void i386_device::i386_decode_opcode() @@ -4761,371 +3300,6 @@ void pentium3_device::device_reset() } /*****************************************************************************/ -/* AMD Athlon XP - Model: Athlon XP 2400+ - Part number: AXDA2400DKV3C - Stepping code: AIUCP - Date code: 0240MPMW -*/ - -void athlonxp_device::device_start() -{ - i386_common_init(); - register_state_i386_x87_xmm(); - m_data = &space(AS_DATA); - m_opcodes = &space(AS_OPCODES); - mmacache32 = m_data->cache<2, 0, ENDIANNESS_LITTLE>(); - m_opcodes->install_read_handler(0, 0xffffffff, read32_delegate(FUNC(athlonxp_device::debug_read_memory), this)); - - build_x87_opcode_table(); - build_opcode_table(OP_I386 | OP_FPU | OP_I486 | OP_PENTIUM | OP_PPRO | OP_MMX | OP_SSE); - m_cycle_table_rm = cycle_table_rm[CPU_CYCLES_PENTIUM].get(); // TODO: generate own cycle tables - m_cycle_table_pm = cycle_table_pm[CPU_CYCLES_PENTIUM].get(); // TODO: generate own cycle tables - - // put savestate calls here - save_item(NAME(m_processor_name_string)); - save_item(NAME(m_msr_top_mem)); - save_item(NAME(m_msr_sys_cfg)); - save_item(NAME(m_msr_smm_base)); - save_item(NAME(m_msr_smm_mask)); - save_item(NAME(m_msr_mtrrfix)); - save_item(NAME(m_memory_ranges_1m)); -} - -void athlonxp_device::device_reset() -{ - zero_state(); - - m_sreg[CS].selector = 0xf000; - m_sreg[CS].base = 0xffff0000; - m_sreg[CS].limit = 0xffff; - m_sreg[CS].flags = 0x0093; - - m_sreg[DS].base = m_sreg[ES].base = m_sreg[FS].base = m_sreg[GS].base = m_sreg[SS].base = 0x00000000; - m_sreg[DS].limit = m_sreg[ES].limit = m_sreg[FS].limit = m_sreg[GS].limit = m_sreg[SS].limit = 0xffff; - m_sreg[DS].flags = m_sreg[ES].flags = m_sreg[FS].flags = m_sreg[GS].flags = m_sreg[SS].flags = 0x0093; - - m_idtr.base = 0; - m_idtr.limit = 0x3ff; - - m_a20_mask = ~0; - - m_cr[0] = 0x60000010; - m_eflags = 0x00200000; - m_eflags_mask = 0x00277fd7; /* TODO: is this correct? */ - m_eip = 0xfff0; - m_mxcsr = 0x1f80; - m_smm = false; - m_smi_latched = false; - m_smbase = 0x30000; - m_nmi_masked = false; - m_nmi_latched = false; - - x87_reset(); - - // [11:8] Family - // [ 7:4] Model - // [ 3:0] Stepping ID - // Family 6, Model 8, Stepping 1 - REG32(EAX) = 0; - REG32(EDX) = (6 << 8) | (8 << 4) | (1); - - m_cpuid_id0 = ('h' << 24) | ('t' << 16) | ('u' << 8) | 'A'; // Auth - m_cpuid_id1 = ('i' << 24) | ('t' << 16) | ('n' << 8) | 'e'; // enti - m_cpuid_id2 = ('D' << 24) | ('M' << 16) | ('A' << 8) | 'c'; // cAMD - memset(m_processor_name_string, 0, 48); - strcpy((char *)m_processor_name_string, "AMD Athlon(tm) Processor"); - for (int n = 0; n < 11; n++) - m_msr_mtrrfix[n] = 0; - for (int n = 0; n < (1024 / 4); n++) - m_memory_ranges_1m[n] = 0; - m_msr_top_mem = 1024 * 1024; - m_msr_sys_cfg = 0; - m_msr_smm_base = m_smbase; - m_msr_smm_mask = 0; - - m_cpuid_max_input_value_eax = 0x01; - m_cpu_version = REG32(EDX); - - // see FEATURE_FLAGS enum for bit names - m_feature_flags = 0x0383fbff; - - CHANGE_PC(m_eip); -} - -device_memory_interface::space_config_vector athlonxp_device::memory_space_config() const -{ - return space_config_vector{ - std::make_pair(AS_PROGRAM, &m_program_config), - std::make_pair(AS_IO, &m_io_config), - std::make_pair(AS_DATA, &m_data_config), - std::make_pair(AS_OPCODES, &m_opcodes_config) - }; -} - -void athlonxp_device::enter_smm() -{ - u64 data; - - if (m_msr_smm_mask & 1) - data = 0x1818181818181818; // when smm is active - else - data = m_msr_mtrrfix[2]; - parse_mtrrfix(data, 0xa0000, 16); - i386_device::enter_smm(); -} - -void athlonxp_device::leave_smm() -{ - u64 data; - - i386_device::leave_smm(); - if (m_msr_smm_mask & 1) - data = 0; // when smm is not active - else - data = m_msr_mtrrfix[2]; - parse_mtrrfix(data, 0xa0000, 16); -} - -void athlonxp_device::parse_mtrrfix(u64 mtrr, offs_t base, int kblock) -{ - int nb = kblock / 4; - int range = (int)(base >> 12); // base must never be higher than 1 megabyte - - for (int n = 0; n < 8; n++) - { - uint8_t type = mtrr & 0xff; - - for (int b = 0; b < nb; b++) - { - m_memory_ranges_1m[range] = type; - range++; - } - mtrr = mtrr >> 8; - } -} - -int athlonxp_device::check_cacheable(offs_t address) -{ - offs_t block; - int disabled; - - disabled = 0; - if (m_cr[0] & (1 << 30)) - disabled = 128; - if (address >= 0x100000) - return disabled; - block = address >> 12; - return m_memory_ranges_1m[block] | disabled; -} - -template <int wr> -int athlonxp_device::address_mode(offs_t address) -{ - if (address >= m_msr_top_mem) - return 1; - if (address >= 1 * 1024 * 1024) - return 0; - if ((m_memory_ranges_1m[address >> 12] & (1 << (3 + wr))) != 0) - return 0; - return 1; -} - -READ32_MEMBER(athlonxp_device::debug_read_memory) -{ - offs_t address = offset << 2; - int mode = check_cacheable(address); - bool nocache = false; - address_space *m = m_program; - u8 *data; - - if ((mode & 7) == 0) - nocache = true; - if (mode & 1) - nocache = true; - if (nocache == false) - { - int offset = (address & 63); - data = cache.search<CacheRead>(address); - if (data) - return *(u32 *)(data + offset); - } - if (address_mode<1>(address)) - m = m_data; - return m->read_dword(address); -} - -template <class dt, offs_t xorle> -dt athlonxp_device::opcode_read_cache(offs_t address) -{ - int mode = check_cacheable(address); - bool nocache = false; - memory_access_cache<2, 0, ENDIANNESS_LITTLE> *m = macache32; - u8 *data; - - if ((mode & 7) == 0) - nocache = true; - if (mode & 1) - nocache = true; - if (nocache == false) - { - int offset = (address & 63) ^ xorle; - data = cache.search<CacheRead>(address); - if (data) - return *(dt *)(data + offset); - if (!(mode & 128)) - { - bool dirty = cache.allocate<CacheRead>(address, &data); - address = cache.base(address); - if (dirty) - { - offs_t old_address = cache.old(); - - for (int w = 0; w < 64; w += 4) - m->write_dword(old_address + w, *(u32 *)(data + w)); - } - for (int r = 0; r < 64; r += 4) - *(u32 *)(data + r) = m->read_dword(address + r); - return *(dt *)(data + offset); - } - } - if (address_mode<1>(address)) - m = mmacache32; - if (sizeof(dt) == 1) - return m->read_byte(address); - else if (sizeof(dt) == 2) - return m->read_word(address); - else - return m->read_dword(address); -} - -template <class dt, offs_t xorle> -dt athlonxp_device::program_read_cache(offs_t address) -{ - int mode = check_cacheable(address); - bool nocache = false; - address_space *m = m_program; - u8 *data; - - if ((mode & 7) == 0) - nocache = true; - if (mode & 1) - nocache = true; - if (nocache == false) - { - int offset = (address & 63) ^ xorle; - data = cache.search<CacheRead>(address); - if (data) - return *(dt *)(data + offset); - if (!(mode & 128)) - { - bool dirty = cache.allocate<CacheRead>(address, &data); - address = cache.base(address); - if (dirty) - { - offs_t old_address = cache.old(); - - for (int w = 0; w < 64; w += 4) - m->write_dword(old_address + w, *(u32 *)(data + w)); - } - for (int r = 0; r < 64; r += 4) - *(u32 *)(data + r) = m->read_dword(address + r); - return *(dt *)(data + offset); - } - } - if (address_mode<1>(address)) - m = m_data; - if (sizeof(dt) == 1) - return m->read_byte(address); - else if (sizeof(dt) == 2) - return m->read_word(address); - else - return m->read_dword(address); -} - -template <class dt, offs_t xorle> -void athlonxp_device::program_write_cache(offs_t address, dt data) -{ - int mode = check_cacheable(address); - bool nocache = false; - address_space *m = m_program; - u8 *dataw; - - if ((mode & 7) == 0) - nocache = true; - if (mode & 1) - nocache = true; - if (nocache == false) - { - int offset = (address & 63) ^ xorle; - dataw = cache.search<CacheWrite>(address); - if (dataw) - { - *(dt *)(dataw + offset) = data; - return; - } - if (!(mode & 128)) - { - bool dirty = cache.allocate<CacheWrite>(address, &dataw); - address = cache.base(address); - if (dirty) - { - offs_t old_address = cache.old(); - - for (int w = 0; w < 64; w += 4) - m->write_dword(old_address + w, *(u32 *)(dataw + w)); - } - for (int r = 0; r < 64; r += 4) - *(u32 *)(dataw + r) = m->read_dword(address + r); - *(dt *)(dataw + offset) = data; - return; - } - } - if (address_mode<0>(address)) - m = m_data; - if (sizeof(dt) == 1) - m->write_byte(address, data); - else if (sizeof(dt) == 2) - m->write_word(address, data); - else - m->write_dword(address, data); -} - -void athlonxp_device::cache_writeback() -{ - // dirty cachelines are written back to memory - address_space *m = m_program; - u32 base; - u8 *data; - - data = cache.first_dirty(base, false); - while (data != nullptr) - { - for (int w = 0; w < 64; w += 4) - m->write_dword(base + w, *(u32 *)(data + w)); - data = cache.next_dirty(base, false); - } -} - -void athlonxp_device::cache_invalidate() -{ - // dirty cachelines are not written back to memory - cache.reset(); -} - -void athlonxp_device::cache_clean() -{ - // dirty cachelines are marked as clean but not written back to memory - u32 base; - u8 *data; - - data = cache.first_dirty(base, true); - while (data != nullptr) - data = cache.next_dirty(base, true); -} - - -/*****************************************************************************/ /* Intel Pentium 4 */ void pentium4_device::device_start() |