///////////////////////////////////////////////////////////////////////// // $Id: io.cc,v 1.48 2007-12-23 18:09:34 sshwarts Exp $ ///////////////////////////////////////////////////////////////////////// // // Copyright (C) 2001 MandrakeSoft S.A. // // MandrakeSoft S.A. // 43, rue d'Aboukir // 75002 Paris - France // http://www.linux-mandrake.com/ // http://www.mandrakesoft.com/ // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #define NEED_CPU_REG_SHORTCUTS 1 #include "bochs.h" #include "cpu.h" #define LOG_THIS BX_CPU_THIS_PTR #include "iodev/iodev.h" #if BX_SUPPORT_X86_64==0 // Make life easier for merging cpu64 and cpu32 code. #define RDI EDI #define RSI ESI #define RAX EAX #define RCX ECX #endif // // Repeat Speedups methods // #if BX_SupportRepeatSpeedups Bit32u BX_CPU_C::FastRepINSW(bxInstruction_c *i, bx_address dstOff, Bit16u port, Bit32u wordCount) { Bit32u wordsFitDst; signed int pointerDelta; Bit8u *hostAddrDst; bx_segment_reg_t *dstSegPtr = &BX_CPU_THIS_PTR sregs[BX_SEG_REG_ES]; // Do segment checks for the 1st word. We do not want to // trip an exception beyond this, because the address would // be incorrect. After we know how many bytes we will directly // transfer, we can do the full segment limit check ourselves // without generating an exception. write_virtual_checks(dstSegPtr, dstOff, 2); bx_address laddrDst = BX_CPU_THIS_PTR get_segment_base(BX_SEG_REG_ES) + dstOff; // check that the address is word aligned if (laddrDst & 1) return 0; #if BX_SupportGuest2HostTLB hostAddrDst = v2h_write_byte(laddrDst, CPL); #else bx_phy_address paddrDst; if (BX_CPU_THIS_PTR cr0.get_PG()) paddrDst = dtranslate_linear(laddrDst, CPL, BX_WRITE); else paddrDst = laddrDst; // If we want to write directly into the physical memory array, // we need the A20 address. hostAddrDst = BX_CPU_THIS_PTR mem->getHostMemAddr(BX_CPU_THIS, A20ADDR(paddrDst), BX_WRITE, DATA_ACCESS); #endif // Check that native host access was not vetoed for that page if (!hostAddrDst) return 0; // See how many words can fit in the rest of this page. if (BX_CPU_THIS_PTR get_DF()) { // Counting downward // Note: 1st word must not cross page boundary. if ((laddrDst & 0xfff) > 0xffe) return 0; wordsFitDst = (2 + (laddrDst & 0xfff)) >> 1; pointerDelta = -2; } else { // Counting upward wordsFitDst = (0x1000 - (laddrDst & 0xfff)) >> 1; pointerDelta = 2; } // Restrict word count to the number that will fit in this page. if (wordCount > wordsFitDst) wordCount = wordsFitDst; // If after all the restrictions, there is anything left to do... if (wordCount) { Bit32u dstSegLimit = dstSegPtr->cache.u.segment.limit_scaled; unsigned count; // For 16-bit addressing mode, clamp the segment limits to 16bits // so we don't have to worry about computations using si/di // rolling over 16-bit boundaries. if (!i->as32L()) { if (dstSegLimit > 0xffff) dstSegLimit = 0xffff; } // Before we copy memory, we need to make sure that the segments // allow the accesses up to the given source and dest offset. If // the cache.valid bits have SegAccessWOK and ROK, we know that // the cache is valid for those operations, and that the segments // are non-expand down (thus we can make a simple limit check). if (!(dstSegPtr->cache.valid & SegAccessWOK)) return 0; if (BX_CPU_THIS_PTR cpu_mode != BX_MODE_LONG_64) { // Now make sure transfer will fit within the constraints of the // segment boundaries, 0..limit for non expand-down. We know // wordCount >= 1 here. if (BX_CPU_THIS_PTR get_DF()) { // Counting downward Bit32u minOffset = (wordCount-1) << 1; if (dstOff < minOffset) return 0; } else { // Counting upward Bit32u dstMaxOffset = (dstSegLimit - (wordCount<<1)) + 1; if (dstOff > dstMaxOffset) return 0; } } for (count=0; count> 8) | (temp16 << 8)); #endif hostAddrDst += pointerDelta; count++; } // Terminate early if there was an event. if (BX_CPU_THIS_PTR async_event) break; } // Reset for next non-bulk IO bx_devices.bulkIOQuantumsRequested = 0; return count; } return 0; } Bit32u BX_CPU_C::FastRepOUTSW(bxInstruction_c *i, unsigned srcSeg, bx_address srcOff, Bit16u port, Bit32u wordCount) { Bit32u wordsFitSrc; signed int pointerDelta; Bit8u *hostAddrSrc; bx_segment_reg_t *srcSegPtr = &BX_CPU_THIS_PTR sregs[srcSeg]; // Do segment checks for the 1st word. We do not want to // trip an exception beyond this, because the address would // be incorrect. After we know how many bytes we will directly // transfer, we can do the full segment limit check ourselves // without generating an exception. read_virtual_checks(srcSegPtr, srcOff, 2); bx_address laddrSrc = BX_CPU_THIS_PTR get_segment_base(srcSeg) + srcOff; // check that the address is word aligned if (laddrSrc & 1) return 0; #if BX_SupportGuest2HostTLB hostAddrSrc = v2h_read_byte(laddrSrc, CPL); #else bx_phy_address paddrSrc; if (BX_CPU_THIS_PTR cr0.get_PG()) paddrSrc = dtranslate_linear(laddrSrc, CPL, BX_READ); else paddrSrc = laddrSrc; // If we want to write directly into the physical memory array, // we need the A20 address. hostAddrSrc = BX_CPU_THIS_PTR mem->getHostMemAddr(BX_CPU_THIS, A20ADDR(paddrSrc), BX_READ, DATA_ACCESS); #endif // Check that native host access was not vetoed for that page if (!hostAddrSrc) return 0; // See how many words can fit in the rest of this page. if (BX_CPU_THIS_PTR get_DF()) { // Counting downward // Note: 1st word must not cross page boundary. if ((laddrSrc & 0xfff) > 0xffe) return 0; wordsFitSrc = (2 + (laddrSrc & 0xfff)) >> 1; pointerDelta = (unsigned) -2; } else { // Counting upward wordsFitSrc = (0x1000 - (laddrSrc & 0xfff)) >> 1; pointerDelta = 2; } // Restrict word count to the number that will fit in this page. if (wordCount > wordsFitSrc) wordCount = wordsFitSrc; // If after all the restrictions, there is anything left to do... if (wordCount) { Bit32u srcSegLimit = srcSegPtr->cache.u.segment.limit_scaled; unsigned count; // For 16-bit addressing mode, clamp the segment limits to 16bits // so we don't have to worry about computations using si/di // rolling over 16-bit boundaries. if (!i->as32L()) { if (srcSegLimit > 0xffff) srcSegLimit = 0xffff; } // Before we copy memory, we need to make sure that the segments // allow the accesses up to the given source and dest offset. If // the cache.valid bits have SegAccessWOK and ROK, we know that // the cache is valid for those operations, and that the segments // are non-expand down (thus we can make a simple limit check). if ( !(srcSegPtr->cache.valid & SegAccessROK) ) return 0; if (BX_CPU_THIS_PTR cpu_mode != BX_MODE_LONG_64) { // Now make sure transfer will fit within the constraints of the // segment boundaries, 0..limit for non expand-down. We know // wordCount >= 1 here. if (BX_CPU_THIS_PTR get_DF()) { // Counting downward Bit32u minOffset = (wordCount-1) << 1; if (srcOff < minOffset) return 0; } else { // Counting upward Bit32u srcMaxOffset = (srcSegLimit - (wordCount<<1)) + 1; if (srcOff > srcMaxOffset) return 0; } } for (count=0; count> 8) | (temp16 << 8)), 2); #endif if (bx_devices.bulkIOQuantumsTransferred) { hostAddrSrc = bx_devices.bulkIOHostAddr; count += bx_devices.bulkIOQuantumsTransferred; } else { hostAddrSrc += pointerDelta; count++; } // Terminate early if there was an event. if (BX_CPU_THIS_PTR async_event) break; } // Reset for next non-bulk IO bx_devices.bulkIOQuantumsRequested = 0; return count; } return 0; } #endif // // REP INS methods // void BX_CPU_C::REP_INSB_YbDX(bxInstruction_c *i) { BX_CPU_THIS_PTR repeat(i, &BX_CPU_C::INSB_YbDX); } void BX_CPU_C::REP_INSW_YwDX(bxInstruction_c *i) { BX_CPU_THIS_PTR repeat(i, &BX_CPU_C::INSW_YwDX); } void BX_CPU_C::REP_INSD_YdDX(bxInstruction_c *i) { BX_CPU_THIS_PTR repeat(i, &BX_CPU_C::INSD_YdDX); } // // INSB/INSW/INSD methods // void BX_CPU_C::INSB_YbDX(bxInstruction_c *i) { Bit8u value8=0; if (! BX_CPU_THIS_PTR allow_io(DX, 1)) { BX_DEBUG(("INSB_YbDX: I/O access not allowed !")); exception(BX_GP_EXCEPTION, 0, 0); } #if BX_SUPPORT_X86_64 if (i->as64L()) { // Write a zero to memory, to trigger any segment or page // faults before reading from IO port. write_virtual_byte(BX_SEG_REG_ES, RDI, value8); value8 = BX_INP(DX, 1); /* no seg override possible */ write_virtual_byte(BX_SEG_REG_ES, RDI, value8); if (BX_CPU_THIS_PTR get_DF()) RDI--; else RDI++; } else #endif if (i->as32L()) { // Write a zero to memory, to trigger any segment or page // faults before reading from IO port. write_virtual_byte(BX_SEG_REG_ES, EDI, value8); value8 = BX_INP(DX, 1); /* no seg override possible */ write_virtual_byte(BX_SEG_REG_ES, EDI, value8); if (BX_CPU_THIS_PTR get_DF()) { RDI = EDI - 1; } else { RDI = EDI + 1; } } else { // Write a zero to memory, to trigger any segment or page // faults before reading from IO port. write_virtual_byte(BX_SEG_REG_ES, DI, value8); value8 = BX_INP(DX, 1); /* no seg override possible */ write_virtual_byte(BX_SEG_REG_ES, DI, value8); if (BX_CPU_THIS_PTR get_DF()) DI--; else DI++; } } // input word from port to string void BX_CPU_C::INSW_YwDX(bxInstruction_c *i) { Bit16u value16=0; #if BX_SUPPORT_X86_64 if (i->as64L()) { Bit64u rdi = RDI; // Write a zero to memory, to trigger any segment or page // faults before reading from IO port. write_virtual_word(BX_SEG_REG_ES, rdi, value16); value16 = BX_INP(DX, 2); /* no seg override allowed */ write_virtual_word(BX_SEG_REG_ES, rdi, value16); if (BX_CPU_THIS_PTR get_DF()) rdi -= 2; else rdi += 2; RDI = rdi; } else #endif { Bit32u edi; Bit32u incr = 2; if (i->as32L()) edi = EDI; else edi = DI; #if (BX_SupportRepeatSpeedups) && (BX_DEBUGGER == 0) /* If conditions are right, we can transfer IO to physical memory * in a batch, rather than one instruction at a time. */ if (i->repUsedL() && !BX_CPU_THIS_PTR async_event) { Bit32u wordCount; if (i->as32L()) wordCount = ECX; else wordCount = CX; BX_ASSERT(wordCount > 0); wordCount = FastRepINSW(i, edi, DX, wordCount); if (wordCount) { // Decrement the ticks count by the number of iterations, minus // one, since the main cpu loop will decrement one. Also, // the count is predecremented before examined, so defintely // don't roll it under zero. BX_TICKN(wordCount-1); if (i->as32L()) RCX = ECX - (wordCount-1); else CX -= (wordCount-1); incr = wordCount << 1; // count * 2. goto doIncr; } } #endif // Write a zero to memory, to trigger any segment or page // faults before reading from IO port. write_virtual_word(BX_SEG_REG_ES, edi, value16); value16 = BX_INP(DX, 2); /* no seg override allowed */ write_virtual_word(BX_SEG_REG_ES, edi, value16); #if (BX_SupportRepeatSpeedups) && (BX_DEBUGGER == 0) doIncr: #endif if (i->as32L()) { if (BX_CPU_THIS_PTR get_DF()) RDI = EDI - incr; else RDI = EDI + incr; } else { if (BX_CPU_THIS_PTR get_DF()) DI -= incr; else DI += incr; } } } // input doubleword from port to string void BX_CPU_C::INSD_YdDX(bxInstruction_c *i) { if (! BX_CPU_THIS_PTR allow_io(DX, 4)) { BX_DEBUG(("INSD_YdDX: I/O access not allowed !")); exception(BX_GP_EXCEPTION, 0, 0); } #if BX_SUPPORT_X86_64 if (i->as64L()) { Bit64u rdi = RDI; // Write a zero to memory, to trigger any segment or page // faults before reading from IO port. write_virtual_dword(BX_SEG_REG_ES, rdi, 0); Bit32u value32 = BX_INP(DX, 4); /* no seg override allowed */ write_virtual_dword(BX_SEG_REG_ES, rdi, value32); if (BX_CPU_THIS_PTR get_DF()) rdi -= 4; else rdi += 4; RDI = rdi; } else #endif if (i->as32L()) { Bit32u edi = EDI; // Write a zero to memory, to trigger any segment or page // faults before reading from IO port. write_virtual_dword(BX_SEG_REG_ES, edi, 0); Bit32u value32 = BX_INP(DX, 4); /* no seg override allowed */ write_virtual_dword(BX_SEG_REG_ES, edi, value32); if (BX_CPU_THIS_PTR get_DF()) edi -= 4; else edi += 4; RDI = edi; } else { Bit16u di = DI; // Write a zero to memory, to trigger any segment or page // faults before reading from IO port. write_virtual_dword(BX_SEG_REG_ES, di, 0); Bit32u value32 = BX_INP(DX, 4); /* no seg override allowed */ write_virtual_dword(BX_SEG_REG_ES, di, value32); if (BX_CPU_THIS_PTR get_DF()) di -= 4; else di += 4; DI = di; } } // // REP OUTS methods // void BX_CPU_C::REP_OUTSB_DXXb(bxInstruction_c *i) { BX_CPU_THIS_PTR repeat(i, &BX_CPU_C::OUTSB_DXXb); } void BX_CPU_C::REP_OUTSW_DXXw(bxInstruction_c *i) { BX_CPU_THIS_PTR repeat(i, &BX_CPU_C::OUTSW_DXXw); } void BX_CPU_C::REP_OUTSD_DXXd(bxInstruction_c *i) { BX_CPU_THIS_PTR repeat(i, &BX_CPU_C::OUTSD_DXXd); } // // OUTSB/OUTSW/OUTSD methods // void BX_CPU_C::OUTSB_DXXb(bxInstruction_c *i) { if (! BX_CPU_THIS_PTR allow_io(DX, 1)) { BX_DEBUG(("OUTSB_DXXb: I/O access not allowed !")); exception(BX_GP_EXCEPTION, 0, 0); } Bit8u value8; #if BX_SUPPORT_X86_64 if (i->as64L()) { Bit64u rsi = RSI; value8 = read_virtual_byte(i->seg(), rsi); BX_OUTP(DX, value8, 1); if (BX_CPU_THIS_PTR get_DF()) rsi--; else rsi++; RSI = rsi; } else #endif if (i->as32L()) { Bit32u esi = ESI; value8 = read_virtual_byte(i->seg(), esi); BX_OUTP(DX, value8, 1); if (BX_CPU_THIS_PTR get_DF()) esi--; else esi++; RSI = esi; } else { Bit16u si = SI; value8 = read_virtual_byte(i->seg(), si); BX_OUTP(DX, value8, 1); if (BX_CPU_THIS_PTR get_DF()) si--; else si++; SI = si; } } // output word string to port void BX_CPU_C::OUTSW_DXXw(bxInstruction_c *i) { if (! BX_CPU_THIS_PTR allow_io(DX, 2)) { BX_DEBUG(("OUTSW_DXXw: I/O access not allowed !")); exception(BX_GP_EXCEPTION, 0, 0); } Bit16u value16; #if BX_SUPPORT_X86_64 if (i->as64L()) { Bit64u rsi = RSI; value16 = read_virtual_word(i->seg(), rsi); BX_OUTP(DX, value16, 2); if (BX_CPU_THIS_PTR get_DF()) rsi -= 2; else rsi += 2; RSI = rsi; } else #endif { Bit32u esi; Bit32u incr = 2; if (i->as32L()) esi = ESI; else esi = SI; #if (BX_SupportRepeatSpeedups) && (BX_DEBUGGER == 0) /* If conditions are right, we can transfer IO to physical memory * in a batch, rather than one instruction at a time. */ if (i->repUsedL() && !BX_CPU_THIS_PTR async_event) { Bit32u wordCount; if (i->as32L()) wordCount = ECX; else wordCount = CX; wordCount = FastRepOUTSW(i, i->seg(), esi, DX, wordCount); if (wordCount) { // Decrement eCX. Note, the main loop will decrement 1 also, so // decrement by one less than expected, like the case above. BX_TICKN(wordCount-1); // Main cpu loop also decrements one more. if (i->as32L()) RCX = ECX - (wordCount-1); else CX -= (wordCount-1); incr = wordCount << 1; // count * 2. } else { value16 = read_virtual_word(i->seg(), esi); BX_OUTP(DX, value16, 2); } } else #endif { value16 = read_virtual_word(i->seg(), esi); BX_OUTP(DX, value16, 2); } if (i->as32L()) { if (BX_CPU_THIS_PTR get_DF()) RSI = ESI - incr; else RSI = ESI + incr; } else { if (BX_CPU_THIS_PTR get_DF()) SI = SI - incr; else SI = SI + incr; } } } // output doubleword string to port void BX_CPU_C::OUTSD_DXXd(bxInstruction_c *i) { if (! BX_CPU_THIS_PTR allow_io(DX, 4)) { BX_DEBUG(("OUTSD_DXXd: I/O access not allowed !")); exception(BX_GP_EXCEPTION, 0, 0); } Bit32u value32; #if BX_SUPPORT_X86_64 if (i->as64L()) { Bit64u rsi = RSI; value32 = read_virtual_dword(i->seg(), rsi); BX_OUTP(DX, value32, 4); if (BX_CPU_THIS_PTR get_DF()) rsi -= 4; else rsi += 4; RSI = rsi; } else #endif if (i->as32L()) { Bit32u esi = ESI; value32 = read_virtual_dword(i->seg(), esi); BX_OUTP(DX, value32, 4); if (BX_CPU_THIS_PTR get_DF()) esi -= 4; else esi += 4; RSI = esi; } else { Bit16u si = SI; value32 = read_virtual_dword(i->seg(), si); BX_OUTP(DX, value32, 4); if (BX_CPU_THIS_PTR get_DF()) si -= 4; else si += 4; SI = si; } } // // non repeatable IN/OUT methods // void BX_CPU_C::IN_ALIb(bxInstruction_c *i) { AL = BX_CPU_THIS_PTR inp8(i->Ib()); } void BX_CPU_C::IN_AXIb(bxInstruction_c *i) { AX = BX_CPU_THIS_PTR inp16(i->Ib()); } void BX_CPU_C::IN_EAXIb(bxInstruction_c *i) { RAX = BX_CPU_THIS_PTR inp32(i->Ib()); } void BX_CPU_C::OUT_IbAL(bxInstruction_c *i) { BX_CPU_THIS_PTR outp8(i->Ib(), AL); } void BX_CPU_C::OUT_IbAX(bxInstruction_c *i) { BX_CPU_THIS_PTR outp16(i->Ib(), AX); } void BX_CPU_C::OUT_IbEAX(bxInstruction_c *i) { BX_CPU_THIS_PTR outp32(i->Ib(), EAX); } void BX_CPU_C::IN_ALDX(bxInstruction_c *i) { AL = BX_CPU_THIS_PTR inp8(DX); } void BX_CPU_C::IN_AXDX(bxInstruction_c *i) { AX = BX_CPU_THIS_PTR inp16(DX); } void BX_CPU_C::IN_EAXDX(bxInstruction_c *i) { RAX = BX_CPU_THIS_PTR inp32(DX); } void BX_CPU_C::OUT_DXAL(bxInstruction_c *i) { BX_CPU_THIS_PTR outp8(DX, AL); } void BX_CPU_C::OUT_DXAX(bxInstruction_c *i) { BX_CPU_THIS_PTR outp16(DX, AX); } void BX_CPU_C::OUT_DXEAX(bxInstruction_c *i) { BX_CPU_THIS_PTR outp32(DX, EAX); }