///////////////////////////////////////////////////////////////////////// // $Id: io.cc,v 1.23 2004-06-19 15:20:07 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 "iodev/iodev.h" #define LOG_THIS BX_CPU_THIS_PTR #if BX_SUPPORT_X86_64==0 // Make life easier for merging cpu64 and cpu32 code. #define RDI EDI #define RSI ESI #define RAX EAX #endif #if BX_SUPPORT_X86_64 #define IsLongMode() (BX_CPU_THIS_PTR cpu_mode == BX_MODE_LONG_64) #else #define IsLongMode() (0) #endif void BX_CPU_C::INSB_YbDX(bxInstruction_c *i) { Bit8u value8=0; if (BX_CPU_THIS_PTR cr0.pe && (BX_CPU_THIS_PTR get_VM () || (CPL>BX_CPU_THIS_PTR get_IOPL ()))) { if ( !BX_CPU_THIS_PTR allow_io(DX, 1) ) { 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++; } } void BX_CPU_C::INSW_YvDX(bxInstruction_c *i) // input word/doubleword from port to string { bx_address edi; unsigned int incr; #if BX_SUPPORT_X86_64 if (i->as64L()) // This was coded as if (i->as_64) ??? edi = RDI; else #endif if (i->as32L()) edi = EDI; else edi = DI; if (i->os32L()) { Bit32u value32=0; if (BX_CPU_THIS_PTR cr0.pe && (BX_CPU_THIS_PTR get_VM () || (CPL>BX_CPU_THIS_PTR get_IOPL ()))) { if ( !BX_CPU_THIS_PTR allow_io(DX, 4) ) { exception(BX_GP_EXCEPTION, 0, 0); } } // 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, &value32); value32 = BX_INP(DX, 4); /* no seg override allowed */ write_virtual_dword(BX_SEG_REG_ES, edi, &value32); incr = 4; } else { Bit16u value16=0; if (BX_CPU_THIS_PTR cr0.pe && (BX_CPU_THIS_PTR get_VM () || (CPL>BX_CPU_THIS_PTR get_IOPL ()))) { if ( !BX_CPU_THIS_PTR allow_io(DX, 2) ) exception(BX_GP_EXCEPTION, 0, 0); } #if BX_SupportRepeatSpeedups #if (BX_DEBUGGER == 0) #if (defined(__i386__) && __i386__) /* 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 BX_SUPPORT_X86_64 if (i->as64L()) wordCount = RCX; // Truncated to 32bits. (we're only doing 1 page) else #endif if (i->as32L()) wordCount = ECX; else wordCount = CX; if (wordCount) { bx_address laddrDst; Bit32u paddrDst, wordsFitDst; Bit8u *hostAddrDst; bx_segment_reg_t *dstSegPtr; int pointerDelta; 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, edi, 2); laddrDst = dstSegPtr->cache.u.segment.base + edi; if (BX_CPU_THIS_PTR cr0.pg) paddrDst = dtranslate_linear(laddrDst, CPL==3, BX_WRITE); else paddrDst = laddrDst; // If we want to write directly into the physical memory array, // we need the A20 address. paddrDst = A20ADDR(paddrDst); hostAddrDst = BX_CPU_THIS_PTR mem->getHostMemAddr(BX_CPU_THIS, paddrDst, BX_WRITE); // Check that native host access was not vetoed for that page, and // that the address is word aligned. if ( hostAddrDst && ! (paddrDst & 1) ) { // 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 ( (paddrDst & 0xfff) > 0xffe ) goto noAcceleration; wordsFitDst = (2 + (paddrDst & 0xfff)) >> 1; pointerDelta = -2; } else { // Counting upward. wordsFitDst = (0x1000 - (paddrDst & 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) { unsigned j; Bit32u dstSegLimit; dstSegLimit = dstSegPtr->cache.u.segment.limit_scaled; // 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) ) { goto noAcceleration; } if ( !IsLongMode() ) { // 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 ( edi < minOffset ) goto noAcceleration; } else { // Counting upward. Bit32u dstMaxOffset = (dstSegLimit - (wordCount<<1)) + 1; if ( edi > dstMaxOffset ) goto noAcceleration; } } for (j=0; jas64L()) RCX -= (wordCount-1); else #endif if (i->as32L()) ECX -= (wordCount-1); else CX -= (wordCount-1); incr = wordCount << 1; // count * 2. goto doIncr; } } } } noAcceleration: #endif // __i386__ #endif // (BX_DEBUGGER == 0) #endif // #if BX_SupportRepeatSpeedups // 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); incr = 2; } #if BX_SupportRepeatSpeedups #if (BX_DEBUGGER == 0) #if (defined(__i386__) && __i386__) doIncr: #endif #endif #endif #if BX_SUPPORT_X86_64 if (i->as64L()) { if (BX_CPU_THIS_PTR get_DF ()) RDI = RDI - incr; else RDI = RDI + incr; } else #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 = DI - incr; else DI = DI + incr; } } void BX_CPU_C::OUTSB_DXXb(bxInstruction_c *i) { unsigned seg; Bit8u value8; bx_address esi; if (BX_CPU_THIS_PTR cr0.pe && (BX_CPU_THIS_PTR get_VM () || (CPL>BX_CPU_THIS_PTR get_IOPL ()))) { if ( !BX_CPU_THIS_PTR allow_io(DX, 1) ) { exception(BX_GP_EXCEPTION, 0, 0); } } if (!BX_NULL_SEG_REG(i->seg())) { seg = i->seg(); } else { seg = BX_SEG_REG_DS; } #if BX_SUPPORT_X86_64 if (i->as64L()) esi = RSI; else #endif if (i->as32L()) esi = ESI; else esi = SI; read_virtual_byte(seg, esi, &value8); BX_OUTP(DX, value8, 1); #if BX_SUPPORT_X86_64 if (i->as64L()) { if (BX_CPU_THIS_PTR get_DF ()) RSI--; else RSI++; } else #endif if (i->as32L()) { if (BX_CPU_THIS_PTR get_DF ()) RSI--; else RSI++; } else { if (BX_CPU_THIS_PTR get_DF ()) SI--; else SI++; } } void BX_CPU_C::OUTSW_DXXv(bxInstruction_c *i) // output word/doubleword string to port { unsigned seg; bx_address esi; unsigned int incr; if (!BX_NULL_SEG_REG(i->seg())) { seg = i->seg(); } else { seg = BX_SEG_REG_DS; } #if BX_SUPPORT_X86_64 if (i->as64L()) esi = RSI; else #endif if (i->as32L()) esi = ESI; else esi = SI; if (i->os32L()) { Bit32u value32; if (BX_CPU_THIS_PTR cr0.pe && (BX_CPU_THIS_PTR get_VM () || (CPL>BX_CPU_THIS_PTR get_IOPL ()))) { if ( !BX_CPU_THIS_PTR allow_io(DX, 4) ) { exception(BX_GP_EXCEPTION, 0, 0); } } read_virtual_dword(seg, esi, &value32); BX_OUTP(DX, value32, 4); incr = 4; } else { Bit16u value16; if (BX_CPU_THIS_PTR cr0.pe && (BX_CPU_THIS_PTR get_VM () || (CPL>BX_CPU_THIS_PTR get_IOPL ()))) { if ( !BX_CPU_THIS_PTR allow_io(DX, 2) ) { exception(BX_GP_EXCEPTION, 0, 0); } } #if BX_SupportRepeatSpeedups #if (BX_DEBUGGER == 0) #if (defined(__i386__) && __i386__) /* 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 BX_SUPPORT_X86_64 if (i->as64L()) wordCount = RCX; // Truncated to 32bits. (we're only doing 1 page) else #endif if (i->as32L()) wordCount = ECX; else wordCount = CX; if (wordCount) { bx_address laddrSrc; Bit32u paddrSrc, wordsFitSrc; Bit8u *hostAddrSrc; bx_segment_reg_t *srcSegPtr; unsigned pointerDelta; srcSegPtr = &BX_CPU_THIS_PTR sregs[seg]; // 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, esi, 2); laddrSrc = srcSegPtr->cache.u.segment.base + esi; if (BX_CPU_THIS_PTR cr0.pg) paddrSrc = dtranslate_linear(laddrSrc, CPL==3, BX_READ); else paddrSrc = laddrSrc; // If we want to write directly into the physical memory array, // we need the A20 address. paddrSrc = A20ADDR(paddrSrc); hostAddrSrc = BX_CPU_THIS_PTR mem->getHostMemAddr(BX_CPU_THIS, paddrSrc, BX_READ); // Check that native host access was not vetoed for that page, and // that the address is word aligned. if ( hostAddrSrc && ! (paddrSrc & 1) ) { // 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 ( (paddrSrc & 0xfff) > 0xffe ) goto noAcceleration; wordsFitSrc = (2 + (paddrSrc & 0xfff)) >> 1; pointerDelta = (unsigned) -2; } else { // Counting upward. wordsFitSrc = (0x1000 - (paddrSrc & 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) { unsigned j; Bit32u srcSegLimit; srcSegLimit = srcSegPtr->cache.u.segment.limit_scaled; // 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) ) { goto noAcceleration; } if ( !IsLongMode() ) { // 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 ( esi < minOffset ) goto noAcceleration; } else { // Counting upward. Bit32u srcMaxOffset = (srcSegLimit - (wordCount<<1)) + 1; if ( esi > srcMaxOffset ) goto noAcceleration; } } for (j=0; jas64L()) RCX -= (wordCount-1); else #endif if (i->as32L()) ECX -= (wordCount-1); else CX -= (wordCount-1); incr = wordCount << 1; // count * 2. goto doIncr; } } } } noAcceleration: #endif // __i386__ #endif // (BX_DEBUGGER == 0) #endif // #if BX_SupportRepeatSpeedups read_virtual_word(seg, esi, &value16); BX_OUTP(DX, value16, 2); incr = 2; } #if BX_SupportRepeatSpeedups #if (BX_DEBUGGER == 0) #if (defined(__i386__) && __i386__) doIncr: #endif #endif #endif #if BX_SUPPORT_X86_64 if (i->as64L()) { if (BX_CPU_THIS_PTR get_DF ()) RSI = RSI - incr; else RSI = RSI + incr; } else #endif 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; } } void BX_CPU_C::IN_ALIb(bxInstruction_c *i) { AL = BX_CPU_THIS_PTR inp8(i->Ib()); } void BX_CPU_C::IN_eAXIb(bxInstruction_c *i) { #if BX_CPU_LEVEL > 2 if (i->os32L()) { Bit32u eax = BX_CPU_THIS_PTR inp32(i->Ib()); RAX = eax; } else #endif /* BX_CPU_LEVEL > 2 */ { AX = BX_CPU_THIS_PTR inp16(i->Ib()); } } void BX_CPU_C::OUT_IbAL(bxInstruction_c *i) { BX_CPU_THIS_PTR outp8(i->Ib(), AL); } void BX_CPU_C::OUT_IbeAX(bxInstruction_c *i) { #if BX_CPU_LEVEL > 2 if (i->os32L()) { BX_CPU_THIS_PTR outp32(i->Ib(), EAX); } else #endif /* BX_CPU_LEVEL > 2 */ { BX_CPU_THIS_PTR outp16(i->Ib(), AX); } } void BX_CPU_C::IN_ALDX(bxInstruction_c *i) { AL = BX_CPU_THIS_PTR inp8(DX); } void BX_CPU_C::IN_eAXDX(bxInstruction_c *i) { #if BX_CPU_LEVEL > 2 if (i->os32L()) { Bit32u eax = BX_CPU_THIS_PTR inp32(DX); RAX = eax; } else #endif /* BX_CPU_LEVEL > 2 */ { AX = BX_CPU_THIS_PTR inp16(DX); } } void BX_CPU_C::OUT_DXAL(bxInstruction_c *i) { BX_CPU_THIS_PTR outp8(DX, AL); } void BX_CPU_C::OUT_DXeAX(bxInstruction_c *i) { #if BX_CPU_LEVEL > 2 if (i->os32L()) { BX_CPU_THIS_PTR outp32(DX, EAX); } else #endif /* BX_CPU_LEVEL > 2 */ { BX_CPU_THIS_PTR outp16(DX, AX); } }