Bochs/bochs/cpu/io.cc
Bryce Denney 5fc31bcfda - this revision changes the way eflags are accessed throughout the cpu and
cpu64 directories.  Instead of using the macros introduced in cpu.h rev 1.37
  such as GetEFlagsDFLogical and SetEFlagsDF and ClearEFlagsDF, I made inline
  methods on the BX_CPU_C object that access the eflags fields.  The problem
  with the macros is that they cannot be used outside the BX_CPU_C object.  The
  macros have now been removed, and all references to eflags now use these new
  accessors.
- I debated whether to put the accessors as members of the BX_CPU_C object
  or members of the bx_flags_reg_t struct.  I chose to make them members
  of BX_CPU_C for two reasons: 1. the lazy flags are implemented as
  members of BX_CPU_C, and 2. the eflags are referenced in many many places
  and it is more compact without having to put eflags in front of each.  (The
  real problem with compactness is having to write BX_CPU_THIS_PTR in front of
  everything, but that's another story.)
- Kevin pointed out a major bug in my set accessor code.  What a difference a
  little tilde can make!  That is fixed now.
- modified: load32bitOShack.cc debug/dbg_main.cc
  and in both cpu and cpu64 directories:
    cpu.cc cpu.h ctrl_xfer_pro.cc debugstuff.cc exception.cc flag_ctrl.cc
    flag_ctrl_pro.cc init.cc io.cc io_pro.cc proc_ctrl.cc soft_int.cc
    string.cc vm8086.cc
2002-09-12 18:10:46 +00:00

692 lines
18 KiB
C++

/////////////////////////////////////////////////////////////////////////
// $Id: io.cc,v 1.11 2002-09-12 18:10:41 bdenney 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"
#define LOG_THIS BX_CPU_THIS_PTR
void
BX_CPU_C::INSB_YbDX(BxInstruction_t *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 (i->as_32) {
// 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 ()) {
EDI = EDI - 1;
}
else {
EDI = 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 = DI - 1;
}
else {
DI = DI + 1;
}
}
}
void
BX_CPU_C::INSW_YvDX(BxInstruction_t *i)
// input word/doubleword from port to string
{
Bit32u edi;
unsigned int incr;
if (i->as_32)
edi = EDI;
else
edi = DI;
if (i->os_32) {
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->rep_used && !BX_CPU_THIS_PTR async_event) {
Bit32u wordCount;
if (i->as_32)
wordCount = ECX;
else
wordCount = CX;
if (wordCount) {
Bit32u laddrDst, paddrDst, wordsFitDst;
Bit8u *hostAddrDst;
bx_segment_reg_t *dstSegPtr;
int pointerDelta;
dstSegPtr = &BX_CPU_THIS_PTR sregs[BX_SREG_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(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->as_32) {
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;
}
// 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; j<wordCount; ) {
Bit16u temp16;
bx_devices.bulkIOQuantumsTransferred = 0;
if ( BX_CPU_THIS_PTR get_DF ()==0 ) { // Only do accel for DF=0
bx_devices.bulkIOHostAddr = (Bit32u) hostAddrDst;
bx_devices.bulkIOQuantumsRequested = (wordCount - j);
}
else
bx_devices.bulkIOQuantumsRequested = 0;
temp16 = BX_INP(DX, 2);
if ( bx_devices.bulkIOQuantumsTransferred ) {
hostAddrDst = (Bit8u*) bx_devices.bulkIOHostAddr;
j += bx_devices.bulkIOQuantumsTransferred;
}
else {
* (Bit16u *) hostAddrDst = temp16;
hostAddrDst += pointerDelta;
j++;
}
// 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;
wordCount = j;
// Decrement eCX. Note, the main loop will decrement 1 also, so
// decrement by one less than expected, like the case above.
BX_TICKN(j-1); // Main cpu loop also decrements one more.
if (i->as_32)
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 (i->as_32) {
if (BX_CPU_THIS_PTR get_DF ())
EDI = EDI - incr;
else
EDI = EDI + incr;
}
else {
if (BX_CPU_THIS_PTR get_DF ())
DI = DI - incr;
else
DI = DI + incr;
}
}
void
BX_CPU_C::OUTSB_DXXb(BxInstruction_t *i)
{
unsigned seg;
Bit8u value8;
Bit32u 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 (i->as_32)
esi = ESI;
else
esi = SI;
read_virtual_byte(seg, esi, &value8);
BX_OUTP(DX, value8, 1);
if (i->as_32) {
if (BX_CPU_THIS_PTR get_DF ())
ESI -= 1;
else
ESI += 1;
}
else {
if (BX_CPU_THIS_PTR get_DF ())
SI -= 1;
else
SI += 1;
}
}
void
BX_CPU_C::OUTSW_DXXv(BxInstruction_t *i)
// output word/doubleword string to port
{
unsigned seg;
Bit32u esi;
unsigned int incr;
if (!BX_NULL_SEG_REG(i->seg)) {
seg = i->seg;
}
else {
seg = BX_SEG_REG_DS;
}
if (i->as_32)
esi = ESI;
else
esi = SI;
if (i->os_32) {
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->rep_used && !BX_CPU_THIS_PTR async_event) {
Bit32u wordCount;
if (i->as_32)
wordCount = ECX;
else
wordCount = CX;
if (wordCount) {
Bit32u laddrSrc, 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(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->as_32) {
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;
}
// 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; j<wordCount; ) {
Bit16u temp16;
bx_devices.bulkIOQuantumsTransferred = 0;
if ( BX_CPU_THIS_PTR get_DF ()==0 ) { // Only do accel for DF=0
bx_devices.bulkIOHostAddr = (Bit32u) hostAddrSrc;
bx_devices.bulkIOQuantumsRequested = (wordCount - j);
}
else
bx_devices.bulkIOQuantumsRequested = 0;
temp16 = * (Bit16u *) hostAddrSrc;
BX_OUTP(DX, temp16, 2);
if ( bx_devices.bulkIOQuantumsTransferred ) {
hostAddrSrc = (Bit8u*) bx_devices.bulkIOHostAddr;
j += bx_devices.bulkIOQuantumsTransferred;
}
else {
hostAddrSrc += pointerDelta;
j++;
}
// 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;
wordCount = j;
// Decrement eCX. Note, the main loop will decrement 1 also, so
// decrement by one less than expected, like the case above.
BX_TICKN(j-1); // Main cpu loop also decrements one more.
if (i->as_32)
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 (i->as_32) {
if (BX_CPU_THIS_PTR get_DF ())
ESI = ESI - incr;
else
ESI = ESI + incr;
}
else {
if (BX_CPU_THIS_PTR get_DF ())
SI = SI - incr;
else
SI = SI + incr;
}
}
void
BX_CPU_C::IN_ALIb(BxInstruction_t *i)
{
Bit8u al, imm8;
imm8 = i->Ib;
al = BX_CPU_THIS_PTR inp8(imm8);
AL = al;
}
void
BX_CPU_C::IN_eAXIb(BxInstruction_t *i)
{
Bit8u imm8;
imm8 = i->Ib;
#if BX_CPU_LEVEL > 2
if (i->os_32) {
Bit32u eax;
eax = BX_CPU_THIS_PTR inp32(imm8);
EAX = eax;
}
else
#endif /* BX_CPU_LEVEL > 2 */
{
Bit16u ax;
ax = BX_CPU_THIS_PTR inp16(imm8);
AX = ax;
}
}
void
BX_CPU_C::OUT_IbAL(BxInstruction_t *i)
{
Bit8u al, imm8;
imm8 = i->Ib;
al = AL;
BX_CPU_THIS_PTR outp8(imm8, al);
}
void
BX_CPU_C::OUT_IbeAX(BxInstruction_t *i)
{
Bit8u imm8;
imm8 = i->Ib;
#if BX_CPU_LEVEL > 2
if (i->os_32) {
BX_CPU_THIS_PTR outp32(imm8, EAX);
}
else
#endif /* BX_CPU_LEVEL > 2 */
{
BX_CPU_THIS_PTR outp16(imm8, AX);
}
}
void
BX_CPU_C::IN_ALDX(BxInstruction_t *i)
{
Bit8u al;
al = BX_CPU_THIS_PTR inp8(DX);
AL = al;
}
void
BX_CPU_C::IN_eAXDX(BxInstruction_t *i)
{
#if BX_CPU_LEVEL > 2
if (i->os_32) {
Bit32u eax;
eax = BX_CPU_THIS_PTR inp32(DX);
EAX = eax;
}
else
#endif /* BX_CPU_LEVEL > 2 */
{
Bit16u ax;
ax = BX_CPU_THIS_PTR inp16(DX);
AX = ax;
}
}
void
BX_CPU_C::OUT_DXAL(BxInstruction_t *i)
{
Bit16u dx;
Bit8u al;
dx = DX;
al = AL;
BX_CPU_THIS_PTR outp8(dx, al);
}
void
BX_CPU_C::OUT_DXeAX(BxInstruction_t *i)
{
Bit16u dx;
dx = DX;
#if BX_CPU_LEVEL > 2
if (i->os_32) {
BX_CPU_THIS_PTR outp32(dx, EAX);
}
else
#endif /* BX_CPU_LEVEL > 2 */
{
BX_CPU_THIS_PTR outp16(dx, AX);
}
}