diff options
Diffstat (limited to 'tools/ioemu/iodev')
88 files changed, 41188 insertions, 0 deletions
diff --git a/tools/ioemu/iodev/Makefile b/tools/ioemu/iodev/Makefile new file mode 100644 index 0000000000..3a43661918 --- /dev/null +++ b/tools/ioemu/iodev/Makefile @@ -0,0 +1,15 @@ +TOPDIR= .. +CXXFLAGS=-I. -I../include -I.. +OBJS=$(patsubst %.cc,%.o,$(wildcard *.cc)) +BXLIBS = ../gui/libgui.a ../memory/libmemory.a +LDLIBS= $(BXLIBS) -L/usr/X11R6/lib -lX11 -lXpm -lstdc++ -L ../../../tools/libxc -L ../../../tools/libxutil -lxc -lxutil + +all: device-model + +device-model: $(OBJS) $(BXLIBS) + $(LINK.o) $(OBJS) $(LOADLIBES) $(LDLIBS) -o $@ + +include $(TOPDIR)/mk/helix.mk + +install:: all + install device-model $(DESTDIR)/usr/sbin diff --git a/tools/ioemu/iodev/aspi-win32.h b/tools/ioemu/iodev/aspi-win32.h new file mode 100644 index 0000000000..afa62d1e19 --- /dev/null +++ b/tools/ioemu/iodev/aspi-win32.h @@ -0,0 +1,210 @@ +// +// iodev/aspi-win32.h +// $Id: aspi-win32.h,v 1.2 2001/06/25 12:52:37 bdenney Exp $ +// +// This file was copied from cdrecord 1.9 under libscg/scg/aspi-win32.h. +// The only modification is related to use of the PACKED keyword. +// + +#ifndef __ASPI_WIN32_H_ +#define __ASPI_WIN32_H_ + +#include <windows.h> + +#ifndef PACKED +// It seems that VC++ has no PACKED keyword but Cygwin does. We can just +// define PACKED to be empty if it's not already defined by the system +// headers. +#define PACKED /* empty */ +#endif + +/*************************************************************************** + ** SCSI MISCELLANEOUS EQUATES + ***************************************************************************/ +#define SENSE_LEN 14 /* Default sense buffer length */ +#define SRB_DIR_SCSI 0x00 /* Direction determined by SCSI */ +#define SRB_POSTING 0x01 /* Enable ASPI posting */ +#define SRB_ENABLE_RESIDUAL_COUNT 0x04 /* Enable residual byte count */ + /* reporting */ +#define SRB_DIR_IN 0x08 /* Transfer from SCSI target to */ + /* host */ +#define SRB_DIR_OUT 0x10 /* Transfer from host to SCSI */ + /* target */ +#define SRB_EVENT_NOTIFY 0x40 /* Enable ASPI event notification */ +#define RESIDUAL_COUNT_SUPPORTED 0x02 /* Extended buffer flag */ +#define MAX_SRB_TIMEOUT 1080001u /* 30 hour maximum timeout in sec */ +#define DEFAULT_SRB_TIMEOUT 1080001u /* use max.timeout by default */ + +/*************************************************************************** + ** ASPI command definitions + ***************************************************************************/ +#define SC_HA_INQUIRY 0x00 /* Host adapter inquiry */ +#define SC_GET_DEV_TYPE 0x01 /* Get device type */ +#define SC_EXEC_SCSI_CMD 0x02 /* Execute SCSI command */ +#define SC_ABORT_SRB 0x03 /* Abort an SRB */ +#define SC_RESET_DEV 0x04 /* SCSI bus device reset */ +#define SC_SET_HA_PARMS 0x05 /* Set HA parameters */ +#define SC_GET_DISK_INFO 0x06 /* Get Disk */ +#define SC_RESCAN_SCSI_BUS 0x07 /* Rebuild SCSI device map */ +#define SC_GETSET_TIMEOUTS 0x08 /* Get/Set target timeouts */ + + +/*************************************************************************** + ** SRB Status + ***************************************************************************/ +#define SS_PENDING 0x00 /* SRB being processed */ +#define SS_COMP 0x01 /* SRB completed without error */ +#define SS_ABORTED 0x02 /* SRB aborted */ +#define SS_ABORT_FAIL 0x03 /* Unable to abort SRB */ +#define SS_ERR 0x04 /* SRB completed with error */ +#define SS_INVALID_CMD 0x80 /* Invalid ASPI command */ +#define SS_INVALID_HA 0x81 /* Invalid host adapter number */ +#define SS_NO_DEVICE 0x82 /* SCSI device not installed */ +#define SS_INVALID_SRB 0xE0 /* Invalid parameter set in SRB */ +#define SS_OLD_MANAGER 0xE1 /* ASPI manager doesn't support */ + /* windows */ +#define SS_BUFFER_ALIGN 0xE1 /* Buffer not aligned (replaces */ + /* SS_OLD_MANAGER in Win32) */ +#define SS_ILLEGAL_MODE 0xE2 /* Unsupported Windows mode */ +#define SS_NO_ASPI 0xE3 /* No ASPI managers */ +#define SS_FAILED_INIT 0xE4 /* ASPI for windows failed init */ +#define SS_ASPI_IS_BUSY 0xE5 /* No resources available to */ + /* execute command */ +#define SS_BUFFER_TO_BIG 0xE6 /* Buffer size too big to handle */ +#define SS_BUFFER_TOO_BIG 0xE6 /* Correct spelling of 'too' */ +#define SS_MISMATCHED_COMPONENTS 0xE7 /* The DLLs/EXEs of ASPI don't */ + /* version check */ +#define SS_NO_ADAPTERS 0xE8 /* No host adapters to manager */ +#define SS_INSUFFICIENT_RESOURCES 0xE9 /* Couldn't allocate resources */ + /* needed to init */ +#define SS_ASPI_IS_SHUTDOWN 0xEA /* Call came to ASPI after */ + /* PROCESS_DETACH */ +#define SS_BAD_INSTALL 0xEB /* The DLL or other components */ + /* are installed wrong */ + +/*************************************************************************** + ** Host Adapter Status + ***************************************************************************/ +#define HASTAT_OK 0x00 /* No error detected by HA */ +#define HASTAT_SEL_TO 0x11 /* Selection Timeout */ +#define HASTAT_DO_DU 0x12 /* Data overrun/data underrun */ +#define HASTAT_BUS_FREE 0x13 /* Unexpected bus free */ +#define HASTAT_PHASE_ERR 0x14 /* Target bus phase sequence */ +#define HASTAT_TIMEOUT 0x09 /* Timed out while SRB was */ + /* waiting to be processed */ +#define HASTAT_COMMAND_TIMEOUT 0x0B /* Adapter timed out while */ + /* processing SRB */ +#define HASTAT_MESSAGE_REJECT 0x0D /* While processing the SRB, the */ + /* adapter received a MESSAGE */ +#define HASTAT_BUS_RESET 0x0E /* A bus reset was detected */ +#define HASTAT_PARITY_ERROR 0x0F /* A parity error was detected */ +#define HASTAT_REQUEST_SENSE_FAILED 0x10 /* The adapter failed in issuing */ + +/*************************************************************************** + ** SRB - HOST ADAPTER INQUIRIY - SC_HA_INQUIRY (0) + ***************************************************************************/ +typedef struct { + BYTE SRB_Cmd; /* 00/000 ASPI command code == SC_HA_INQUIRY */ + BYTE SRB_Status; /* 01/001 ASPI command status byte */ + BYTE SRB_HaId; /* 02/002 ASPI host adapter number */ + BYTE SRB_Flags; /* 03/003 ASPI request flags */ + DWORD SRB_Hdr_Rsvd; /* 04/004 Reserved, must = 0 */ + BYTE HA_Count; /* 08/008 Number of host adapters present */ + BYTE HA_SCSI_ID; /* 09/009 SCSI ID of host adapter */ + BYTE HA_ManagerId[16]; /* 0a/010 String describing the manager */ + BYTE HA_Identifier[16]; /* 1a/026 String describing the host adapter */ + BYTE HA_Unique[16]; /* 2a/042 Host Adapter Unique parameters */ + WORD HA_Rsvd1; /* 3a/058 Reserved, must = 0 */ +} PACKED SRB_HAInquiry, *PSRB_HAInquiry, FAR *LPSRB_HAInquiry; + + +/*************************************************************************** + ** SRB - GET DEVICE TYPE - SC_GET_DEV_TYPE (1) + ***************************************************************************/ +typedef struct +{ + BYTE SRB_Cmd; /* 00/000 ASPI cmd code == SC_GET_DEV_TYPE */ + BYTE SRB_Status; /* 01/001 ASPI command status byte */ + BYTE SRB_HaId; /* 02/002 ASPI host adapter number */ + BYTE SRB_Flags; /* 03/003 Reserved, must = 0 */ + DWORD SRB_Hdr_Rsvd; /* 04/004 Reserved, must = 0 */ + BYTE SRB_Target; /* 08/008 Target's SCSI ID */ + BYTE SRB_Lun; /* 09/009 Target's LUN number */ + BYTE SRB_DeviceType; /* 0a/010 Target's peripheral device type */ + BYTE SRB_Rsvd1; /* 0b/011 Reserved, must = 0 */ +} PACKED SRB_GDEVBlock, *PSRB_GDEVBlock, FAR *LPSRB_GDEVBlock; + + +/*************************************************************************** + ** SRB - EXECUTE SCSI COMMAND - SC_EXEC_SCSI_CMD (2) + ***************************************************************************/ +typedef struct +{ + BYTE SRB_Cmd; /* 00/000 ASPI cmd code == SC_EXEC_SCSI_CMD */ + BYTE SRB_Status; /* 01/001 ASPI command status byte */ + BYTE SRB_HaId; /* 02/002 ASPI host adapter number */ + BYTE SRB_Flags; /* 03/003 Reserved, must = 0 */ + DWORD SRB_Hdr_Rsvd; /* 04/004 Reserved, must = 0 */ + BYTE SRB_Target; /* 08/008 Target's SCSI ID */ + BYTE SRB_Lun; /* 09/009 Target's LUN */ + WORD SRB_Rsvd1; /* 0a/010 Reserved for alignment */ + DWORD SRB_BufLen; /* 0c/012 Data Allocation Length */ + BYTE FAR *SRB_BufPointer; /* 10/016 Data Buffer Pointer */ + BYTE SRB_SenseLen; /* 14/020 Sense Allocation Length */ + BYTE SRB_CDBLen; /* 15/021 CDB Length */ + BYTE SRB_HaStat; /* 16/022 Host Adapter Status */ + BYTE SRB_TargStat; /* 17/023 Target Status */ + VOID FAR *SRB_PostProc; /* 18/024 Post routine */ + BYTE SRB_Rsvd2[20]; /* 1c/028 Reserved, must = 0 */ + BYTE CDBByte[16]; /* 30/048 SCSI CDB */ + BYTE SenseArea[SENSE_LEN+2]; /* 40/064 Request Sense buffer */ +} PACKED SRB_ExecSCSICmd, *PSRB_ExecSCSICmd, FAR *LPSRB_ExecSCSICmd; + + +typedef struct +{ + BYTE SRB_Cmd; /* 00/000 ASPI cmd code == SC_ABORT_SRB */ + BYTE SRB_Status; /* 01/001 ASPI command status byte */ + BYTE SRB_HaId; /* 02/002 ASPI host adapter number */ + BYTE SRB_Flags; /* 03/003 Reserved, must = 0 */ + DWORD SRB_Hdr_Rsvd; /* 04/004 Reserved, must = 0 */ + void *SRB_ToAbort; /* 08/008 Pointer to SRB to abort */ +} PACKED SRB_Abort, *PSRB_Abort, FAR *LPSRB_Abort; + + +/*************************************************************************** + ** SRB - BUS DEVICE RESET - SC_RESET_DEV (4) + ***************************************************************************/ +typedef struct +{ + BYTE SRB_Cmd; /* 00/000 ASPI cmd code == SC_RESET_DEV */ + BYTE SRB_Status; /* 01/001 ASPI command status byte */ + BYTE SRB_HaId; /* 02/002 ASPI host adapter number */ + DWORD SRB_Flags; /* 04/004 Reserved */ + BYTE SRB_Target; /* 08/008 Target's SCSI ID */ + BYTE SRB_Lun; /* 09/009 Target's LUN number */ + BYTE SRB_Rsvd1[12]; /* 0A/010 Reserved for alignment */ + BYTE SRB_HaStat; /* 16/022 Host Adapter Status */ + BYTE SRB_TargStat; /* 17/023 Target Status */ + VOID FAR *SRB_PostProc; /* 18/024 Post routine */ + BYTE SRB_Rsvd2[36]; /* 1C/028 Reserved, must = 0 */ +} SRB_BusDeviceReset, *PSRB_BusDeviceReset, FAR *LPSRB_BusDeviceReset; + +typedef struct tag_ASPI32BUFF +{ + PBYTE AB_BufPointer; + DWORD AB_BufLen; + DWORD AB_ZeroFill; + DWORD AB_Reserved; +} PACKED ASPI32BUFF, *PASPI32BUFF, FAR *LPASPI32BUFF; + +typedef struct +{ + BYTE SRB_Cmd; + BYTE SRB_Status; + BYTE SRB_HaId; + BYTE SRB_Flags; + DWORD SRB_Hdr_Rsvd; +} SRB, *PSRB, FAR *LPSRB; + +#endif diff --git a/tools/ioemu/iodev/biosdev.cc b/tools/ioemu/iodev/biosdev.cc new file mode 100644 index 0000000000..d4a6ef2b8c --- /dev/null +++ b/tools/ioemu/iodev/biosdev.cc @@ -0,0 +1,212 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: biosdev.cc,v 1.7 2003/12/08 19:36:23 danielg4 Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + +// Here are the virtual ports use to display messages from the bioses : +// +// 0x0400 : rombios Panic port with message +// 0x0401 : rombios Panic port with line number +// 0x0402 : rombios Info port with message +// 0x0403 : rombios Debug port with message +// +// 0x0500 : vgabios Info port with message +// 0x0501 : vgabios Panic port with message +// 0x0502 : vgabios Panic port with line number +// 0x0503 : vgabios Debug port with message + + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" + +bx_biosdev_c *theBiosDevice; + + int +libbiosdev_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + theBiosDevice = new bx_biosdev_c (); + bx_devices.pluginBiosDevice = theBiosDevice; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theBiosDevice, BX_PLUGIN_BIOSDEV); + return(0); // Success +} + + void +libbiosdev_LTX_plugin_fini(void) +{ +} + +logfunctions *bioslog; +logfunctions *vgabioslog; + +bx_biosdev_c::bx_biosdev_c(void) +{ + bioslog = new logfunctions(); + bioslog->put("BIOS"); + bioslog->settype(BIOSLOG); + s.bios_message_i = 0; + + vgabioslog = new logfunctions(); + vgabioslog->put("VBIOS"); + vgabioslog->settype(BIOSLOG); + s.vgabios_message_i = 0; +} + +bx_biosdev_c::~bx_biosdev_c(void) +{ + if ( bioslog != NULL ) + { + delete bioslog; + bioslog = NULL; + } + + if ( vgabioslog != NULL ) + { + delete vgabioslog; + vgabioslog = NULL; + } +} + + void +bx_biosdev_c::init(void) +{ + DEV_register_iowrite_handler(this, write_handler, 0x0400, "Bios Panic Port 1", 3); + DEV_register_iowrite_handler(this, write_handler, 0x0401, "Bios Panic Port 2", 3); + DEV_register_iowrite_handler(this, write_handler, 0x0403, "Bios Debug Port", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0402, "Bios Info Port", 1); + + DEV_register_iowrite_handler(this, write_handler, 0x0501, "VGABios Panic Port 1", 3); + DEV_register_iowrite_handler(this, write_handler, 0x0502, "VGABios Panic Port 2", 3); + DEV_register_iowrite_handler(this, write_handler, 0x0503, "VGABios Debug Port", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0500, "VGABios Info Port", 1); +} + + void +bx_biosdev_c::reset(unsigned type) +{ +} + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_biosdev_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_BIOS_SMF + bx_biosdev_c *class_ptr = (bx_biosdev_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + void +bx_biosdev_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_BIOS_SMF + UNUSED(io_len); + + + switch (address) { + // 0x400-0x401 are used as panic ports for the rombios + case 0x0401: + if (BX_BIOS_THIS s.bios_message_i > 0) { + // if there are bits of message in the buffer, print them as the + // panic message. Otherwise fall into the next case. + if (BX_BIOS_THIS s.bios_message_i >= BX_BIOS_MESSAGE_SIZE) + BX_BIOS_THIS s.bios_message_i = BX_BIOS_MESSAGE_SIZE-1; + BX_BIOS_THIS s.bios_message[ BX_BIOS_THIS s.bios_message_i] = 0; + BX_BIOS_THIS s.bios_message_i = 0; + bioslog->panic("%s", BX_BIOS_THIS s.bios_message); + break; + } + case 0x0400: + bioslog->panic("BIOS panic at rombios.c, line %d", value); + break; + + // 0x0402 is used as the info port for the rombios + // 0x0403 is used as the debug port for the rombios + case 0x0402: + case 0x0403: + BX_BIOS_THIS s.bios_message[BX_BIOS_THIS s.bios_message_i] = + (Bit8u) value; + BX_BIOS_THIS s.bios_message_i ++; + if ( BX_BIOS_THIS s.bios_message_i >= BX_BIOS_MESSAGE_SIZE ) { + BX_BIOS_THIS s.bios_message[ BX_BIOS_MESSAGE_SIZE - 1] = 0; + BX_BIOS_THIS s.bios_message_i = 0; + if (address==0x403) bioslog->ldebug("%s", BX_BIOS_THIS s.bios_message); + else bioslog->info("%s", BX_BIOS_THIS s.bios_message); + } + else if ((value & 0xff) == '\n') { + BX_BIOS_THIS s.bios_message[ BX_BIOS_THIS s.bios_message_i - 1 ] = 0; + BX_BIOS_THIS s.bios_message_i = 0; + if (address==0x403) bioslog->ldebug("%s", BX_BIOS_THIS s.bios_message); + else bioslog->info("%s", BX_BIOS_THIS s.bios_message); + } + break; + + // 0x501-0x502 are used as panic ports for the vgabios + case 0x0502: + if (BX_BIOS_THIS s.vgabios_message_i > 0) { + // if there are bits of message in the buffer, print them as the + // panic message. Otherwise fall into the next case. + if (BX_BIOS_THIS s.vgabios_message_i >= BX_BIOS_MESSAGE_SIZE) + BX_BIOS_THIS s.vgabios_message_i = BX_BIOS_MESSAGE_SIZE-1; + BX_BIOS_THIS s.vgabios_message[ BX_BIOS_THIS s.vgabios_message_i] = 0; + BX_BIOS_THIS s.vgabios_message_i = 0; + vgabioslog->panic("%s", BX_BIOS_THIS s.vgabios_message); + break; + } + case 0x0501: + vgabioslog->panic("BIOS panic at rombios.c, line %d", value); + break; + + // 0x0500 is used as the message port for the vgabios + case 0x0500: + case 0x0503: + BX_BIOS_THIS s.vgabios_message[BX_BIOS_THIS s.vgabios_message_i] = + (Bit8u) value; + BX_BIOS_THIS s.vgabios_message_i ++; + if ( BX_BIOS_THIS s.vgabios_message_i >= BX_BIOS_MESSAGE_SIZE ) { + BX_BIOS_THIS s.vgabios_message[ BX_BIOS_MESSAGE_SIZE - 1] = 0; + BX_BIOS_THIS s.vgabios_message_i = 0; + if (address==0x503) vgabioslog->ldebug("%s", BX_BIOS_THIS s.vgabios_message); + else vgabioslog->info("%s", BX_BIOS_THIS s.vgabios_message); + } + else if ((value & 0xff) == '\n') { + BX_BIOS_THIS s.vgabios_message[ BX_BIOS_THIS s.vgabios_message_i - 1 ] = 0; + BX_BIOS_THIS s.vgabios_message_i = 0; + if (address==0x503) vgabioslog->ldebug("%s", BX_BIOS_THIS s.vgabios_message); + else vgabioslog->info("%s", BX_BIOS_THIS s.vgabios_message); + } + break; + + default: + break; + } +} diff --git a/tools/ioemu/iodev/biosdev.h b/tools/ioemu/iodev/biosdev.h new file mode 100644 index 0000000000..7cd98736f4 --- /dev/null +++ b/tools/ioemu/iodev/biosdev.h @@ -0,0 +1,63 @@ + +// $Id: biosdev.h,v 1.3 2002/10/24 21:07:09 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 BX_BIOS_MESSAGE_SIZE 80 + + +#if BX_USE_BIOS_SMF +# define BX_BIOS_SMF static +# define BX_BIOS_THIS theBiosDevice-> +#else +# define BX_BIOS_SMF +# define BX_BIOS_THIS this-> +#endif + + +class bx_biosdev_c : public bx_devmodel_c { +public: + bx_biosdev_c(void); + ~bx_biosdev_c(void); + + virtual void init(void); + virtual void reset (unsigned type); + +private: + + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#if !BX_USE_BIOS_SMF + void write(Bit32u address, Bit32u value, unsigned io_len); +#endif + + struct { + Bit8u bios_message[BX_BIOS_MESSAGE_SIZE]; + unsigned int bios_message_i; + + Bit8u vgabios_message[BX_BIOS_MESSAGE_SIZE]; + unsigned int vgabios_message_i; + } s; // state information + + }; diff --git a/tools/ioemu/iodev/cdrom.cc b/tools/ioemu/iodev/cdrom.cc new file mode 100644 index 0000000000..2b78e8d15a --- /dev/null +++ b/tools/ioemu/iodev/cdrom.cc @@ -0,0 +1,1338 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: cdrom.cc,v 1.66 2003/12/08 23:49:48 danielg4 Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + +// These are the low-level CDROM functions which are called +// from 'harddrv.cc'. They effect the OS specific functionality +// needed by the CDROM emulation in 'harddrv.cc'. Mostly, just +// ioctl() calls and such. Should be fairly easy to add support +// for your OS if it is not supported yet. + + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#if BX_SUPPORT_CDROM + +#define LOG_THIS /* no SMF tricks here, not needed */ + +extern "C" { +#include <errno.h> +} + +#ifdef __linux__ +extern "C" { +#include <sys/ioctl.h> +#include <linux/cdrom.h> +// I use the framesize in non OS specific code too +#define BX_CD_FRAMESIZE CD_FRAMESIZE +} + +#elif defined(__GNU__) || (defined(__CYGWIN32__) && !defined(WIN32)) +extern "C" { +#include <sys/ioctl.h> +#define BX_CD_FRAMESIZE 2048 +#define CD_FRAMESIZE 2048 +} + +#elif BX_WITH_MACOS +#define BX_CD_FRAMESIZE 2048 +#define CD_FRAMESIZE 2048 + +#elif defined(__sun) +extern "C" { +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/cdio.h> +#define BX_CD_FRAMESIZE CDROM_BLK_2048 +} + +#elif defined(__DJGPP__) +extern "C" { +#include <sys/ioctl.h> +#define BX_CD_FRAMESIZE 2048 +#define CD_FRAMESIZE 2048 +} + +#elif defined(__BEOS__) +#include "cdrom_beos.h" +#define BX_CD_FRAMESIZE 2048 + +#elif (defined (__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)) +// OpenBSD pre version 2.7 may require extern "C" { } structure around +// all the includes, because the i386 sys/disklabel.h contains code which +// c++ considers invalid. +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> +#include <sys/cdio.h> +#include <sys/ioctl.h> +#include <sys/disklabel.h> +// ntohl(x) et al have been moved out of sys/param.h in FreeBSD 5 +#include <netinet/in.h> + +// XXX +#define BX_CD_FRAMESIZE 2048 +#define CD_FRAMESIZE 2048 + +#elif defined(__APPLE__) +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <dev/disk.h> +#include <errno.h> +#include <paths.h> +#include <sys/param.h> + +#include <IOKit/IOKitLib.h> +#include <IOKit/IOBSD.h> +#include <IOKit/storage/IOCDMedia.h> +#include <IOKit/storage/IOMedia.h> +#include <IOKit/storage/IOCDTypes.h> +#include <CoreFoundation/CoreFoundation.h> + +// These definitions were taken from mount_cd9660.c +// There are some similar definitions in IOCDTypes.h +// however there seems to be some dissagreement in +// the definition of CDTOC.length +struct _CDMSF { + u_char minute; + u_char second; + u_char frame; +}; + +#define MSF_TO_LBA(msf) \ + (((((msf).minute * 60UL) + (msf).second) * 75UL) + (msf).frame - 150) + +struct _CDTOC_Desc { + u_char session; + u_char ctrl_adr; /* typed to be machine and compiler independent */ + u_char tno; + u_char point; + struct _CDMSF address; + u_char zero; + struct _CDMSF p; +}; + +struct _CDTOC { + u_short length; /* in native cpu endian */ + u_char first_session; + u_char last_session; + struct _CDTOC_Desc trackdesc[1]; +}; + +static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator, mach_port_t *masterPort ); +static kern_return_t GetDeviceFilePath( io_iterator_t mediaIterator, char *deviceFilePath, CFIndex maxPathSize ); +//int OpenDrive( const char *deviceFilePath ); +static struct _CDTOC * ReadTOC( const char * devpath ); + +static char CDDevicePath[ MAXPATHLEN ]; + +#define BX_CD_FRAMESIZE 2048 +#define CD_FRAMESIZE 2048 + +#elif defined(WIN32) +// windows.h included by bochs.h +#include <winioctl.h> +#include "aspi-win32.h" +#include "scsidefs.h" + +DWORD (*GetASPI32SupportInfo)(void); +DWORD (*SendASPI32Command)(LPSRB); +BOOL (*GetASPI32Buffer)(PASPI32BUFF); +BOOL (*FreeASPI32Buffer)(PASPI32BUFF); +BOOL (*TranslateASPI32Address)(PDWORD,PDWORD); +DWORD (*GetASPI32DLLVersion)(void); + + +static BOOL bUseASPI = FALSE; +static BOOL bHaveDev; +static UINT cdromCount = 0; +static HINSTANCE hASPI = NULL; + +#define BX_CD_FRAMESIZE 2048 +#define CD_FRAMESIZE 2048 + +#else // all others (Irix, Tru64) +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#define BX_CD_FRAMESIZE 2048 +#define CD_FRAMESIZE 2048 +#endif + +#include <stdio.h> + +#ifdef __APPLE__ +static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator, + mach_port_t *masterPort ) +{ + kern_return_t kernResult; + CFMutableDictionaryRef classesToMatch; + kernResult = IOMasterPort( bootstrap_port, masterPort ); + if ( kernResult != KERN_SUCCESS ) + { + fprintf ( stderr, "IOMasterPort returned %d\n", kernResult ); + return kernResult; + } + // CD media are instances of class kIOCDMediaClass. + classesToMatch = IOServiceMatching( kIOCDMediaClass ); + if ( classesToMatch == NULL ) + fprintf ( stderr, "IOServiceMatching returned a NULL dictionary.\n" ); + else + { + // Each IOMedia object has a property with key kIOMediaEjectableKey + // which is true if the media is indeed ejectable. So add property + // to CFDictionary for matching. + CFDictionarySetValue( classesToMatch, + CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue ); + } + kernResult = IOServiceGetMatchingServices( *masterPort, + classesToMatch, mediaIterator ); + if ( (kernResult != KERN_SUCCESS) || (*mediaIterator == NULL) ) + fprintf( stderr, "No ejectable CD media found.\n kernResult = %d\n", kernResult ); + + return kernResult; +} + + +static kern_return_t GetDeviceFilePath( io_iterator_t mediaIterator, + char *deviceFilePath, CFIndex maxPathSize ) +{ + io_object_t nextMedia; + kern_return_t kernResult = KERN_FAILURE; + nextMedia = IOIteratorNext( mediaIterator ); + if ( nextMedia == NULL ) + { + *deviceFilePath = '\0'; + } + else + { + CFTypeRef deviceFilePathAsCFString; + deviceFilePathAsCFString = IORegistryEntryCreateCFProperty( + nextMedia, CFSTR( kIOBSDNameKey ), + kCFAllocatorDefault, 0 ); + *deviceFilePath = '\0'; + if ( deviceFilePathAsCFString ) + { + size_t devPathLength = strlen( _PATH_DEV ); + strcpy( deviceFilePath, _PATH_DEV ); + if ( CFStringGetCString( (const __CFString *) deviceFilePathAsCFString, + deviceFilePath + devPathLength, + maxPathSize - devPathLength, + kCFStringEncodingASCII ) ) + { + // fprintf( stderr, "BSD path: %s\n", deviceFilePath ); + kernResult = KERN_SUCCESS; + } + CFRelease( deviceFilePathAsCFString ); + } + } + IOObjectRelease( nextMedia ); + return kernResult; +} + + +static int OpenDrive( const char *deviceFilePath ) +{ + + int fileDescriptor; + + fileDescriptor = open( deviceFilePath, O_RDONLY ); + if ( fileDescriptor == -1 ) + fprintf( stderr, "Error %d opening device %s.\n", errno, deviceFilePath ); + return fileDescriptor; + +} + +static struct _CDTOC * ReadTOC( const char * devpath ) { + + struct _CDTOC * toc_p = NULL; + io_iterator_t iterator = 0; + io_registry_entry_t service = 0; + CFDictionaryRef properties = 0; + CFDataRef data = 0; + mach_port_t port = 0; + char * devname; + + if (( devname = strrchr( devpath, '/' )) != NULL ) { + ++devname; + } + else { + devname = (char *) devpath; + } + + if ( IOMasterPort(bootstrap_port, &port ) != KERN_SUCCESS ) { + fprintf( stderr, "IOMasterPort failed\n" ); + goto Exit; + } + + if ( IOServiceGetMatchingServices( port, IOBSDNameMatching( port, 0, devname ), + &iterator ) != KERN_SUCCESS ) { + fprintf( stderr, "IOServiceGetMatchingServices failed\n" ); + goto Exit; + } + + service = IOIteratorNext( iterator ); + + IOObjectRelease( iterator ); + + iterator = 0; + + while ( service && !IOObjectConformsTo( service, "IOCDMedia" )) { + if ( IORegistryEntryGetParentIterator( service, kIOServicePlane, + &iterator ) != KERN_SUCCESS ) { + fprintf( stderr, "IORegistryEntryGetParentIterator failed\n" ); + goto Exit; + } + + IOObjectRelease( service ); + service = IOIteratorNext( iterator ); + IOObjectRelease( iterator ); + + } + + if ( service == NULL ) { + fprintf( stderr, "CD media not found\n" ); + goto Exit; + } + + if ( IORegistryEntryCreateCFProperties( service, (__CFDictionary **) &properties, + kCFAllocatorDefault, + kNilOptions ) != KERN_SUCCESS ) { + fprintf( stderr, "IORegistryEntryGetParentIterator failed\n" ); + goto Exit; + } + + data = (CFDataRef) CFDictionaryGetValue( properties, CFSTR(kIOCDMediaTOCKey) ); + if ( data == NULL ) { + fprintf( stderr, "CFDictionaryGetValue failed\n" ); + goto Exit; + } + else { + + CFRange range; + CFIndex buflen; + + buflen = CFDataGetLength( data ) + 1; + range = CFRangeMake( 0, buflen ); + toc_p = (struct _CDTOC *) malloc( buflen ); + if ( toc_p == NULL ) { + fprintf( stderr, "Out of memory\n" ); + goto Exit; + } + else { + CFDataGetBytes( data, range, (unsigned char *) toc_p ); + } + + /* + fprintf( stderr, "Table of contents\n length %d first %d last %d\n", + toc_p->length, toc_p->first_session, toc_p->last_session ); + */ + + CFRelease( properties ); + + } + + + Exit: + + if ( service ) { + IOObjectRelease( service ); + } + + return toc_p; + +} +#endif + +#ifdef WIN32 + +bool ReadCDSector(unsigned int hid, unsigned int tid, unsigned int lun, unsigned long frame, unsigned char *buf, int bufsize) +{ + HANDLE hEventSRB; + SRB_ExecSCSICmd srb; + DWORD dwStatus; + + hEventSRB = CreateEvent(NULL, TRUE, FALSE, NULL); + + memset(&srb,0,sizeof(SRB_ExecSCSICmd)); + srb.SRB_Cmd = SC_EXEC_SCSI_CMD; + srb.SRB_HaId = hid; + srb.SRB_Target = tid; + srb.SRB_Lun = lun; + srb.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + srb.SRB_SenseLen = SENSE_LEN; + srb.SRB_PostProc = hEventSRB; + srb.SRB_BufPointer = buf; + srb.SRB_BufLen = bufsize; + srb.SRB_CDBLen = 10; + srb.CDBByte[0] = SCSI_READ10; + srb.CDBByte[2] = (unsigned char) (frame>>24); + srb.CDBByte[3] = (unsigned char) (frame>>16); + srb.CDBByte[4] = (unsigned char) (frame>>8); + srb.CDBByte[5] = (unsigned char) (frame); + srb.CDBByte[7] = 0; + srb.CDBByte[8] = 1; /* read 1 frames */ + + ResetEvent(hEventSRB); + dwStatus = SendASPI32Command((SRB *)&srb); + if(dwStatus == SS_PENDING) { + WaitForSingleObject(hEventSRB, 100000); + } + CloseHandle(hEventSRB); + return (srb.SRB_TargStat == STATUS_GOOD); +} + +int GetCDCapacity(unsigned int hid, unsigned int tid, unsigned int lun) +{ + HANDLE hEventSRB; + SRB_ExecSCSICmd srb; + DWORD dwStatus; + unsigned char buf[8]; + + hEventSRB = CreateEvent(NULL, TRUE, FALSE, NULL); + + memset(&buf, 0, sizeof(buf)); + memset(&srb,0,sizeof(SRB_ExecSCSICmd)); + srb.SRB_Cmd = SC_EXEC_SCSI_CMD; + srb.SRB_HaId = hid; + srb.SRB_Target = tid; + srb.SRB_Lun = lun; + srb.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY; + srb.SRB_SenseLen = SENSE_LEN; + srb.SRB_PostProc = hEventSRB; + srb.SRB_BufPointer = (unsigned char *)buf; + srb.SRB_BufLen = 8; + srb.SRB_CDBLen = 10; + srb.CDBByte[0] = SCSI_READCDCAP; + srb.CDBByte[2] = 0; + srb.CDBByte[3] = 0; + srb.CDBByte[4] = 0; + srb.CDBByte[5] = 0; + srb.CDBByte[8] = 0; + + ResetEvent(hEventSRB); + dwStatus = SendASPI32Command((SRB *)&srb); + if(dwStatus == SS_PENDING) { + WaitForSingleObject(hEventSRB, 100000); + } + + CloseHandle(hEventSRB); + return ((buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]) * ((buf[4] << 24) + (buf[5] << 16) + (buf[6] << 8) + buf[7]); +} + +#endif + +cdrom_interface::cdrom_interface(char *dev) +{ + put("CD"); + settype(CDLOG); + fd = -1; // File descriptor not yet allocated + + if ( dev == NULL ) + path = NULL; + else { + path = strdup(dev); + } + using_file=0; +} + +void +cdrom_interface::init(void) { + BX_DEBUG(("Init $Id: cdrom.cc,v 1.66 2003/12/08 23:49:48 danielg4 Exp $")); + BX_INFO(("file = '%s'",path)); +} + +cdrom_interface::~cdrom_interface(void) +{ +#ifdef WIN32 +#else + if (fd >= 0) + close(fd); +#endif + if (path) + free(path); + BX_DEBUG(("Exit")); +} + + bx_bool +cdrom_interface::insert_cdrom(char *dev) +{ + unsigned char buffer[BX_CD_FRAMESIZE]; + ssize_t ret; + + // Load CD-ROM. Returns false if CD is not ready. + if (dev != NULL) path = strdup(dev); + BX_INFO (("load cdrom with path=%s", path)); +#ifdef WIN32 + char drive[256]; + OSVERSIONINFO osi; + if ( (path[1] == ':') && (strlen(path) == 2) ) + { + osi.dwOSVersionInfoSize = sizeof(osi); + GetVersionEx(&osi); + if(osi.dwPlatformId == VER_PLATFORM_WIN32_NT) { + // Use direct device access under windows NT/2k + + // With all the backslashes it's hard to see, but to open D: drive + // the name would be: \\.\d: + sprintf(drive, "\\\\.\\%s", path); + using_file = 0; + BX_INFO (("Using direct access for cdrom.")); + // This trick only works for Win2k and WinNT, so warn the user of that. + } else { + BX_INFO(("Using ASPI for cdrom. Drive letters are unused yet.")); + bUseASPI = TRUE; + } + } + else + { + strcpy(drive,path); + using_file = 1; + bUseASPI = FALSE; + BX_INFO (("Opening image file as a cd")); + } + if(bUseASPI) { + DWORD d; + UINT cdr, cnt, max; + UINT i, j, k; + SRB_HAInquiry sh; + SRB_GDEVBlock sd; + if (!hASPI) { + hASPI = LoadLibrary("WNASPI32.DLL"); + } + if(hASPI) { + SendASPI32Command = (DWORD(*)(LPSRB))GetProcAddress( hASPI, "SendASPI32Command" ); + GetASPI32DLLVersion = (DWORD(*)(void))GetProcAddress( hASPI, "GetASPI32DLLVersion" ); + GetASPI32SupportInfo = (DWORD(*)(void))GetProcAddress( hASPI, "GetASPI32SupportInfo" ); +// BX_INFO(("Using first CDROM. Please upgrade your ASPI drivers to version 4.01 or later if you wish to specify a cdrom driver.")); + + cdr = 0; + bHaveDev = FALSE; + d = GetASPI32SupportInfo(); + cnt = LOBYTE(LOWORD(d)); + for(i = 0; i < cnt; i++) { + memset(&sh, 0, sizeof(sh)); + sh.SRB_Cmd = SC_HA_INQUIRY; + sh.SRB_HaId = i; + SendASPI32Command((LPSRB)&sh); + if(sh.SRB_Status != SS_COMP) + continue; + + max = (int)sh.HA_Unique[3]; + for(j = 0; j < max; j++) { + for(k = 0; k < 8; k++) { + memset(&sd, 0, sizeof(sd)); + sd.SRB_Cmd = SC_GET_DEV_TYPE; + sd.SRB_HaId = i; + sd.SRB_Target = j; + sd.SRB_Lun = k; + SendASPI32Command((LPSRB)&sd); + if(sd.SRB_Status == SS_COMP) { + if(sd.SRB_DeviceType == DTYPE_CDROM) { + cdr++; + if(cdr > cdromCount) { + hid = i; + tid = j; + lun = k; + cdromCount++; + bHaveDev = TRUE; + } + } + } + if(bHaveDev) break; + } + if(bHaveDev) break; + } + + } + } else { + BX_PANIC(("Could not load ASPI drivers, so cdrom access will fail")); + } + fd=1; + } else { + BX_INFO(("Using direct access for CDROM")); + hFile=CreateFile((char *)&drive, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL); + if (hFile !=(void *)0xFFFFFFFF) + fd=1; + } +#elif defined(__APPLE__) + if(strcmp(path, "drive") == 0) + { + mach_port_t masterPort = NULL; + io_iterator_t mediaIterator; + kern_return_t kernResult; + + BX_INFO(( "Insert CDROM" )); + + kernResult = FindEjectableCDMedia( &mediaIterator, &masterPort ); + if ( kernResult != KERN_SUCCESS ) { + BX_INFO (("Unable to find CDROM")); + return false; + } + + kernResult = GetDeviceFilePath( mediaIterator, CDDevicePath, sizeof( CDDevicePath ) ); + if ( kernResult != KERN_SUCCESS ) { + BX_INFO (("Unable to get CDROM device file path" )); + return false; + } + + // Here a cdrom was found so see if we can read from it. + // At this point a failure will result in panic. + if ( strlen( CDDevicePath ) ) { + fd = open(CDDevicePath, O_RDONLY); + } + } + else + { + fd = open(path, O_RDONLY); + } +#else + // all platforms except win32 + fd = open(path, O_RDONLY); +#endif + if (fd < 0) { + BX_ERROR(( "open cd failed for %s: %s", path, strerror(errno))); + return(false); + } + + // I just see if I can read a sector to verify that a + // CD is in the drive and readable. +#ifdef WIN32 + if(bUseASPI) { + return ReadCDSector(hid, tid, lun, 0, buffer, BX_CD_FRAMESIZE); + } else { + if (!ReadFile(hFile, (void *) buffer, BX_CD_FRAMESIZE, (unsigned long *) &ret, NULL)) { + CloseHandle(hFile); + fd = -1; + BX_DEBUG(( "insert_cdrom: read returns error." )); + return(false); + } + } +#else + // do fstat to determine if it's a file or a device, then set using_file. + struct stat stat_buf; + ret = fstat (fd, &stat_buf); + if (ret) { + BX_PANIC (("fstat cdrom file returned error: %s", strerror (errno))); + } + if (S_ISREG (stat_buf.st_mode)) { + using_file = 1; + BX_INFO (("Opening image file %s as a cd.", path)); + } else { + using_file = 0; + BX_INFO (("Using direct access for cdrom.")); + } + + ret = read(fd, (char*) &buffer, BX_CD_FRAMESIZE); + if (ret < 0) { + close(fd); + fd = -1; + BX_DEBUG(( "insert_cdrom: read returns error: %s", strerror (errno) )); + return(false); + } +#endif + return(true); +} + + int +cdrom_interface::start_cdrom() +{ + // Spin up the cdrom drive. + + if (fd >= 0) { +#if defined(__NetBSD__) + if (ioctl (fd, CDIOCSTART) < 0) + BX_DEBUG(( "start_cdrom: start returns error: %s", strerror (errno) )); + return(true); +#else + BX_INFO(("start_cdrom: your OS is not supported yet.")); + return(false); // OS not supported yet, return false always. +#endif + } + return(false); +} + + void +cdrom_interface::eject_cdrom() +{ + // Logically eject the CD. I suppose we could stick in + // some ioctl() calls to really eject the CD as well. + + if (fd >= 0) { +#if (defined(__OpenBSD__) || defined(__FreeBSD__)) + (void) ioctl (fd, CDIOCALLOW); + if (ioctl (fd, CDIOCEJECT) < 0) + BX_DEBUG(( "eject_cdrom: eject returns error." )); +#endif + +#ifdef WIN32 +if (using_file == 0) +{ + if(bUseASPI) { + } else { + DWORD lpBytesReturned; + DeviceIoControl(hFile, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &lpBytesReturned, NULL); + } +} +#else // WIN32 + +#if __linux__ + if (!using_file) + ioctl (fd, CDROMEJECT, NULL); +#endif + + close(fd); +#endif // WIN32 + fd = -1; + } +} + + + bx_bool +cdrom_interface::read_toc(uint8* buf, int* length, bx_bool msf, int start_track) +{ + // Read CD TOC. Returns false if start track is out of bounds. + + if (fd < 0) { + BX_PANIC(("cdrom: read_toc: file not open.")); + } + +#if defined(WIN32) + if (1) { // This is a hack and works okay if there's one rom track only +#else + if (using_file) { +#endif + // From atapi specs : start track can be 0-63, AA + if ((start_track > 1) && (start_track != 0xaa)) + return false; + + buf[2] = 1; + buf[3] = 1; + + int len = 4; + if (start_track <= 1) { + buf[len++] = 0; // Reserved + buf[len++] = 0x14; // ADR, control + buf[len++] = 1; // Track number + buf[len++] = 0; // Reserved + + // Start address + if (msf) { + buf[len++] = 0; // reserved + buf[len++] = 0; // minute + buf[len++] = 2; // second + buf[len++] = 0; // frame + } else { + buf[len++] = 0; + buf[len++] = 0; + buf[len++] = 0; + buf[len++] = 0; // logical sector 0 + } + } + + // Lead out track + buf[len++] = 0; // Reserved + buf[len++] = 0x16; // ADR, control + buf[len++] = 0xaa; // Track number + buf[len++] = 0; // Reserved + + uint32 blocks = capacity(); + + // Start address + if (msf) { + buf[len++] = 0; // reserved + buf[len++] = (uint8)(((blocks + 150) / 75) / 60); // minute + buf[len++] = (uint8)(((blocks + 150) / 75) % 60); // second + buf[len++] = (uint8)((blocks + 150) % 75); // frame; + } else { + buf[len++] = (blocks >> 24) & 0xff; + buf[len++] = (blocks >> 16) & 0xff; + buf[len++] = (blocks >> 8) & 0xff; + buf[len++] = (blocks >> 0) & 0xff; + } + + buf[0] = ((len-2) >> 8) & 0xff; + buf[1] = (len-2) & 0xff; + + *length = len; + + return true; + } + // all these implementations below are the platform-dependent code required + // to read the TOC from a physical cdrom. +#ifdef WIN32 + { +/* #define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM + #define IOCTL_CDROM_READ_TOC CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS) + unsigned long iBytesReturned; + DeviceIoControl(hFile, IOCTL_CDROM_READ_TOC, NULL, 0, NULL, 0, &iBytesReturned, NULL); */ + BX_ERROR (("WARNING: read_toc is not implemented, just returning length=1")); + *length = 1; + return true; + } +#elif __linux__ || defined(__sun) + { + struct cdrom_tochdr tochdr; + if (ioctl(fd, CDROMREADTOCHDR, &tochdr)) + BX_PANIC(("cdrom: read_toc: READTOCHDR failed.")); + + if ((start_track > tochdr.cdth_trk1) && (start_track != 0xaa)) + return false; + + buf[2] = tochdr.cdth_trk0; + buf[3] = tochdr.cdth_trk1; + + if (start_track < tochdr.cdth_trk0) + start_track = tochdr.cdth_trk0; + + int len = 4; + for (int i = start_track; i <= tochdr.cdth_trk1; i++) { + struct cdrom_tocentry tocentry; + tocentry.cdte_format = (msf) ? CDROM_MSF : CDROM_LBA; + tocentry.cdte_track = i; + if (ioctl(fd, CDROMREADTOCENTRY, &tocentry)) + BX_PANIC(("cdrom: read_toc: READTOCENTRY failed.")); + buf[len++] = 0; // Reserved + buf[len++] = (tocentry.cdte_adr << 4) | tocentry.cdte_ctrl ; // ADR, control + buf[len++] = i; // Track number + buf[len++] = 0; // Reserved + + // Start address + if (msf) { + buf[len++] = 0; // reserved + buf[len++] = tocentry.cdte_addr.msf.minute; + buf[len++] = tocentry.cdte_addr.msf.second; + buf[len++] = tocentry.cdte_addr.msf.frame; + } else { + buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 24) & 0xff; + buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 16) & 0xff; + buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 8) & 0xff; + buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 0) & 0xff; + } + } + + // Lead out track + struct cdrom_tocentry tocentry; + tocentry.cdte_format = (msf) ? CDROM_MSF : CDROM_LBA; +#ifdef CDROM_LEADOUT + tocentry.cdte_track = CDROM_LEADOUT; +#else + tocentry.cdte_track = 0xaa; +#endif + if (ioctl(fd, CDROMREADTOCENTRY, &tocentry)) + BX_PANIC(("cdrom: read_toc: READTOCENTRY lead-out failed.")); + buf[len++] = 0; // Reserved + buf[len++] = (tocentry.cdte_adr << 4) | tocentry.cdte_ctrl ; // ADR, control + buf[len++] = 0xaa; // Track number + buf[len++] = 0; // Reserved + + // Start address + if (msf) { + buf[len++] = 0; // reserved + buf[len++] = tocentry.cdte_addr.msf.minute; + buf[len++] = tocentry.cdte_addr.msf.second; + buf[len++] = tocentry.cdte_addr.msf.frame; + } else { + buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 24) & 0xff; + buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 16) & 0xff; + buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 8) & 0xff; + buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 0) & 0xff; + } + + buf[0] = ((len-2) >> 8) & 0xff; + buf[1] = (len-2) & 0xff; + + *length = len; + + return true; + } +#elif (defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)) + { + struct ioc_toc_header h; + struct ioc_read_toc_entry t; + + if (ioctl (fd, CDIOREADTOCHEADER, &h) < 0) + BX_PANIC(("cdrom: read_toc: READTOCHDR failed.")); + + if ((start_track > h.ending_track) && (start_track != 0xaa)) + return false; + + buf[2] = h.starting_track; + buf[3] = h.ending_track; + + if (start_track < h.starting_track) + start_track = h.starting_track; + + int len = 4; + for (int i = start_track; i <= h.ending_track; i++) { + struct cd_toc_entry tocentry; + t.address_format = (msf) ? CD_MSF_FORMAT : CD_LBA_FORMAT; + t.starting_track = i; + t.data_len = sizeof(tocentry); + t.data = &tocentry; + + if (ioctl (fd, CDIOREADTOCENTRYS, &t) < 0) + BX_PANIC(("cdrom: read_toc: READTOCENTRY failed.")); + + buf[len++] = 0; // Reserved + buf[len++] = (tocentry.addr_type << 4) | tocentry.control ; // ADR, control + buf[len++] = i; // Track number + buf[len++] = 0; // Reserved + + // Start address + if (msf) { + buf[len++] = 0; // reserved + buf[len++] = tocentry.addr.msf.minute; + buf[len++] = tocentry.addr.msf.second; + buf[len++] = tocentry.addr.msf.frame; + } else { + buf[len++] = (((unsigned)tocentry.addr.lba) >> 24) & 0xff; + buf[len++] = (((unsigned)tocentry.addr.lba) >> 16) & 0xff; + buf[len++] = (((unsigned)tocentry.addr.lba) >> 8) & 0xff; + buf[len++] = (((unsigned)tocentry.addr.lba) >> 0) & 0xff; + } + } + + // Lead out track + struct cd_toc_entry tocentry; + t.address_format = (msf) ? CD_MSF_FORMAT : CD_LBA_FORMAT; + t.starting_track = 0xaa; + t.data_len = sizeof(tocentry); + t.data = &tocentry; + + if (ioctl (fd, CDIOREADTOCENTRYS, &t) < 0) + BX_PANIC(("cdrom: read_toc: READTOCENTRY lead-out failed.")); + + buf[len++] = 0; // Reserved + buf[len++] = (tocentry.addr_type << 4) | tocentry.control ; // ADR, control + buf[len++] = 0xaa; // Track number + buf[len++] = 0; // Reserved + + // Start address + if (msf) { + buf[len++] = 0; // reserved + buf[len++] = tocentry.addr.msf.minute; + buf[len++] = tocentry.addr.msf.second; + buf[len++] = tocentry.addr.msf.frame; + } else { + buf[len++] = (((unsigned)tocentry.addr.lba) >> 24) & 0xff; + buf[len++] = (((unsigned)tocentry.addr.lba) >> 16) & 0xff; + buf[len++] = (((unsigned)tocentry.addr.lba) >> 8) & 0xff; + buf[len++] = (((unsigned)tocentry.addr.lba) >> 0) & 0xff; + } + + buf[0] = ((len-2) >> 8) & 0xff; + buf[1] = (len-2) & 0xff; + + *length = len; + + return true; + } +#elif defined(__APPLE__) + // Read CD TOC. Returns false if start track is out of bounds. + +#if 1 + { + struct _CDTOC * toc = ReadTOC( CDDevicePath ); + + if ((start_track > toc->last_session) && (start_track != 0xaa)) + return false; + + buf[2] = toc->first_session; + buf[3] = toc->last_session; + + if (start_track < toc->first_session) + start_track = toc->first_session; + + int len = 4; + for (int i = start_track; i <= toc->last_session; i++) { + buf[len++] = 0; // Reserved + buf[len++] = toc->trackdesc[i].ctrl_adr ; // ADR, control + buf[len++] = i; // Track number + buf[len++] = 0; // Reserved + + // Start address + if (msf) { + buf[len++] = 0; // reserved + buf[len++] = toc->trackdesc[i].address.minute; + buf[len++] = toc->trackdesc[i].address.second; + buf[len++] = toc->trackdesc[i].address.frame; + } else { + unsigned lba = (unsigned)(MSF_TO_LBA(toc->trackdesc[i].address)); + buf[len++] = (lba >> 24) & 0xff; + buf[len++] = (lba >> 16) & 0xff; + buf[len++] = (lba >> 8) & 0xff; + buf[len++] = (lba >> 0) & 0xff; + } + } + + // Lead out track + buf[len++] = 0; // Reserved + buf[len++] = 0x16; // ADR, control + buf[len++] = 0xaa; // Track number + buf[len++] = 0; // Reserved + + uint32 blocks = capacity(); + + // Start address + if (msf) { + buf[len++] = 0; // reserved + buf[len++] = (uint8)(((blocks + 150) / 75) / 60); // minute + buf[len++] = (uint8)(((blocks + 150) / 75) % 60); // second + buf[len++] = (uint8)((blocks + 150) % 75); // frame; + } else { + buf[len++] = (blocks >> 24) & 0xff; + buf[len++] = (blocks >> 16) & 0xff; + buf[len++] = (blocks >> 8) & 0xff; + buf[len++] = (blocks >> 0) & 0xff; + } + + buf[0] = ((len-2) >> 8) & 0xff; + buf[1] = (len-2) & 0xff; + + *length = len; + + return true; +// BX_INFO(( "Read TOC - Not Implemented" )); +// return false; + } +#else + BX_INFO(( "Read TOC - Not Implemented" )); + return false; +#endif +#else + BX_INFO(("read_toc: your OS is not supported yet.")); + return(false); // OS not supported yet, return false always. +#endif +} + + + uint32 +cdrom_interface::capacity() +{ + // Return CD-ROM capacity. I believe you want to return + // the number of blocks of capacity the actual media has. + +#if !defined WIN32 + // win32 has its own way of doing this + if (using_file) { + // return length of the image file + struct stat stat_buf; + int ret = fstat (fd, &stat_buf); + if (ret) { + BX_PANIC (("fstat on cdrom image returned err: %s", strerror(errno))); + } + BX_INFO (("cdrom size is %lld bytes", stat_buf.st_size)); + if ((stat_buf.st_size % 2048) != 0) { + BX_ERROR (("expected cdrom image to be a multiple of 2048 bytes")); + } + return stat_buf.st_size / 2048; + } +#endif + +#ifdef __BEOS__ + return GetNumDeviceBlocks(fd, BX_CD_FRAMESIZE); +#elif defined(__sun) + { + struct stat buf = {0}; + + if (fd < 0) { + BX_PANIC(("cdrom: capacity: file not open.")); + } + + if( fstat(fd, &buf) != 0 ) + BX_PANIC(("cdrom: capacity: stat() failed.")); + + return(buf.st_size); + } +#elif (defined(__NetBSD__) || defined(__OpenBSD__)) + { + // We just read the disklabel, imagine that... + struct disklabel lp; + + if (fd < 0) + BX_PANIC(("cdrom: capacity: file not open.")); + + if (ioctl(fd, DIOCGDINFO, &lp) < 0) + BX_PANIC(("cdrom: ioctl(DIOCGDINFO) failed")); + + BX_DEBUG(( "capacity: %u", lp.d_secperunit )); + return(lp.d_secperunit); + } +#elif defined(__linux__) + { + // Read the TOC to get the data size, since BLKGETSIZE doesn't work on + // non-ATAPI drives. This is based on Keith Jones code below. + // <splite@purdue.edu> 21 June 2001 + + int i, dtrk_lba, num_sectors; + int dtrk = 0; + struct cdrom_tochdr td; + struct cdrom_tocentry te; + + if (fd < 0) + BX_PANIC(("cdrom: capacity: file not open.")); + + if (ioctl(fd, CDROMREADTOCHDR, &td) < 0) + BX_PANIC(("cdrom: ioctl(CDROMREADTOCHDR) failed")); + + num_sectors = -1; + dtrk_lba = -1; + + for (i = td.cdth_trk0; i <= td.cdth_trk1; i++) { + te.cdte_track = i; + te.cdte_format = CDROM_LBA; + if (ioctl(fd, CDROMREADTOCENTRY, &te) < 0) + BX_PANIC(("cdrom: ioctl(CDROMREADTOCENTRY) failed")); + + if (dtrk_lba != -1) { + num_sectors = te.cdte_addr.lba - dtrk_lba; + break; + } + if (te.cdte_ctrl & CDROM_DATA_TRACK) { + dtrk = i; + dtrk_lba = te.cdte_addr.lba; + } + } + + if (num_sectors < 0) { + if (dtrk_lba != -1) { + te.cdte_track = CDROM_LEADOUT; + te.cdte_format = CDROM_LBA; + if (ioctl(fd, CDROMREADTOCENTRY, &te) < 0) + BX_PANIC(("cdrom: ioctl(CDROMREADTOCENTRY) failed")); + num_sectors = te.cdte_addr.lba - dtrk_lba; + } else + BX_PANIC(("cdrom: no data track found")); + } + + BX_INFO(("cdrom: Data track %d, length %d", dtrk, num_sectors)); + + return(num_sectors); + + } +#elif defined(__FreeBSD__) + { + // Read the TOC to get the size of the data track. + // Keith Jones <freebsd.dev@blueyonder.co.uk>, 16 January 2000 + +#define MAX_TRACKS 100 + + int i, num_tracks, num_sectors; + struct ioc_toc_header td; + struct ioc_read_toc_entry rte; + struct cd_toc_entry toc_buffer[MAX_TRACKS + 1]; + + if (fd < 0) + BX_PANIC(("cdrom: capacity: file not open.")); + + if (ioctl(fd, CDIOREADTOCHEADER, &td) < 0) + BX_PANIC(("cdrom: ioctl(CDIOREADTOCHEADER) failed")); + + num_tracks = (td.ending_track - td.starting_track) + 1; + if (num_tracks > MAX_TRACKS) + BX_PANIC(("cdrom: TOC is too large")); + + rte.address_format = CD_LBA_FORMAT; + rte.starting_track = td.starting_track; + rte.data_len = (num_tracks + 1) * sizeof(struct cd_toc_entry); + rte.data = toc_buffer; + if (ioctl(fd, CDIOREADTOCENTRYS, &rte) < 0) + BX_PANIC(("cdrom: ioctl(CDIOREADTOCENTRYS) failed")); + + num_sectors = -1; + for (i = 0; i < num_tracks; i++) { + if (rte.data[i].control & 4) { /* data track */ + num_sectors = ntohl(rte.data[i + 1].addr.lba) + - ntohl(rte.data[i].addr.lba); + BX_INFO(( "cdrom: Data track %d, length %d", + rte.data[i].track, num_sectors)); + break; + } + } + + if (num_sectors < 0) + BX_PANIC(("cdrom: no data track found")); + + return(num_sectors); + + } +#elif defined WIN32 + { + if(bUseASPI) { + return (GetCDCapacity(hid, tid, lun) / 2352); + } else if(using_file) { + ULARGE_INTEGER FileSize; + FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart); + return (FileSize.QuadPart / 2048); + } else { /* direct device access */ + DWORD SectorsPerCluster; + DWORD TotalNumOfClusters; + GetDiskFreeSpace( path, &SectorsPerCluster, NULL, NULL, &TotalNumOfClusters); + return (TotalNumOfClusters * SectorsPerCluster); + } + } +#elif defined __APPLE__ +// Find the size of the first data track on the cd. This has produced +// the same results as the linux version on every cd I have tried, about +// 5. The differences here seem to be that the entries in the TOC when +// retrieved from the IOKit interface appear in a reversed order when +// compared with the linux READTOCENTRY ioctl. + { + // Return CD-ROM capacity. I believe you want to return + // the number of bytes of capacity the actual media has. + + BX_INFO(( "Capacity" )); + + struct _CDTOC * toc = ReadTOC( CDDevicePath ); + + if ( toc == NULL ) { + BX_PANIC(( "capacity: Failed to read toc" )); + } + + size_t toc_entries = ( toc->length - 2 ) / sizeof( struct _CDTOC_Desc ); + + BX_DEBUG(( "reading %d toc entries\n", toc_entries )); + + int start_sector = -1; + int data_track = -1; + + // Iterate through the list backward. Pick the first data track and + // get the address of the immediately previous (or following depending + // on how you look at it). The difference in the sector numbers + // is returned as the sized of the data track. + for ( int i=toc_entries - 1; i>=0; i-- ) { + + BX_DEBUG(( "session %d ctl_adr %d tno %d point %d lba %d z %d p lba %d\n", + (int)toc->trackdesc[i].session, + (int)toc->trackdesc[i].ctrl_adr, + (int)toc->trackdesc[i].tno, + (int)toc->trackdesc[i].point, + MSF_TO_LBA( toc->trackdesc[i].address ), + (int)toc->trackdesc[i].zero, + MSF_TO_LBA(toc->trackdesc[i].p ))); + + if ( start_sector != -1 ) { + + start_sector = MSF_TO_LBA(toc->trackdesc[i].p) - start_sector; + break; + + } + + if (( toc->trackdesc[i].ctrl_adr >> 4) != 1 ) continue; + + if ( toc->trackdesc[i].ctrl_adr & 0x04 ) { + + data_track = toc->trackdesc[i].point; + + start_sector = MSF_TO_LBA(toc->trackdesc[i].p); + + } + + } + + free( toc ); + + if ( start_sector == -1 ) { + start_sector = 0; + } + + BX_INFO(("first data track %d data size is %d", data_track, start_sector)); + + return start_sector; + } +#else + BX_ERROR(( "capacity: your OS is not supported yet." )); + return(0); +#endif +} + + void BX_CPP_AttrRegparmN(2) +cdrom_interface::read_block(uint8* buf, int lba) +{ + // Read a single block from the CD + +#ifdef WIN32 + LARGE_INTEGER pos; +#else + off_t pos; +#endif + ssize_t n; + +#ifdef WIN32 + if(bUseASPI) { + ReadCDSector(hid, tid, lun, lba, buf, BX_CD_FRAMESIZE); + n = BX_CD_FRAMESIZE; + } else { + pos.QuadPart = (LONGLONG)lba*BX_CD_FRAMESIZE; + pos.LowPart = SetFilePointer(hFile, pos.LowPart, &pos.HighPart, SEEK_SET); + if ((pos.LowPart == 0xffffffff) && (GetLastError() != NO_ERROR)) { + BX_PANIC(("cdrom: read_block: SetFilePointer returned error.")); + } + ReadFile(hFile, (void *) buf, BX_CD_FRAMESIZE, (unsigned long *) &n, NULL); + } +#elif defined(__APPLE__) +#define CD_SEEK_DISTANCE kCDSectorSizeWhole + if(using_file) + { + pos = lseek(fd, lba*BX_CD_FRAMESIZE, SEEK_SET); + if (pos < 0) { + BX_PANIC(("cdrom: read_block: lseek returned error.")); + } + n = read(fd, buf, BX_CD_FRAMESIZE); + } + else + { + // This seek will leave us 16 bytes from the start of the data + // hence the magic number. + pos = lseek(fd, lba*CD_SEEK_DISTANCE + 16, SEEK_SET); + if (pos < 0) { + BX_PANIC(("cdrom: read_block: lseek returned error.")); + } + n = read(fd, buf, CD_FRAMESIZE); + } +#else + pos = lseek(fd, lba*BX_CD_FRAMESIZE, SEEK_SET); + if (pos < 0) { + BX_PANIC(("cdrom: read_block: lseek returned error.")); + } + n = read(fd, (char*) buf, BX_CD_FRAMESIZE); +#endif + + if (n != BX_CD_FRAMESIZE) { + BX_PANIC(("cdrom: read_block: read returned %d", + (int) n)); + } +} + +#endif /* if BX_SUPPORT_CDROM */ diff --git a/tools/ioemu/iodev/cdrom.h b/tools/ioemu/iodev/cdrom.h new file mode 100644 index 0000000000..29fdfaff85 --- /dev/null +++ b/tools/ioemu/iodev/cdrom.h @@ -0,0 +1,67 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: cdrom.h,v 1.13 2003/08/19 00:37:03 cbothamy Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + +// Header file for low-level OS specific CDROM emulation + + +class cdrom_interface : public logfunctions { +public: + cdrom_interface(char *dev); + ~cdrom_interface(void); + void init(void); + + // Load CD-ROM. Returns false if CD is not ready. + bx_bool insert_cdrom(char *dev = NULL); + + // Logically eject the CD. + void eject_cdrom(); + + // Read CD TOC. Returns false if start track is out of bounds. + bx_bool read_toc(uint8* buf, int* length, bx_bool msf, int start_track); + + // Return CD-ROM capacity (in 2048 byte frames) + uint32 capacity(); + + // Read a single block from the CD + void read_block(uint8* buf, int lba) BX_CPP_AttrRegparmN(2); + + // Start (spin up) the CD. + int start_cdrom(); + +private: + int fd; + char *path; + + int using_file; +#ifdef WIN32 + HANDLE hFile; + int hid; + int tid; + int lun; +#endif + }; + diff --git a/tools/ioemu/iodev/cdrom_beos.h b/tools/ioemu/iodev/cdrom_beos.h new file mode 100644 index 0000000000..8a22d5c0f8 --- /dev/null +++ b/tools/ioemu/iodev/cdrom_beos.h @@ -0,0 +1,10 @@ +#ifndef CDROM_BEOS_H +#define CDROM_BEOS_H + +#include <stddef.h> //for off_t + +off_t GetNumDeviceBlocks(int fd, int block_size); +int GetLogicalBlockSize(int fd); +int GetDeviceBlockSize(int fd); + +#endif diff --git a/tools/ioemu/iodev/cmos.cc b/tools/ioemu/iodev/cmos.cc new file mode 100644 index 0000000000..fbf3144989 --- /dev/null +++ b/tools/ioemu/iodev/cmos.cc @@ -0,0 +1,824 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: cmos.cc,v 1.44 2003/12/27 13:43:41 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" + +#define LOG_THIS theCmosDevice-> + +bx_cmos_c *theCmosDevice = NULL; + +// CMOS register definitions from Ralf Brown's interrupt list v6.1, in a file +// called cmos.lst. In cases where there are multiple uses for a given +// register in the interrupt list, I only listed the purpose that Bochs +// actually uses it for, but I wrote "alternatives" next to it. +#define REG_SEC 0x00 +#define REG_SEC_ALARM 0x01 +#define REG_MIN 0x02 +#define REG_MIN_ALARM 0x03 +#define REG_HOUR 0x04 +#define REG_HOUR_ALARM 0x05 +#define REG_WEEK_DAY 0x06 +#define REG_MONTH_DAY 0x07 +#define REG_MONTH 0x08 +#define REG_YEAR 0x09 +#define REG_STAT_A 0x0a +#define REG_STAT_B 0x0b +#define REG_STAT_C 0x0c +#define REG_STAT_D 0x0d +#define REG_DIAGNOSTIC_STATUS 0x0e /* alternatives */ +#define REG_SHUTDOWN_STATUS 0x0f +#define REG_EQUIPMENT_BYTE 0x14 +#define REG_CSUM_HIGH 0x2e +#define REG_CSUM_LOW 0x2f +#define REG_IBM_CENTURY_BYTE 0x32 /* alternatives */ +#define REG_IBM_PS2_CENTURY_BYTE 0x37 /* alternatives */ + +// Bochs CMOS map (to be completed) +// +// Idx Len Description +// 0x15 2 Base memory in 1k +// 0x17 2 Memory size above 1M in 1k +// 0x30 2 Memory size above 1M in 1k +// 0x34 2 Memory size above 16M in 64k +// + +// check that BX_NUM_CMOS_REGS is 64 or 128 +#if (BX_NUM_CMOS_REGS == 64) +#elif (BX_NUM_CMOS_REGS == 128) +#else +#error "Invalid BX_NUM_CMOS_REGS value in config.h" +#endif + + + int +libcmos_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + theCmosDevice = new bx_cmos_c (); + bx_devices.pluginCmosDevice = theCmosDevice; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theCmosDevice, BX_PLUGIN_CMOS); + return(0); // Success +} + + void +libcmos_LTX_plugin_fini(void) +{ +} + +bx_cmos_c::bx_cmos_c(void) +{ + put("CMOS"); + settype(CMOSLOG); + + unsigned i; + for (i=0; i<BX_NUM_CMOS_REGS; i++) + s.reg[i] = 0; + s.periodic_timer_index = BX_NULL_TIMER_HANDLE; + s.one_second_timer_index = BX_NULL_TIMER_HANDLE; + s.uip_timer_index = BX_NULL_TIMER_HANDLE; +} + +bx_cmos_c::~bx_cmos_c(void) +{ + BX_DEBUG(("Exit.")); +} + + + void +bx_cmos_c::init(void) +{ + BX_DEBUG(("Init $Id: cmos.cc,v 1.44 2003/12/27 13:43:41 vruppert Exp $")); + // CMOS RAM & RTC + + DEV_register_ioread_handler(this, read_handler, 0x0070, "CMOS RAM", 1); + DEV_register_ioread_handler(this, read_handler, 0x0071, "CMOS RAM", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0070, "CMOS RAM", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0071, "CMOS RAM", 1); + DEV_register_irq(8, "CMOS RTC"); + if (BX_CMOS_THIS s.periodic_timer_index == BX_NULL_TIMER_HANDLE) { + BX_CMOS_THIS s.periodic_timer_index = + DEV_register_timer(this, periodic_timer_handler, + 1000000, 1,0, "cmos"); // continuous, not-active + } + if (BX_CMOS_THIS s.one_second_timer_index == BX_NULL_TIMER_HANDLE) { + BX_CMOS_THIS s.one_second_timer_index = + DEV_register_timer(this, one_second_timer_handler, + 1000000, 1,0, "cmos"); // continuous, not-active + } + if (BX_CMOS_THIS s.uip_timer_index == BX_NULL_TIMER_HANDLE) { + BX_CMOS_THIS s.uip_timer_index = + DEV_register_timer(this, uip_timer_handler, + 244, 0, 0, "cmos"); // one-shot, not-active + } + +#if BX_USE_SPECIFIED_TIME0 != 0 + // ??? this will not be correct for using an image file. + // perhaps take values in CMOS and work backwards to find + // s.timeval from values read in. + BX_CMOS_THIS s.timeval = BX_USE_SPECIFIED_TIME0; + +#else // BX_USE_SPECIFIED_TIME0 != 0 + + // localtime + if (bx_options.clock.Otime0->get () == BX_CLOCK_TIME0_LOCAL) { + BX_INFO(("Using local time for initial clock")); + BX_CMOS_THIS s.timeval = time(NULL); + } + // utc + else if (bx_options.clock.Otime0->get () == BX_CLOCK_TIME0_UTC) { + bx_bool utc_ok = 0; + + BX_INFO(("Using utc time for initial clock")); + + BX_CMOS_THIS s.timeval = time(NULL); + +#if BX_HAVE_GMTIME +#if BX_HAVE_MKTIME + struct tm *utc_holder = gmtime(&BX_CMOS_THIS s.timeval); + utc_holder->tm_isdst = -1; + utc_ok = 1; + BX_CMOS_THIS s.timeval = mktime(utc_holder); +#elif BX_HAVE_TIMELOCAL + struct tm *utc_holder = gmtime(&BX_CMOS_THIS s.timeval); + utc_holder->tm_isdst = 0; // XXX Is this correct??? + utc_ok = 1; + BX_CMOS_THIS s.timeval = timelocal(utc_holder); +#endif //BX_HAVE_MKTIME +#endif //BX_HAVE_GMTIME + + if (!utc_ok) { + BX_ERROR(("UTC time is not supported on your platform. Using current time(NULL)")); + } + } + else { + BX_INFO(("Using specified time for initial clock")); + BX_CMOS_THIS s.timeval = bx_options.clock.Otime0->get (); + } +#endif // BX_USE_SPECIFIED_TIME0 != 0 + + char *tmptime; + while( (tmptime = strdup(ctime(&(BX_CMOS_THIS s.timeval)))) == NULL) { + BX_PANIC(("Out of memory.")); + } + tmptime[strlen(tmptime)-1]='\0'; + + BX_INFO(("Setting initial clock to: %s (time0=%u)", tmptime, (Bit32u)BX_CMOS_THIS s.timeval)); + + update_clock(); + BX_CMOS_THIS s.timeval_change = 0; + + // load CMOS from image file if requested. + if (bx_options.cmos.OcmosImage->get ()) { + // CMOS image file requested + int fd, ret; + struct stat stat_buf; + + fd = open(bx_options.cmos.Opath->getptr (), O_RDONLY +#ifdef O_BINARY + | O_BINARY +#endif + ); + if (fd < 0) { + BX_PANIC(("trying to open cmos image file '%s'", + bx_options.cmos.Opath->getptr ())); + } + ret = fstat(fd, &stat_buf); + if (ret) { + BX_PANIC(("CMOS: could not fstat() image file.")); + } + if (stat_buf.st_size != BX_NUM_CMOS_REGS) { + BX_PANIC(("CMOS: image file not same size as BX_NUM_CMOS_REGS.")); + } + + ret = ::read(fd, (bx_ptr_t) BX_CMOS_THIS s.reg, BX_NUM_CMOS_REGS); + if (ret != BX_NUM_CMOS_REGS) { + BX_PANIC(("CMOS: error reading cmos file.")); + } + close(fd); + BX_INFO(("successfuly read from image file '%s'.", + bx_options.cmos.Opath->getptr ())); + } + else { + // CMOS values generated + BX_CMOS_THIS s.reg[REG_STAT_A] = 0x26; + BX_CMOS_THIS s.reg[REG_STAT_B] = 0x02; + BX_CMOS_THIS s.reg[REG_STAT_C] = 0x00; + BX_CMOS_THIS s.reg[REG_STAT_D] = 0x80; +#if BX_SUPPORT_FPU == 1 + BX_CMOS_THIS s.reg[REG_EQUIPMENT_BYTE] |= 0x02; +#endif + } +} + + void +bx_cmos_c::reset(unsigned type) +{ + BX_CMOS_THIS s.cmos_mem_address = 0; + + // RESET affects the following registers: + // CRA: no effects + // CRB: bits 4,5,6 forced to 0 + // CRC: bits 4,5,6,7 forced to 0 + // CRD: no effects + BX_CMOS_THIS s.reg[REG_STAT_B] &= 0x8f; + BX_CMOS_THIS s.reg[REG_STAT_C] = 0; + + // One second timer for updating clock & alarm functions + bx_pc_system.activate_timer(BX_CMOS_THIS s.one_second_timer_index, + 1000000, 1); + + // handle periodic interrupt rate select + BX_CMOS_THIS CRA_change(); +} + + void +bx_cmos_c::CRA_change(void) +{ + unsigned nibble; + + // Periodic Interrupt timer + nibble = BX_CMOS_THIS s.reg[REG_STAT_A] & 0x0f; + if (nibble == 0) { + // No Periodic Interrupt Rate when 0, deactivate timer + bx_pc_system.deactivate_timer(BX_CMOS_THIS s.periodic_timer_index); + BX_CMOS_THIS s.periodic_interval_usec = (Bit32u) -1; // max value + } + else { + // values 0001b and 0010b are the same as 1000b and 1001b + if (nibble <= 2) + nibble += 7; + BX_CMOS_THIS s.periodic_interval_usec = (unsigned) (1000000.0L / + (32768.0L / (1 << (nibble - 1)))); + + // if Periodic Interrupt Enable bit set, activate timer + if ( BX_CMOS_THIS s.reg[REG_STAT_B] & 0x40 ) + bx_pc_system.activate_timer(BX_CMOS_THIS s.periodic_timer_index, + BX_CMOS_THIS s.periodic_interval_usec, 1); + else + bx_pc_system.deactivate_timer(BX_CMOS_THIS s.periodic_timer_index); + } +} + + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_cmos_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_CMOS_SMF + bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + Bit32u +bx_cmos_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif + Bit8u ret8; + + if (bx_dbg.cmos) + BX_INFO(("CMOS read of CMOS register 0x%02x", + (unsigned) BX_CMOS_THIS s.cmos_mem_address)); + + + switch (address) { + case 0x0070: + BX_INFO(("read of index port 0x70. returning 0xff")); + // Volker says his boxes return 0xff + //ret8 = BX_CMOS_THIS s.cmos_mem_address; + return(0xff); + break; + case 0x0071: + if (BX_CMOS_THIS s.cmos_mem_address >= BX_NUM_CMOS_REGS) { + BX_PANIC(("unsupported cmos io read, register(0x%02x)!", + (unsigned) BX_CMOS_THIS s.cmos_mem_address)); + } + + ret8 = BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address]; + // all bits of Register C are cleared after a read occurs. + if (BX_CMOS_THIS s.cmos_mem_address == REG_STAT_C) { + BX_CMOS_THIS s.reg[REG_STAT_C] = 0x00; + DEV_pic_lower_irq(8); + } + return(ret8); + break; + + default: + BX_PANIC(("unsupported cmos read, address=0x%04x!", + (unsigned) address)); + return(0); + break; + } +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_cmos_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_CMOS_SMF + bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + void +bx_cmos_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_CMOS_SMF + + if (bx_dbg.cmos) + BX_INFO(("CMOS write to address: 0x%04x = 0x%02x", + (unsigned) address, (unsigned) value)); + + + switch (address) { + case 0x0070: +#if (BX_NUM_CMOS_REGS == 64) + BX_CMOS_THIS s.cmos_mem_address = value & 0x3F; +#else + BX_CMOS_THIS s.cmos_mem_address = value & 0x7F; +#endif + break; + + case 0x0071: + if (BX_CMOS_THIS s.cmos_mem_address >= BX_NUM_CMOS_REGS) { + BX_PANIC(("unsupported cmos io write, register(0x%02x) = 0x%02x !", + (unsigned) BX_CMOS_THIS s.cmos_mem_address, (unsigned) value)); + return; + } + switch (BX_CMOS_THIS s.cmos_mem_address) { + case REG_SEC_ALARM: // seconds alarm + case REG_MIN_ALARM: // minutes alarm + case REG_HOUR_ALARM: // hours alarm + BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value; + BX_DEBUG(("alarm time changed to %02x:%02x:%02x", BX_CMOS_THIS s.reg[REG_HOUR_ALARM], + BX_CMOS_THIS s.reg[REG_MIN_ALARM], BX_CMOS_THIS s.reg[REG_SEC_ALARM])); + return; + break; + + case REG_SEC: // seconds + case REG_MIN: // minutes + case REG_HOUR: // hours + case REG_WEEK_DAY: // day of the week + case REG_MONTH_DAY: // day of the month + case REG_MONTH: // month + case REG_YEAR: // year + case REG_IBM_CENTURY_BYTE: // century + case REG_IBM_PS2_CENTURY_BYTE: // century (PS/2) + //BX_INFO(("write reg 0x%02x: value = 0x%02x", + // (unsigned) BX_CMOS_THIS s.cmos_mem_address, (unsigned) value); + BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value; + if (BX_CMOS_THIS s.cmos_mem_address == REG_IBM_PS2_CENTURY_BYTE) { + BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE] = value; + } + if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x80) { + BX_CMOS_THIS s.timeval_change = 1; + } else { + update_timeval(); + } + return; + break; + + case REG_STAT_A: // Control Register A + // bit 7: Update in Progress (read-only) + // 1 = signifies time registers will be updated within 244us + // 0 = time registers will not occur before 244us + // note: this bit reads 0 when CRB bit 7 is 1 + // bit 6..4: Divider Chain Control + // 000 oscillator disabled + // 001 oscillator disabled + // 010 Normal operation + // 011 TEST + // 100 TEST + // 101 TEST + // 110 Divider Chain RESET + // 111 Divider Chain RESET + // bit 3..0: Periodic Interrupt Rate Select + // 0000 None + // 0001 3.90625 ms + // 0010 7.8125 ms + // 0011 122.070 us + // 0100 244.141 us + // 0101 488.281 us + // 0110 976.562 us + // 0111 1.953125 ms + // 1000 3.90625 ms + // 1001 7.8125 ms + // 1010 15.625 ms + // 1011 31.25 ms + // 1100 62.5 ms + // 1101 125 ms + // 1110 250 ms + // 1111 500 ms + + unsigned dcc; + dcc = (value >> 4) & 0x07; + if ((dcc & 0x06) == 0x06) { + BX_INFO(("CRA: divider chain RESET")); + } else if (dcc != 0x02) { + BX_PANIC(("CRA: divider chain control 0x%02x", dcc)); + } + BX_CMOS_THIS s.reg[REG_STAT_A] &= 0x80; + BX_CMOS_THIS s.reg[REG_STAT_A] |= (value & 0x7f); + BX_CMOS_THIS CRA_change(); + return; + break; + + case REG_STAT_B: // Control Register B + // bit 0: Daylight Savings Enable + // 1 = enable daylight savings + // 0 = disable daylight savings + // bit 1: 24/12 houre mode + // 1 = 24 hour format + // 0 = 12 hour format + // bit 2: Data Mode + // 1 = binary format + // 0 = BCD format + // bit 3: "square wave enable" + // Not supported and always read as 0 + // bit 4: Update Ended Interrupt Enable + // 1 = enable generation of update ended interrupt + // 0 = disable + // bit 5: Alarm Interrupt Enable + // 1 = enable generation of alarm interrupt + // 0 = disable + // bit 6: Periodic Interrupt Enable + // 1 = enable generation of periodic interrupt + // 0 = disable + // bit 7: Set mode + // 1 = user copy of time is "frozen" allowing time registers + // to be accessed without regard for an occurance of an update + // 0 = time updates occur normally + + // can not handle binary or 12-hour mode yet. + if (value & 0x04) + BX_PANIC(("write status reg B, binary format enabled.")); + if ( !(value & 0x02) ) + BX_PANIC(("write status reg B, 12 hour mode enabled.")); + + value &= 0xf7; // bit3 always 0 + // Note: setting bit 7 clears bit 4 + if (value & 0x80) + value &= 0xef; + + unsigned prev_CRB; + prev_CRB = BX_CMOS_THIS s.reg[REG_STAT_B]; + BX_CMOS_THIS s.reg[REG_STAT_B] = value; + if ( (prev_CRB & 0x40) != (value & 0x40) ) { + // Periodic Interrupt Enabled changed + if (prev_CRB & 0x40) { + // transition from 1 to 0, deactivate timer + bx_pc_system.deactivate_timer( + BX_CMOS_THIS s.periodic_timer_index); + } + else { + // transition from 0 to 1 + // if rate select is not 0, activate timer + if ( (BX_CMOS_THIS s.reg[REG_STAT_A] & 0x0f) != 0 ) { + bx_pc_system.activate_timer( + BX_CMOS_THIS s.periodic_timer_index, + BX_CMOS_THIS s.periodic_interval_usec, 1); + } + } + } + if ( (prev_CRB >= 0x80) && (value < 0x80) && BX_CMOS_THIS s.timeval_change) { + update_timeval(); + BX_CMOS_THIS s.timeval_change = 0; + } + return; + break; + + case REG_STAT_C: // Control Register C + case REG_STAT_D: // Control Register D + BX_ERROR(("write to control register 0x%02x (read-only)", + BX_CMOS_THIS s.cmos_mem_address)); + break; + + case REG_DIAGNOSTIC_STATUS: + BX_DEBUG(("write register 0x0e: 0x%02x", (unsigned) value)); + break; + + case REG_SHUTDOWN_STATUS: + switch (value) { + case 0x00: /* proceed with normal POST (soft reset) */ + BX_DEBUG(("Reg 0Fh(00): shutdown action = normal POST")); + break; + case 0x01: /* shutdown after memory size check */ + BX_DEBUG(("Reg 0Fh(01): request to change shutdown action" + " to shutdown after memory size check")); + case 0x02: /* shutdown after successful memory test */ + BX_DEBUG(("Reg 0Fh(02): request to change shutdown action" + " to shutdown after successful memory test")); + break; + case 0x03: /* shutdown after failed memory test */ + BX_DEBUG(("Reg 0Fh(03): request to change shutdown action" + " to shutdown after successful memory test")); + break; + case 0x04: /* jump to disk bootstrap routine */ + BX_DEBUG(("Reg 0Fh(04): request to change shutdown action " + "to jump to disk bootstrap routine.")); + break; + case 0x05: /* flush keyboard (issue EOI) and jump via 40h:0067h */ + BX_DEBUG(("Reg 0Fh(05): request to change shutdown action " + "to flush keyboard (issue EOI) and jump via 40h:0067h.")); + break; + case 0x06: + BX_DEBUG(("Reg 0Fh(06): Shutdown after memory test !")); + break; + case 0x07: /* reset (after failed test in virtual mode) */ + BX_DEBUG(("Reg 0Fh(07): request to change shutdown action " + "to reset (after failed test in virtual mode).")); + break; + case 0x08: /* used by POST during protected-mode RAM test (return to POST) */ + BX_DEBUG(("Reg 0Fh(08): request to change shutdown action " + "to return to POST (used by POST during protected-mode RAM test).")); + break; + case 0x09: /* return to BIOS extended memory block move + (interrupt 15h, func 87h was in progress) */ + BX_DEBUG(("Reg 0Fh(09): request to change shutdown action " + "to return to BIOS extended memory block move.")); + break; + case 0x0a: /* jump to DWORD pointer at 40:67 */ + BX_DEBUG(("Reg 0Fh(0a): request to change shutdown action" + " to jump to DWORD at 40:67")); + break; + case 0x0b: /* iret to DWORD pointer at 40:67 */ + BX_DEBUG(("Reg 0Fh(0b): request to change shutdown action" + " to iret to DWORD at 40:67")); + break; + case 0x0c: /* retf to DWORD pointer at 40:67 */ + BX_DEBUG(("Reg 0Fh(0c): request to change shutdown action" + " to retf to DWORD at 40:67")); + break; + default: + BX_PANIC(("unsupported cmos io write to reg F, case 0x%02x!", + (unsigned) value)); + break; + } + break; + + default: + BX_DEBUG(("write reg 0x%02x: value = 0x%02x", + (unsigned) BX_CMOS_THIS s.cmos_mem_address, (unsigned) value)); + break; + } + + BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value; + break; + } +} + + + void +bx_cmos_c::checksum_cmos(void) +{ + unsigned i; + Bit16u sum; + + sum = 0; + for (i=0x10; i<=0x2d; i++) { + sum += BX_CMOS_THIS s.reg[i]; + } + BX_CMOS_THIS s.reg[REG_CSUM_HIGH] = (sum >> 8) & 0xff; /* checksum high */ + BX_CMOS_THIS s.reg[REG_CSUM_LOW] = (sum & 0xff); /* checksum low */ +} + + void +bx_cmos_c::periodic_timer_handler(void *this_ptr) +{ + bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr; + + class_ptr->periodic_timer(); +} + + void +bx_cmos_c::periodic_timer() +{ + // if periodic interrupts are enabled, trip IRQ 8, and + // update status register C + if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x40) { + BX_CMOS_THIS s.reg[REG_STAT_C] |= 0xc0; // Interrupt Request, Periodic Int + DEV_pic_raise_irq(8); + } +} + + void +bx_cmos_c::one_second_timer_handler(void *this_ptr) +{ + bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr; + + class_ptr->one_second_timer(); +} + + void +bx_cmos_c::one_second_timer() +{ + // divider chain reset - RTC stopped + if ((BX_CMOS_THIS s.reg[REG_STAT_A] & 0x60) == 0x60) + return; + + // update internal time/date buffer + BX_CMOS_THIS s.timeval++; + + // Dont update CMOS user copy of time/date if CRB bit7 is 1 + // Nothing else do to + if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x80) + return; + + BX_CMOS_THIS s.reg[REG_STAT_A] |= 0x80; // set UIP bit + + // UIP timer for updating clock & alarm functions + bx_pc_system.activate_timer(BX_CMOS_THIS s.uip_timer_index, + 244, 0); +} + + void +bx_cmos_c::uip_timer_handler(void *this_ptr) +{ + bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr; + + class_ptr->uip_timer(); +} + + void +bx_cmos_c::uip_timer() +{ + update_clock(); + + // if update interrupts are enabled, trip IRQ 8, and + // update status register C + if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x10) { + BX_CMOS_THIS s.reg[REG_STAT_C] |= 0x90; // Interrupt Request, Update Ended + DEV_pic_raise_irq(8); + } + + // compare CMOS user copy of time/date to alarm time/date here + if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x20) { + // Alarm interrupts enabled + bx_bool alarm_match = 1; + if ( (BX_CMOS_THIS s.reg[REG_SEC_ALARM] & 0xc0) != 0xc0 ) { + // seconds alarm not in dont care mode + if (BX_CMOS_THIS s.reg[REG_SEC] != BX_CMOS_THIS s.reg[REG_SEC_ALARM]) + alarm_match = 0; + } + if ( (BX_CMOS_THIS s.reg[REG_MIN_ALARM] & 0xc0) != 0xc0 ) { + // minutes alarm not in dont care mode + if (BX_CMOS_THIS s.reg[REG_MIN] != BX_CMOS_THIS s.reg[REG_MIN_ALARM]) + alarm_match = 0; + } + if ( (BX_CMOS_THIS s.reg[REG_HOUR_ALARM] & 0xc0) != 0xc0 ) { + // hours alarm not in dont care mode + if (BX_CMOS_THIS s.reg[REG_HOUR] != BX_CMOS_THIS s.reg[REG_HOUR_ALARM]) + alarm_match = 0; + } + if (alarm_match) { + BX_CMOS_THIS s.reg[REG_STAT_C] |= 0xa0; // Interrupt Request, Alarm Int + DEV_pic_raise_irq(8); + } + } + BX_CMOS_THIS s.reg[REG_STAT_A] &= 0x7f; // clear UIP bit +} + + + void +bx_cmos_c::update_clock() +{ + struct tm *time_calendar; + unsigned year, month, day, century; + Bit8u val_bcd; + + time_calendar = localtime(& BX_CMOS_THIS s.timeval); + + // update seconds + val_bcd = + ((time_calendar->tm_sec / 10) << 4) | + (time_calendar->tm_sec % 10); + BX_CMOS_THIS s.reg[REG_SEC] = val_bcd; + + // update minutes + val_bcd = + ((time_calendar->tm_min / 10) << 4) | + (time_calendar->tm_min % 10); + BX_CMOS_THIS s.reg[REG_MIN] = val_bcd; + + // update hours + val_bcd = + ((time_calendar->tm_hour / 10) << 4) | + (time_calendar->tm_hour % 10); + BX_CMOS_THIS s.reg[REG_HOUR] = val_bcd; + + // update day of the week + day = time_calendar->tm_wday + 1; // 0..6 to 1..7 + BX_CMOS_THIS s.reg[REG_WEEK_DAY] = ((day / 10) << 4) | (day % 10); + + // update day of the month + day = time_calendar->tm_mday; + BX_CMOS_THIS s.reg[REG_MONTH_DAY] = ((day / 10) << 4) | (day % 10); + + // update month + month = time_calendar->tm_mon + 1; + BX_CMOS_THIS s.reg[REG_MONTH] = ((month / 10) << 4) | (month % 10); + + // update year + year = time_calendar->tm_year % 100; + BX_CMOS_THIS s.reg[REG_YEAR] = ((year / 10) << 4) | (year % 10); + + // update century + century = (time_calendar->tm_year / 100) + 19; + BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE] = + ((century / 10) << 4) | (century % 10); + + // Raul Hudea pointed out that some bioses also use reg 0x37 for the + // century byte. Tony Heller says this is critical in getting WinXP to run. + BX_CMOS_THIS s.reg[REG_IBM_PS2_CENTURY_BYTE] = + BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE]; +} + + void +bx_cmos_c::update_timeval() +{ + struct tm time_calendar; + Bit8u val_bin; + + // update seconds + val_bin = + ((BX_CMOS_THIS s.reg[REG_SEC] >> 4) * 10) + + (BX_CMOS_THIS s.reg[REG_SEC] & 0x0f); + time_calendar.tm_sec = val_bin; + + // update minutes + val_bin = + ((BX_CMOS_THIS s.reg[REG_MIN] >> 4) * 10) + + (BX_CMOS_THIS s.reg[REG_MIN] & 0x0f); + time_calendar.tm_min = val_bin; + + // update hours + val_bin = + ((BX_CMOS_THIS s.reg[REG_HOUR] >> 4) * 10) + + (BX_CMOS_THIS s.reg[REG_HOUR] & 0x0f); + time_calendar.tm_hour = val_bin; + + // update day of the month + val_bin = + ((BX_CMOS_THIS s.reg[REG_MONTH_DAY] >> 4) * 10) + + (BX_CMOS_THIS s.reg[REG_MONTH_DAY] & 0x0f); + time_calendar.tm_mday = val_bin; + + // update month + val_bin = + ((BX_CMOS_THIS s.reg[REG_MONTH] >> 4) * 10) + + (BX_CMOS_THIS s.reg[REG_MONTH] & 0x0f); + time_calendar.tm_mon = val_bin - 1; + + // update year + val_bin = + ((BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE] >> 4) * 10) + + (BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE] & 0x0f); + val_bin = (val_bin - 19) * 100; + val_bin += + (((BX_CMOS_THIS s.reg[REG_YEAR] >> 4) * 10) + + (BX_CMOS_THIS s.reg[REG_YEAR] & 0x0f)); + time_calendar.tm_year = val_bin; + + BX_CMOS_THIS s.timeval = mktime(& time_calendar); +} diff --git a/tools/ioemu/iodev/cmos.h b/tools/ioemu/iodev/cmos.h new file mode 100644 index 0000000000..c53907db3e --- /dev/null +++ b/tools/ioemu/iodev/cmos.h @@ -0,0 +1,90 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: cmos.h,v 1.9 2003/01/04 00:02:07 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + + + + +#if BX_USE_CMOS_SMF +# define BX_CMOS_SMF static +# define BX_CMOS_THIS theCmosDevice-> +#else +# define BX_CMOS_SMF +# define BX_CMOS_THIS this-> +#endif + + +class bx_cmos_c : public bx_cmos_stub_c { +public: + bx_cmos_c(void); + ~bx_cmos_c(void); + + virtual void init(void); + virtual void checksum_cmos(void); + virtual void reset(unsigned type); + + virtual Bit32u get_reg(unsigned reg) { + return s.reg[reg]; + } + virtual void set_reg(unsigned reg, Bit32u val) { + s.reg[reg] = val; + } + virtual time_t get_timeval() { + return s.timeval; + } + + struct { + int periodic_timer_index; + Bit32u periodic_interval_usec; + int one_second_timer_index; + int uip_timer_index; + time_t timeval; + Bit8u cmos_mem_address; + bx_bool timeval_change; + + Bit8u reg[BX_NUM_CMOS_REGS]; + } s; // state information + +private: + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#if !BX_USE_CMOS_SMF + Bit32u read(Bit32u address, unsigned io_len); + void write(Bit32u address, Bit32u value, unsigned len); +#endif + +public: + static void periodic_timer_handler(void *); + static void one_second_timer_handler(void *); + static void uip_timer_handler(void *); + BX_CMOS_SMF void periodic_timer(void); + BX_CMOS_SMF void one_second_timer(void); + BX_CMOS_SMF void uip_timer(void); +private: + BX_CMOS_SMF void update_clock(void); + BX_CMOS_SMF void update_timeval(void); + BX_CMOS_SMF void CRA_change(void); + }; diff --git a/tools/ioemu/iodev/cpu.cc b/tools/ioemu/iodev/cpu.cc new file mode 100644 index 0000000000..59e4c51c05 --- /dev/null +++ b/tools/ioemu/iodev/cpu.cc @@ -0,0 +1,334 @@ +/* + * Main cpu loop for handling I/O requests coming from a virtual machine + * Copyright © 2004, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "bochs.h" +#include <cpu/cpu.h> +#ifdef BX_USE_VMX +#include <sys/ioctl.h> +/* According to POSIX 1003.1-2001 */ +#include <sys/select.h> + +/* According to earlier standards */ +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <values.h> +#endif + +#define LOG_THIS BX_CPU_THIS_PTR + +#ifdef BX_USE_VMX + +//the evtchn fd for polling +int evtchn_fd = -1; +//the evtchn port for polling the notification, should be inputed as bochs's parameter +u16 ioreq_port = 0; + +void *shared_page = NULL; + +//some functions to handle the io req packet + +//get the ioreq packets from share mem +ioreq_t* bx_cpu_c::__get_ioreq(void) +{ + ioreq_t *req; + req = &((vcpu_iodata_t *) shared_page)->vp_ioreq; + if (req->state == STATE_IOREQ_READY) { + req->state = STATE_IOREQ_INPROCESS; + } else { + BX_INFO(("False I/O requrest ... in-service already: %lx, pvalid: %lx,port: %lx, data: %lx, count: %lx, size: %lx\n", req->state, req->pdata_valid, req->addr, req->u.data, req->count, req->size)); + req = NULL; + } + + return req; +} + +//use poll to get the port notification +//ioreq_vec--out,the +//retval--the number of ioreq packet +ioreq_t* bx_cpu_c::get_ioreq(void) +{ + ioreq_t *req; + int rc; + u16 buf[2]; + rc = read(evtchn_fd, buf, 2); + if (rc == 2 && buf[0] == ioreq_port){//got only one matched 16bit port index + // unmask the wanted port again + write(evtchn_fd, &ioreq_port, 2); + + //get the io packet from shared memory + return __get_ioreq(); + } + + //read error or read nothing + return NULL; +} + +//send the ioreq to device model +void bx_cpu_c::dispatch_ioreq(ioreq_t *req) +{ + int ret, i; + int sign; + + sign = (req->df) ? -1 : 1; + + if ((!req->pdata_valid) && (req->dir == IOREQ_WRITE)) { + if (req->size != 4) { + // Bochs expects higher bits to be 0 + req->u.data &= (1UL << (8 * req->size))-1; + } + } + if (req->port_mm == 0){//port io + if(req->dir == IOREQ_READ){//read + if (!req->pdata_valid) + req->u.data = BX_INP(req->addr, req->size); + else { + unsigned long tmp; + + for (i = 0; i < req->count; i++) { + tmp = BX_INP(req->addr, req->size); + BX_MEM_WRITE_PHYSICAL((Bit32u) req->u.pdata + (sign * i * req->size), + req->size, &tmp); + } + } + } else if(req->dir == IOREQ_WRITE) { + if (!req->pdata_valid) { + BX_OUTP(req->addr, (Bit32u) req->u.data, req->size); + } else { + for (i = 0; i < req->count; i++) { + unsigned long tmp; + + BX_MEM_READ_PHYSICAL((Bit32u) req->u.pdata + (sign * i * req->size), req->size, + &tmp); + BX_OUTP(req->addr, (Bit32u) tmp, req->size); + } + } + + } + } else if (req->port_mm == 1){//memory map io + if (!req->pdata_valid) { + if(req->dir == IOREQ_READ){//read + BX_MEM_READ_PHYSICAL(req->addr, req->size, &req->u.data); + } else if(req->dir == IOREQ_WRITE)//write + BX_MEM_WRITE_PHYSICAL(req->addr, req->size, &req->u.data); + } else { + //handle movs + unsigned long tmp; + if (req->dir == IOREQ_READ) { + //BX_INFO(("<READ>addr:%llx, pdata:%llx, size: %x, count: %x\n", req->addr, req->u.pdata, req->size, req->count)); + for (i = 0; i < req->count; i++) { + BX_MEM_READ_PHYSICAL(req->addr + (sign * i * req->size), req->size, &tmp); + BX_MEM_WRITE_PHYSICAL((Bit32u) req->u.pdata + (sign * i * req->size), req->size, &tmp); + } + } else if (req->dir == IOREQ_WRITE) { + //BX_INFO(("<WRITE>addr:%llx, pdata:%llx, size: %x, count: %x\n", req->addr, req->u.pdata, req->size, req->count)); + for (i = 0; i < req->count; i++) { + BX_MEM_READ_PHYSICAL((Bit32u)req->u.pdata + (sign * i * req->size), req->size, &tmp); + BX_MEM_WRITE_PHYSICAL(req->addr + (sign * i * req->size), req->size, &tmp); + } + } + } + } + + /* No state change if state = STATE_IORESP_HOOK */ + if (req->state == STATE_IOREQ_INPROCESS) + req->state = STATE_IORESP_READY; + + send_event = 1; +} + +void +bx_cpu_c::handle_ioreq(void) +{ + ioreq_t *req = get_ioreq(); + if (req) + dispatch_ioreq(req); +} + +void +bx_cpu_c::timer_handler(void) +{ + handle_ioreq(); +} + +#endif + +#define rdtscl(low) \ + __asm__ __volatile__("rdtsc" : "=a" (low) : : "edx") + +void +bx_cpu_c::cpu_loop(int max_instr_count) +{ + Bit8u vector; + fd_set rfds; + unsigned long stime_usec; + struct timeval tv; + int retval; + + /* Watch stdin (fd 0) to see when it has input. */ + FD_ZERO(&rfds); + + while (1) { + static unsigned long long t1 = 0; + unsigned long long t2; + + /* Wait up to one seconds. */ + tv.tv_sec = 0; + tv.tv_usec = 100000; + FD_SET(evtchn_fd, &rfds); + + send_event = 0; + + if (t1 == 0) // the first time + rdtscll(t1); + + retval = select(evtchn_fd+1, &rfds, NULL, NULL, &tv); + if (retval == -1) { + perror("select"); + return; + } + + rdtscll(t2); + +#if __WORDSIZE == 32 +#define ULONGLONG_MAX 0xffffffffffffffffULL +#else +#define ULONGLONG_MAX ULONG_MAX +#endif + + if (t2 <= t1) + BX_TICKN((t2 + ULONGLONG_MAX - t1) / tsc_per_bx_tick); + else + BX_TICKN((t2 - t1) / tsc_per_bx_tick); + t1 = t2; + + timer_handler(); + if (BX_CPU_INTR) { +#if BX_SUPPORT_APIC + if (BX_CPU_THIS_PTR local_apic.INTR) + vector = BX_CPU_THIS_PTR local_apic.acknowledge_int (); + else + vector = DEV_pic_iac(); // may set INTR with next interrupt +#else + // if no local APIC, always acknowledge the PIC. + vector = DEV_pic_iac(); // may set INTR with next interrupt +#endif + interrupt(vector); + } + /* we check DMA after interrupt check*/ + while(BX_HRQ){ + DEV_dma_raise_hlda(); + } + + if (send_event) { + int ret; + ret = xc_evtchn_send(xc_handle, ioreq_port); + if (ret == -1) { + BX_ERROR(("evtchn_send failed on port: %d\n", ioreq_port)); + } + } + } +} + +static __inline__ void set_bit(long nr, volatile void *addr) +{ + __asm__ __volatile__( "lock ; " + "btsl %1,%0" + :"=m" ((*(volatile long *)addr)) + :"Ir" (nr)); + + return; +} + +void +bx_cpu_c::interrupt(Bit8u vector) +{ + unsigned long *intr, tscl; + int ret; + + // Send a message on the event channel. Add the vector to the shared mem + // page. + + rdtscl(tscl); + BX_DEBUG(("%lx: injecting vector: %x\n", tscl, vector)); + intr = &(((vcpu_iodata_t *) shared_page)->vp_intr[0]); + set_bit(vector, intr); + + send_event = 1; +} + +void +bx_cpu_c::init(bx_mem_c*) +{ +#ifdef BX_USE_VMX + if (evtchn_fd != -1)//the evtchn has been opened by another cpu object + return; + + //use nonblock reading not polling, may change in future. + evtchn_fd = open("/dev/xen/evtchn", O_RDWR|O_NONBLOCK); + if (evtchn_fd == -1) { + perror("open"); + return; + } + + BX_INFO(("listening to port: %d\n", ioreq_port)); + /*unmask the wanted port -- bind*/ + if (ioctl(evtchn_fd, ('E'<<8)|2, ioreq_port) == -1) { + perror("ioctl"); + return; + } + +#if 0 + //register the reading evtchn function as timer + bx_pc_system.register_timer(this, timer_handler, 1000,//1000 us, may change + 1,//continuous timer + 1,//active + "cpu reading evtchn handler"); +#endif + +#endif +} + +void +bx_cpu_c::reset(unsigned) +{ +} + +void +bx_cpu_c::atexit() +{ +} + +void +bx_cpu_c::set_INTR(unsigned value) +{ + BX_CPU_THIS_PTR INTR = value; +} + +void +bx_cpu_c::pagingA20Changed() +{ +} + +bx_cpu_c::bx_cpu_c() +{ +} + +bx_cpu_c::~bx_cpu_c() +{ +} diff --git a/tools/ioemu/iodev/crc32.cc b/tools/ioemu/iodev/crc32.cc new file mode 100644 index 0000000000..a20abaaf82 --- /dev/null +++ b/tools/ioemu/iodev/crc32.cc @@ -0,0 +1,49 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: crc32.cc,v 1.4 2001/10/03 13:10:38 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// +/* CRC-32 calculator + * Adapted from http://www.createwindow.org/programming/crc32/ + */ + +#include "crc32.h" + +CRC_Generator::CRC_Generator() { + init(); +} + +void CRC_Generator::init(void) { + Bit32u POLYNOMIAL = 0x04c11db7; + int i; + + for(i = 0; i<0xFF; i++) { + int j; + crc32_table[i]=reflect(i,8) << 24; + for(j=0; j<8; j++) + crc32_table[i] = (crc32_table[i]<<1)^(crc32_table[i] & (1<<31) ? POLYNOMIAL : 0); + crc32_table[i] = reflect(crc32_table[i], 32); + } +} + +Bit32u CRC_Generator::reflect(Bit32u ref, Bit8u ch) { + Bit32u value(0); + int i; + + for(i=1; i<(ch+1); i++) { + if(ref & 1) + value |= 1 << (ch-i); + ref >>= 1; + } + return value; +} + +Bit32u CRC_Generator::get_CRC(Bit8u * buf, Bit32u buflen) { + Bit32u ulCRC(0xFFFFFFFF); + Bit32u len(buflen); + Bit8u * buffer=(Bit8u *) buf; + + while(len--) + ulCRC=(ulCRC>>8)^crc32_table[(ulCRC & 0xFF)^*buffer++]; + return ulCRC ^ 0xFFFFFFFF; +} + diff --git a/tools/ioemu/iodev/crc32.h b/tools/ioemu/iodev/crc32.h new file mode 100644 index 0000000000..faedaf840b --- /dev/null +++ b/tools/ioemu/iodev/crc32.h @@ -0,0 +1,25 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: crc32.h,v 1.3 2001/10/03 13:10:38 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// +/* CRC-32 calculator + * Adapted from http://www.createwindow.org/programming/crc32/ + */ + +#ifndef _CRC_32_H_ +#define _CRC_32_H_ + +#include "bochs.h" + +class CRC_Generator { +private: + Bit32u crc32_table[256]; + Bit32u reflect(Bit32u ref, Bit8u ch); +public: + void init(void); + CRC_Generator(); + Bit32u get_CRC(Bit8u * buf, Bit32u buflen); +}; + +#endif //_CRC_32_H_ + diff --git a/tools/ioemu/iodev/devices.cc b/tools/ioemu/iodev/devices.cc new file mode 100644 index 0000000000..5dd7f5daa3 --- /dev/null +++ b/tools/ioemu/iodev/devices.cc @@ -0,0 +1,685 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: devices.cc,v 1.58 2003/12/26 13:53:39 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + +#include "bochs.h" +#define LOG_THIS bx_devices. + + + +/* main memory size (in Kbytes) + * subtract 1k for extended BIOS area + * report only base memory, not extended mem + */ +#define BASE_MEMORY_IN_K 640 + + +bx_devices_c bx_devices; + + + + +// constructor for bx_devices_c +bx_devices_c::bx_devices_c(void) +{ + put("DEV"); + settype(DEVLOG); + +#if BX_PCI_SUPPORT + pluginPciBridge = &stubPci; + pluginPci2IsaBridge = NULL; +#if BX_PCI_VGA_SUPPORT + pluginPciVgaAdapter = NULL; +#endif +#if BX_PCI_USB_SUPPORT + pluginPciUSBAdapter = NULL; +#endif +#endif + pit = NULL; + pluginKeyboard = &stubKeyboard; + pluginDmaDevice = &stubDma; + pluginFloppyDevice = &stubFloppy; + pluginBiosDevice = NULL; + pluginCmosDevice = &stubCmos; + pluginSerialDevice = NULL; + pluginParallelDevice = NULL; + pluginUnmapped = NULL; + pluginVgaDevice = &stubVga; + pluginPicDevice = &stubPic; + pluginHardDrive = &stubHardDrive; + pluginSB16Device = NULL; + pluginNE2kDevice =&stubNE2k; + pluginExtFpuIrq = NULL; + pluginGameport = NULL; + g2h = NULL; +#if BX_IODEBUG_SUPPORT + iodebug = NULL; +#endif +} + + +bx_devices_c::~bx_devices_c(void) +{ + // nothing needed for now + BX_DEBUG(("Exit.")); + timer_handle = BX_NULL_TIMER_HANDLE; +} + + + void +bx_devices_c::init(BX_MEM_C *newmem) +{ + unsigned i; + + BX_DEBUG(("Init $Id: devices.cc,v 1.58 2003/12/26 13:53:39 vruppert Exp $")); + mem = newmem; + + /* no read / write handlers defined */ + num_read_handles = 0; + num_write_handles = 0; + + /* set unused elements to appropriate values */ + for (i=0; i < BX_MAX_IO_DEVICES; i++) { + io_read_handler[i].funct = NULL; + io_write_handler[i].funct = NULL; + } + + /* set no-default handlers, will be overwritten by the real default handler */ + io_read_handler[BX_DEFAULT_IO_DEVICE].handler_name = "Default"; + io_read_handler[BX_DEFAULT_IO_DEVICE].funct = &default_read_handler; + io_read_handler[BX_DEFAULT_IO_DEVICE].this_ptr = NULL; + io_read_handler[BX_DEFAULT_IO_DEVICE].mask = 7; + io_write_handler[BX_DEFAULT_IO_DEVICE].handler_name = "Default"; + io_write_handler[BX_DEFAULT_IO_DEVICE].funct = &default_write_handler; + io_write_handler[BX_DEFAULT_IO_DEVICE].this_ptr = NULL; + io_write_handler[BX_DEFAULT_IO_DEVICE].mask = 7; + + /* set handlers to the default one */ + for (i=0; i < 0x10000; i++) { + read_handler_id[i] = BX_DEFAULT_IO_DEVICE; + write_handler_id[i] = BX_DEFAULT_IO_DEVICE; + } + + for (i=0; i < BX_MAX_IRQS; i++) { + irq_handler_name[i] = NULL; + } + + // BBD: At present, the only difference between "core" and "optional" + // plugins is that initialization and reset of optional plugins is handled + // by the plugin device list (). Init and reset of core plugins is done + // "by hand" in this file. Basically, we're using core plugins when we + // want to control the init order. + // + // CB: UNMAPPED and BIOSDEV should maybe be optional + PLUG_load_plugin(unmapped, PLUGTYPE_CORE); + PLUG_load_plugin(biosdev, PLUGTYPE_CORE); + PLUG_load_plugin(cmos, PLUGTYPE_CORE); + PLUG_load_plugin(dma, PLUGTYPE_CORE); + PLUG_load_plugin(pic, PLUGTYPE_CORE); + PLUG_load_plugin(vga, PLUGTYPE_CORE); + PLUG_load_plugin(floppy, PLUGTYPE_CORE); + PLUG_load_plugin(harddrv, PLUGTYPE_OPTIONAL); + PLUG_load_plugin(keyboard, PLUGTYPE_OPTIONAL); + if (is_serial_enabled ()) + PLUG_load_plugin(serial, PLUGTYPE_OPTIONAL); + if (is_parallel_enabled ()) + PLUG_load_plugin(parallel, PLUGTYPE_OPTIONAL); + PLUG_load_plugin(extfpuirq, PLUGTYPE_OPTIONAL); +#if BX_SUPPORT_GAME + PLUG_load_plugin(gameport, PLUGTYPE_OPTIONAL); +#endif + + // Start with registering the default (unmapped) handler + pluginUnmapped->init (); + + // PCI logic (i440FX) + if (bx_options.Oi440FXSupport->get ()) { +#if BX_PCI_SUPPORT + PLUG_load_plugin(pci, PLUGTYPE_OPTIONAL); + PLUG_load_plugin(pci2isa, PLUGTYPE_OPTIONAL); +#if BX_PCI_VGA_SUPPORT + PLUG_load_plugin(pcivga, PLUGTYPE_OPTIONAL); +#endif +#if BX_PCI_USB_SUPPORT + PLUG_load_plugin(pciusb, PLUGTYPE_OPTIONAL); +#endif +#else + BX_ERROR(("Bochs is not compiled with PCI support")); +#endif + } + +#if BX_SUPPORT_APIC + // I/O APIC 82093AA + ioapic = & bx_ioapic; + ioapic->init (); +#endif + + // BIOS log + pluginBiosDevice->init (); + + // CMOS RAM & RTC + pluginCmosDevice->init (); + + /*--- 8237 DMA ---*/ + pluginDmaDevice->init(); + + //--- FLOPPY --- + pluginFloppyDevice->init(); + + //--- SOUND --- + if (bx_options.sb16.Opresent->get ()) { +#if BX_SUPPORT_SB16 + PLUG_load_plugin(sb16, PLUGTYPE_OPTIONAL); +#else + BX_ERROR(("Bochs is not compiled with SB16 support")); +#endif + } + + /*--- VGA adapter ---*/ + pluginVgaDevice->init (); + + /*--- 8259A PIC ---*/ + pluginPicDevice->init(); + + /*--- 8254 PIT ---*/ + pit = & bx_pit; + pit->init(); + + bx_virt_timer.init(); + + bx_slowdown_timer.init(); + +#if BX_IODEBUG_SUPPORT + iodebug = &bx_iodebug; + iodebug->init(); +#endif + + // NE2000 NIC + if (bx_options.ne2k.Opresent->get ()) { +#if BX_NE2K_SUPPORT + PLUG_load_plugin(ne2k, PLUGTYPE_OPTIONAL); +#else + BX_ERROR(("Bochs is not compiled with NE2K support")); +#endif + } + +#if 0 + // Guest to Host interface. Used with special guest drivers + // which move data to/from the host environment. + g2h = &bx_g2h; + g2h->init(); +#endif + + // system hardware + register_io_read_handler( this, + &read_handler, + 0x0092, + "Port 92h System Control", 1 ); + register_io_write_handler(this, + &write_handler, + 0x0092, + "Port 92h System Control", 1 ); + + // misc. CMOS + Bit32u extended_memory_in_k = mem->get_memory_in_k() > 1024 ? (mem->get_memory_in_k() - 1024) : 0; + if (extended_memory_in_k > 0xffff) extended_memory_in_k = 0xffff; + + DEV_cmos_set_reg(0x15, (Bit8u) BASE_MEMORY_IN_K); + DEV_cmos_set_reg(0x16, (Bit8u) (BASE_MEMORY_IN_K >> 8)); + DEV_cmos_set_reg(0x17, (Bit8u) (extended_memory_in_k & 0xff) ); + DEV_cmos_set_reg(0x18, (Bit8u) ((extended_memory_in_k >> 8) & 0xff) ); + DEV_cmos_set_reg(0x30, (Bit8u) (extended_memory_in_k & 0xff) ); + DEV_cmos_set_reg(0x31, (Bit8u) ((extended_memory_in_k >> 8) & 0xff) ); + + Bit32u extended_memory_in_64k = mem->get_memory_in_k() > 16384 ? (mem->get_memory_in_k() - 16384) / 64 : 0; + if (extended_memory_in_64k > 0xffff) extended_memory_in_64k = 0xffff; + + DEV_cmos_set_reg(0x34, (Bit8u) (extended_memory_in_64k & 0xff) ); + DEV_cmos_set_reg(0x35, (Bit8u) ((extended_memory_in_64k >> 8) & 0xff) ); + + if (timer_handle != BX_NULL_TIMER_HANDLE) { + timer_handle = bx_pc_system.register_timer( this, timer_handler, + (unsigned) BX_IODEV_HANDLER_PERIOD, 1, 1, "devices.cc"); + } + + // Clear fields for bulk IO acceleration transfers. + bulkIOHostAddr = 0; + bulkIOQuantumsRequested = 0; + bulkIOQuantumsTransferred = 0; + + bx_init_plugins(); + + /* now perform checksum of CMOS memory */ + DEV_cmos_checksum(); +} + + + void +bx_devices_c::reset(unsigned type) +{ + pluginUnmapped->reset(type); +#if BX_PCI_SUPPORT + if (bx_options.Oi440FXSupport->get ()) { + pluginPciBridge->reset(type); + pluginPci2IsaBridge->reset(type); +#if BX_PCI_VGA_SUPPORT + pluginPciVgaAdapter->reset(type); +#endif +#if BX_PCI_USB_SUPPORT + pluginPciUSBAdapter->reset(type); +#endif + } +#endif +#if BX_SUPPORT_IOAPIC + ioapic->reset (type); +#endif + pluginBiosDevice->reset(type); + pluginCmosDevice->reset(type); + pluginDmaDevice->reset(type); + pluginFloppyDevice->reset(type); +#if BX_SUPPORT_SB16 + if (pluginSB16Device) pluginSB16Device->reset(type); +#endif + pluginVgaDevice->reset(type); + pluginPicDevice->reset(type); + pit->reset(type); + bx_slowdown_timer.reset(type); +#if BX_IODEBUG_SUPPORT + iodebug->reset(type); +#endif +#if BX_NE2K_SUPPORT + if (pluginNE2kDevice) pluginNE2kDevice->reset(type); +#endif + + bx_reset_plugins(type); +} + + + Bit32u +bx_devices_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_DEV_SMF + bx_devices_c *class_ptr = (bx_devices_c *) this_ptr; + + return( class_ptr->port92_read(address, io_len) ); +} + + + Bit32u +bx_devices_c::port92_read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_DEV_SMF + + BX_DEBUG(("port92h read partially supported!!!")); + BX_DEBUG((" returning %02x", (unsigned) (BX_GET_ENABLE_A20() << 1))); + return(BX_GET_ENABLE_A20() << 1); +} + + + void +bx_devices_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_DEV_SMF + bx_devices_c *class_ptr = (bx_devices_c *) this_ptr; + + class_ptr->port92_write(address, value, io_len); +} + + void +bx_devices_c::port92_write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_DEV_SMF + bx_bool bx_cpu_reset; + + BX_DEBUG(("port92h write of %02x partially supported!!!", + (unsigned) value)); + BX_DEBUG(("A20: set_enable_a20() called")); + BX_SET_ENABLE_A20( (value & 0x02) >> 1 ); + BX_DEBUG(("A20: now %u", (unsigned) BX_GET_ENABLE_A20())); + bx_cpu_reset = (value & 0x01); /* high speed reset */ + if (bx_cpu_reset) { + BX_PANIC(("PORT 92h write: CPU reset requested!")); + } +} + + +// This defines a no-default read handler, +// so Bochs does not segfault if unmapped is not loaded + Bit32u +bx_devices_c::default_read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ + UNUSED(this_ptr); + BX_PANIC(("No default io-read handler found for 0x%04x/%d. Unmapped io-device not loaded ?", address, io_len)); + return 0xffffffff; +} + +// This defines a no-default write handler, +// so Bochs does not segfault if unmapped is not loaded + void +bx_devices_c::default_write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ + UNUSED(this_ptr); + BX_PANIC(("No default io-write handler found for 0x%04x/%d. Unmapped io-device not loaded ?", address, io_len)); +} + + void +bx_devices_c::timer_handler(void *this_ptr) +{ + bx_devices_c *class_ptr = (bx_devices_c *) this_ptr; + + class_ptr->timer(); +} + + void +bx_devices_c::timer() +{ +#if (BX_USE_NEW_PIT==0) + if ( pit->periodic( BX_IODEV_HANDLER_PERIOD ) ) { + // This is a hack to make the IRQ0 work + DEV_pic_lower_irq(0); + DEV_pic_raise_irq(0); + } +#endif + + + // separate calls to bx_gui->handle_events from the keyboard code. + { + static int multiple=0; + if ( ++multiple==10) + { + multiple=0; + SIM->periodic (); + if (!BX_CPU(0)->kill_bochs_request) + bx_gui->handle_events(); + } + } + +// KPL Removed lapic periodic timer registration here. +} + + + bx_bool +bx_devices_c::register_irq(unsigned irq, const char *name) +{ + if (irq >= BX_MAX_IRQS) { + BX_PANIC(("IO device %s registered with IRQ=%d above %u", + name, irq, (unsigned) BX_MAX_IRQS-1)); + return false; + } + if (irq_handler_name[irq]) { + BX_PANIC(("IRQ %u conflict, %s with %s", irq, + irq_handler_name[irq], name)); + return false; + } + irq_handler_name[irq] = name; + return true; +} + + bx_bool +bx_devices_c::unregister_irq(unsigned irq, const char *name) +{ + if (irq >= BX_MAX_IRQS) { + BX_PANIC(("IO device %s tried to unregister IRQ %d above %u", + name, irq, (unsigned) BX_MAX_IRQS-1)); + return false; + } + + if (!irq_handler_name[irq]) { + BX_INFO(("IO device %s tried to unregister IRQ %d, not registered", + name, irq)); + return false; + } + + if (strcmp(irq_handler_name[irq], name)) { + BX_INFO(("IRQ %u not registered to %s but to %s", irq, + name, irq_handler_name[irq])); + return false; + } + irq_handler_name[irq] = NULL; + return true; +} + + bx_bool +bx_devices_c::register_io_read_handler( void *this_ptr, bx_read_handler_t f, + Bit32u addr, const char *name, Bit8u mask ) +{ + unsigned handle; + + addr &= 0x0000ffff; + + /* first find existing handle for function or create new one */ + for (handle=0; handle < num_read_handles; handle++) { + if ((io_read_handler[handle].funct == f) && + (io_read_handler[handle].mask == mask)) break; + } + + if (handle >= num_read_handles) { + /* no existing handle found, create new one */ + if (num_read_handles >= BX_DEFAULT_IO_DEVICE) { + BX_INFO(("too many IO devices installed.")); + BX_PANIC((" try increasing BX_MAX_IO_DEVICES")); + } + num_read_handles++; + io_read_handler[handle].funct = f; + io_read_handler[handle].this_ptr = this_ptr; + io_read_handler[handle].handler_name = name; + io_read_handler[handle].mask = mask; + } + + /* change table to reflect new handler id for that address */ + if (read_handler_id[addr] < BX_DEFAULT_IO_DEVICE) { + // another handler is already registered for that address + BX_ERROR(("IO device address conflict(read) at IO address %Xh", + (unsigned) addr)); + BX_ERROR((" conflicting devices: %s & %s", + io_read_handler[handle].handler_name, io_read_handler[read_handler_id[addr]].handler_name)); + return false; // address not available, return false. + } + read_handler_id[addr] = handle; + return true; // address mapped successfully +} + + + + bx_bool +bx_devices_c::register_io_write_handler( void *this_ptr, bx_write_handler_t f, + Bit32u addr, const char *name, Bit8u mask ) +{ + unsigned handle; + + addr &= 0x0000ffff; + + /* first find existing handle for function or create new one */ + for (handle=0; handle < num_write_handles; handle++) { + if ((io_write_handler[handle].funct == f) && + (io_write_handler[handle].mask == mask)) break; + } + + if (handle >= num_write_handles) { + /* no existing handle found, create new one */ + if (num_write_handles >= BX_DEFAULT_IO_DEVICE) { + BX_INFO(("too many IO devices installed.")); + BX_PANIC((" try increasing BX_MAX_IO_DEVICES")); + } + num_write_handles++; + io_write_handler[handle].funct = f; + io_write_handler[handle].this_ptr = this_ptr; + io_write_handler[handle].handler_name = name; + io_write_handler[handle].mask = mask; + } + + /* change table to reflect new handler id for that address */ + if (write_handler_id[addr] < BX_DEFAULT_IO_DEVICE) { + // another handler is already registered for that address + BX_ERROR(("IO device address conflict(write) at IO address %Xh", + (unsigned) addr)); + BX_ERROR((" conflicting devices: %s & %s", + io_write_handler[handle].handler_name, io_write_handler[write_handler_id[addr]].handler_name)); + return false; //unable to map iodevice. + } + write_handler_id[addr] = handle; + return true; // done! +} + + +// Registration of default handlers (mainly be the unmapped device) +// The trick here is to define a handler for the max index, so +// unregisterd io address will get handled by the default function +// This will be helpful when we want to unregister io handlers + + bx_bool +bx_devices_c::register_default_io_read_handler( void *this_ptr, bx_read_handler_t f, + const char *name, Bit8u mask ) +{ + unsigned handle; + + /* handle is fixed to the default I/O device */ + handle = BX_DEFAULT_IO_DEVICE; + + if (strcmp(io_read_handler[handle].handler_name, "Default")) { + BX_ERROR(("Default io read handler already registered '%s'",io_read_handler[handle].handler_name)); + return false; + } + + io_read_handler[handle].funct = f; + io_read_handler[handle].this_ptr = this_ptr; + io_read_handler[handle].handler_name = name; + io_read_handler[handle].mask = mask; + + return true; +} + + + + bx_bool +bx_devices_c::register_default_io_write_handler( void *this_ptr, bx_write_handler_t f, + const char *name, Bit8u mask ) +{ + unsigned handle; + + /* handle is fixed to the MAX */ + handle = BX_DEFAULT_IO_DEVICE; + + if (strcmp(io_write_handler[handle].handler_name, "Default")) { + BX_ERROR(("Default io write handler already registered '%s'",io_write_handler[handle].handler_name)); + return false; + } + + io_write_handler[handle].funct = f; + io_write_handler[handle].this_ptr = this_ptr; + io_write_handler[handle].handler_name = name; + io_write_handler[handle].mask = mask; + + return true; +} + + + +/* + * Read a byte of data from the IO memory address space + */ + + Bit32u BX_CPP_AttrRegparmN(2) +bx_devices_c::inp(Bit16u addr, unsigned io_len) +{ + Bit8u handle; + Bit32u ret; + + BX_INSTR_INP(addr, io_len); + + handle = read_handler_id[addr]; + if ((io_read_handler[handle].funct != NULL) && + (io_read_handler[handle].mask & io_len)) { + ret = (* io_read_handler[handle].funct)(io_read_handler[handle].this_ptr, + (Bit32u) addr, io_len); + } else { + switch (io_len) { + case 1: ret = 0xff; break; + case 2: ret = 0xffff; break; + default: ret = 0xffffffff; break; + } + BX_ERROR(("read from port 0x%04x with len %d returns 0x%x", addr, io_len, ret)); + } + BX_INSTR_INP2(addr, io_len, ret); + BX_DBG_IO_REPORT(addr, io_len, BX_READ, ret); + + return(ret); +} + + +/* + * Write a byte of data to the IO memory address space. + */ + + void BX_CPP_AttrRegparmN(3) +bx_devices_c::outp(Bit16u addr, Bit32u value, unsigned io_len) +{ + Bit8u handle; + + BX_INSTR_OUTP(addr, io_len); + BX_INSTR_OUTP2(addr, io_len, value); + + BX_DBG_IO_REPORT(addr, io_len, BX_WRITE, value); + handle = write_handler_id[addr]; + if ((io_write_handler[handle].funct != NULL) && + (io_write_handler[handle].mask & io_len)) { + (* io_write_handler[handle].funct)(io_write_handler[handle].this_ptr, + (Bit32u) addr, value, io_len); + } else { + BX_ERROR(("write to port 0x%04x with len %d ignored", addr, io_len)); + } +} + +bx_bool bx_devices_c::is_serial_enabled () +{ + for (int i=0; i<BX_N_SERIAL_PORTS; i++) { + if (SIM->get_param_bool (BXP_COMx_ENABLED(i+1))->get()) + return true; + } + return false; +} + +bx_bool bx_devices_c::is_usb_enabled () +{ + for (int i=0; i<BX_N_USB_HUBS; i++) { + if (SIM->get_param_bool (BXP_USBx_ENABLED(i+1))->get()) + return true; + } + return false; +} + +bx_bool bx_devices_c::is_parallel_enabled () +{ + for (int i=0; i<BX_N_PARALLEL_PORTS; i++) { + if (SIM->get_param_bool (BXP_PARPORTx_ENABLED(i+1))->get()) + return true; + } + return false; +} diff --git a/tools/ioemu/iodev/dma.cc b/tools/ioemu/iodev/dma.cc new file mode 100644 index 0000000000..afe0238a92 --- /dev/null +++ b/tools/ioemu/iodev/dma.cc @@ -0,0 +1,825 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: dma.cc,v 1.30 2003/07/31 15:29:34 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" + +#define LOG_THIS theDmaDevice-> + +#define DMA_MODE_DEMAND 0 +#define DMA_MODE_SINGLE 1 +#define DMA_MODE_BLOCK 2 +#define DMA_MODE_CASCADE 3 + +bx_dma_c *theDmaDevice = NULL; + + int +libdma_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + theDmaDevice = new bx_dma_c (); + bx_devices.pluginDmaDevice = theDmaDevice; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theDmaDevice, BX_PLUGIN_DMA); + return(0); // Success +} + + void +libdma_LTX_plugin_fini(void) +{ +} + +bx_dma_c::bx_dma_c(void) +{ + put("DMA"); + settype(DMALOG); +} + +bx_dma_c::~bx_dma_c(void) +{ + BX_DEBUG(("Exit.")); +} + + unsigned +bx_dma_c::registerDMA8Channel( + unsigned channel, + void (* dmaRead)(Bit8u *data_byte), + void (* dmaWrite)(Bit8u *data_byte), + const char *name + ) +{ + if (channel > 3) { + BX_PANIC(("registerDMA8Channel: invalid channel number(%u).", channel)); + return 0; // Fail. + } + if (BX_DMA_THIS s[0].chan[channel].used) { + BX_PANIC(("registerDMA8Channel: channel(%u) already in use.", channel)); + return 0; // Fail. + } + BX_INFO(("channel %u used by %s", channel, name)); + BX_DMA_THIS h[channel].dmaRead8 = dmaRead; + BX_DMA_THIS h[channel].dmaWrite8 = dmaWrite; + BX_DMA_THIS s[0].chan[channel].used = 1; + return 1; // OK. +} + + unsigned +bx_dma_c::registerDMA16Channel( + unsigned channel, + void (* dmaRead)(Bit16u *data_word), + void (* dmaWrite)(Bit16u *data_word), + const char *name + ) +{ + if ((channel < 4) || (channel > 7)) { + BX_PANIC(("registerDMA16Channel: invalid channel number(%u).", channel)); + return 0; // Fail. + } + if (BX_DMA_THIS s[1].chan[channel & 0x03].used) { + BX_PANIC(("registerDMA16Channel: channel(%u) already in use.", channel)); + return 0; // Fail. + } + BX_INFO(("channel %u used by %s", channel, name)); + channel &= 0x03; + BX_DMA_THIS h[channel].dmaRead16 = dmaRead; + BX_DMA_THIS h[channel].dmaWrite16 = dmaWrite; + BX_DMA_THIS s[1].chan[channel].used = 1; + return 1; // OK. +} + + unsigned +bx_dma_c::unregisterDMAChannel(unsigned channel) +{ + bx_bool ma_sl = (channel > 3); + BX_DMA_THIS s[ma_sl].chan[channel & 0x03].used = 0; + BX_INFO(("channel %u no longer used", channel)); + return 1; +} + + unsigned +bx_dma_c::get_TC(void) +{ + return BX_DMA_THIS TC; +} + + + void +bx_dma_c::init(void) +{ + unsigned c, i, j; + BX_DEBUG(("Init $Id: dma.cc,v 1.30 2003/07/31 15:29:34 vruppert Exp $")); + + /* 8237 DMA controller */ + + for (i=0; i < 2; i++) { + for (j=0; j < 4; j++) { + BX_DMA_THIS s[i].DRQ[j] = 0; + BX_DMA_THIS s[i].DACK[j] = 0; + } + } + BX_DMA_THIS HLDA = 0; + BX_DMA_THIS TC = 0; + + // 0000..000F + for (i=0x0000; i<=0x000F; i++) { + DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1); + DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3); + } + + // 00081..008F + for (i=0x0081; i<=0x008F; i++) { + DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1); + DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3); + } + + // 000C0..00DE + for (i=0x00C0; i<=0x00DE; i+=2) { + DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1); + DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3); + } + + + for (i=0; i<2; i++) { + for (c=0; c<4; c++) { + BX_DMA_THIS s[i].chan[c].mode.mode_type = 0; // demand mode + BX_DMA_THIS s[i].chan[c].mode.address_decrement = 0; // address increment + BX_DMA_THIS s[i].chan[c].mode.autoinit_enable = 0; // autoinit disable + BX_DMA_THIS s[i].chan[c].mode.transfer_type = 0; // verify + BX_DMA_THIS s[i].chan[c].base_address = 0; + BX_DMA_THIS s[i].chan[c].current_address = 0; + BX_DMA_THIS s[i].chan[c].base_count = 0; + BX_DMA_THIS s[i].chan[c].current_count = 0; + BX_DMA_THIS s[i].chan[c].page_reg = 0; + BX_DMA_THIS s[i].chan[c].used = 0; + } + } + BX_DMA_THIS s[1].chan[0].used = 1; // cascade channel in use + BX_INFO(("channel 4 used by cascade")); + bios_init(); +} + +/* Remove it when guest fw ready*/ + void +bx_dma_c::bios_init(void){ + BX_DMA_THIS s[1].mask[0] = 0; // unmask cascade channel + BX_DMA_THIS s[1].chan[0].mode.mode_type = 3; // cascade mode for channel 4 +} + + void +bx_dma_c::reset(unsigned type) +{ + reset_controller(0); + reset_controller(1); + bios_init(); +} + + void +bx_dma_c::reset_controller(unsigned num) +{ + BX_DMA_THIS s[num].mask[0] = 1; + BX_DMA_THIS s[num].mask[1] = 1; + BX_DMA_THIS s[num].mask[2] = 1; + BX_DMA_THIS s[num].mask[3] = 1; + BX_DMA_THIS s[num].command_reg = 0; + BX_DMA_THIS s[num].status_reg = 0; + BX_DMA_THIS s[num].request_reg = 0; + BX_DMA_THIS s[num].temporary_reg = 0; + BX_DMA_THIS s[num].flip_flop = 0; +} + + // index to find channel from register number (only [0],[1],[2],[6] used) + Bit8u channelindex[7] = {2, 3, 1, 0, 0, 0, 0}; + + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_dma_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_DMA_SMF + bx_dma_c *class_ptr = (bx_dma_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + /* 8237 DMA controller */ + Bit32u BX_CPP_AttrRegparmN(2) +bx_dma_c::read( Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_DMA_SMF + + Bit8u retval; + Bit8u channel; + bx_bool ma_sl; + + BX_DEBUG(("read addr=%04x", (unsigned) address)); + +#if BX_DMA_FLOPPY_IO < 1 + /* if we're not supporting DMA/floppy IO just return a bogus value */ + return(0xff); +#endif + + switch (address) { + case 0x00: /* DMA-1 current address, channel 0 */ + case 0x02: /* DMA-1 current address, channel 1 */ + case 0x04: /* DMA-1 current address, channel 2 */ + case 0x06: /* DMA-1 current address, channel 3 */ + case 0xc0: /* DMA-2 current address, channel 0 */ + case 0xc4: /* DMA-2 current address, channel 1 */ + case 0xc8: /* DMA-2 current address, channel 2 */ + case 0xcc: /* DMA-2 current address, channel 3 */ + ma_sl = (address >= 0xc0); + channel = (address >> (1 + ma_sl)) & 0x03; + if (BX_DMA_THIS s[ma_sl].flip_flop==0) { + BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop; + return(BX_DMA_THIS s[ma_sl].chan[channel].current_address & 0xff); + } + else { + BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop; + return(BX_DMA_THIS s[ma_sl].chan[channel].current_address >> 8); + } + + case 0x01: /* DMA-1 current count, channel 0 */ + case 0x03: /* DMA-1 current count, channel 1 */ + case 0x05: /* DMA-1 current count, channel 2 */ + case 0x07: /* DMA-1 current count, channel 3 */ + case 0xc2: /* DMA-2 current count, channel 0 */ + case 0xc6: /* DMA-2 current count, channel 1 */ + case 0xca: /* DMA-2 current count, channel 2 */ + case 0xce: /* DMA-2 current count, channel 3 */ + ma_sl = (address >= 0xc2); + channel = (address >> (1 + ma_sl)) & 0x03; + if (BX_DMA_THIS s[ma_sl].flip_flop==0) { + BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop; + return(BX_DMA_THIS s[ma_sl].chan[channel].current_count & 0xff); + } + else { + BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop; + return(BX_DMA_THIS s[ma_sl].chan[channel].current_count >> 8); + } + + case 0x08: // DMA-1 Status Register + case 0xd0: // DMA-2 Status Register + // bit 7: 1 = channel 3 request + // bit 6: 1 = channel 2 request + // bit 5: 1 = channel 1 request + // bit 4: 1 = channel 0 request + // bit 3: 1 = channel 3 has reached terminal count + // bit 2: 1 = channel 2 has reached terminal count + // bit 1: 1 = channel 1 has reached terminal count + // bit 0: 1 = channel 0 has reached terminal count + // reading this register clears lower 4 bits (hold flags) + ma_sl = (address == 0xd0); + retval = BX_DMA_THIS s[ma_sl].status_reg; + BX_DMA_THIS s[ma_sl].status_reg &= 0xf0; + return(retval); + break; + case 0x0d: // DMA-1: temporary register + case 0xda: // DMA-2: temporary register + ma_sl = (address == 0xda); + BX_ERROR(("DMA-%d: read of temporary register", ma_sl+1)); + // Note: write to 0x0D clears temporary register + return(0); + break; + + case 0x0081: // DMA-1 page register, channel 2 + case 0x0082: // DMA-1 page register, channel 3 + case 0x0083: // DMA-1 page register, channel 1 + case 0x0087: // DMA-1 page register, channel 0 + channel = channelindex[address - 0x81]; + return( BX_DMA_THIS s[0].chan[channel].page_reg ); + + case 0x0089: // DMA-2 page register, channel 2 + case 0x008a: // DMA-2 page register, channel 3 + case 0x008b: // DMA-2 page register, channel 1 + case 0x008f: // DMA-2 page register, channel 0 + channel = channelindex[address - 0x89]; + return( BX_DMA_THIS s[1].chan[channel].page_reg ); + + case 0x0084: + case 0x0085: + case 0x0086: + case 0x0088: + case 0x008c: + case 0x008d: + case 0x008e: + BX_DEBUG(("read: extra page register 0x%04x unsupported", (unsigned) address)); + return(0); + + default: + BX_ERROR(("read: unsupported address=%04x", (unsigned) address)); + return(0); + } +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_dma_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_DMA_SMF + bx_dma_c *class_ptr = (bx_dma_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + + /* 8237 DMA controller */ + void BX_CPP_AttrRegparmN(3) +bx_dma_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_DMA_SMF + Bit8u set_mask_bit; + Bit8u channel; + bx_bool ma_sl; + + if (io_len > 1) { + if ( (io_len == 2) && (address == 0x0b) ) { +#if BX_USE_DMA_SMF + BX_DMA_THIS write_handler(NULL, address, value & 0xff, 1); + BX_DMA_THIS write_handler(NULL, address+1, value >> 8, 1); +#else + BX_DMA_THIS write(address, value & 0xff, 1); + BX_DMA_THIS write(address+1, value >> 8, 1); +#endif + return; + } + + BX_ERROR(("io write to address %08x, len=%u", + (unsigned) address, (unsigned) io_len)); + return; + } + + BX_DEBUG(("write: address=%04x value=%02x", + (unsigned) address, (unsigned) value)); + +#if BX_DMA_FLOPPY_IO < 1 + /* if we're not supporting DMA/floppy IO just return */ + return; +#endif + + switch (address) { + case 0x00: + case 0x02: + case 0x04: + case 0x06: + case 0xc0: + case 0xc4: + case 0xc8: + case 0xcc: + ma_sl = (address >= 0xc0); + channel = (address >> (1 + ma_sl)) & 0x03; + BX_DEBUG((" DMA-%d base and current address, channel %d", ma_sl+1, channel)); + if (BX_DMA_THIS s[ma_sl].flip_flop==0) { /* 1st byte */ + BX_DMA_THIS s[ma_sl].chan[channel].base_address = value; + BX_DMA_THIS s[ma_sl].chan[channel].current_address = value; + } + else { /* 2nd byte */ + BX_DMA_THIS s[ma_sl].chan[channel].base_address |= (value << 8); + BX_DMA_THIS s[ma_sl].chan[channel].current_address |= (value << 8); + BX_DEBUG((" base = %04x", + (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].base_address)); + BX_DEBUG((" curr = %04x", + (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].current_address)); + } + BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop; + return; + break; + + case 0x01: + case 0x03: + case 0x05: + case 0x07: + case 0xc2: + case 0xc6: + case 0xca: + case 0xce: + ma_sl = (address >= 0xc2); + channel = (address >> (1 + ma_sl)) & 0x03; + BX_DEBUG((" DMA-%d base and current count, channel %d", ma_sl+1, channel)); + if (BX_DMA_THIS s[ma_sl].flip_flop==0) { /* 1st byte */ + BX_DMA_THIS s[ma_sl].chan[channel].base_count = value; + BX_DMA_THIS s[ma_sl].chan[channel].current_count = value; + } + else { /* 2nd byte */ + BX_DMA_THIS s[ma_sl].chan[channel].base_count |= (value << 8); + BX_DMA_THIS s[ma_sl].chan[channel].current_count |= (value << 8); + BX_DEBUG((" base = %04x", + (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].base_count)); + BX_DEBUG((" curr = %04x", + (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].current_count)); + } + BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop; + return; + break; + + case 0x08: /* DMA-1: command register */ + case 0xd0: /* DMA-2: command register */ + ma_sl = (address == 0xd0); + if (value != 0x00) + BX_ERROR(("write to command register: value(%02xh) not 0x00", + (unsigned) value)); + BX_DMA_THIS s[ma_sl].command_reg = value; + return; + break; + + case 0x09: // DMA-1: request register + case 0xd2: // DMA-2: request register + ma_sl = (address == 0xd2); + channel = value & 0x03; + BX_ERROR(("DMA-%d: write to request register (%02x)", ma_sl+1, (unsigned) value)); + // note: write to 0x0d clears this register + if (value & 0x04) { + // set request bit + BX_DMA_THIS s[ma_sl].status_reg |= (1 << (channel+4)); + BX_DEBUG(("DMA-%d: set request bit for channel %u", ma_sl+1, (unsigned) channel)); + } + else { + // clear request bit + BX_DMA_THIS s[ma_sl].status_reg &= ~(1 << (channel+4)); + BX_DEBUG(("DMA-%d: cleared request bit for channel %u", ma_sl+1, (unsigned) channel)); + } + control_HRQ(ma_sl); + return; + break; + + case 0x0a: + case 0xd4: + ma_sl = (address == 0xd4); + set_mask_bit = value & 0x04; + channel = value & 0x03; + BX_DMA_THIS s[ma_sl].mask[channel] = (set_mask_bit > 0); + BX_DEBUG(("DMA-%d: set_mask_bit=%u, channel=%u, mask now=%02xh", ma_sl+1, + (unsigned) set_mask_bit, (unsigned) channel, (unsigned) BX_DMA_THIS s[ma_sl].mask[channel])); + control_HRQ(ma_sl); + return; + break; + + case 0x0b: /* DMA-1 mode register */ + case 0xd6: /* DMA-2 mode register */ + ma_sl = (address == 0xd6); + channel = value & 0x03; + BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type = (value >> 6) & 0x03; + BX_DMA_THIS s[ma_sl].chan[channel].mode.address_decrement = (value >> 5) & 0x01; + BX_DMA_THIS s[ma_sl].chan[channel].mode.autoinit_enable = (value >> 4) & 0x01; + BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type = (value >> 2) & 0x03; + BX_DEBUG(("DMA-%d: mode register[%u] = %02x", ma_sl+1, + (unsigned) channel, (unsigned) value)); + return; + break; + + case 0x0c: /* DMA-1 clear byte flip/flop */ + case 0xd8: /* DMA-2 clear byte flip/flop */ + ma_sl = (address == 0xd8); + BX_DEBUG(("DMA-%d: clear flip/flop", ma_sl+1)); + BX_DMA_THIS s[ma_sl].flip_flop = 0; + return; + break; + + case 0x0d: // DMA-1: master clear + case 0xda: // DMA-2: master clear + ma_sl = (address == 0xda); + BX_DEBUG(("DMA-%d: master clear", ma_sl+1)); + // writing any value to this port resets DMA controller 1 / 2 + // same action as a hardware reset + // mask register is set (chan 0..3 disabled) + // command, status, request, temporary, and byte flip-flop are all cleared + reset_controller(ma_sl); + return; + break; + + case 0x0e: // DMA-1: clear mask register + case 0xdc: // DMA-2: clear mask register + ma_sl = (address == 0xdc); + BX_DEBUG(("DMA-%d: clear mask register", ma_sl+1)); + BX_DMA_THIS s[ma_sl].mask[0] = 0; + BX_DMA_THIS s[ma_sl].mask[1] = 0; + BX_DMA_THIS s[ma_sl].mask[2] = 0; + BX_DMA_THIS s[ma_sl].mask[3] = 0; + control_HRQ(ma_sl); + return; + break; + + case 0x0f: // DMA-1: write all mask bits + case 0xde: // DMA-2: write all mask bits + ma_sl = (address == 0xde); + BX_DEBUG(("DMA-%d: write all mask bits", ma_sl+1)); + BX_DMA_THIS s[ma_sl].mask[0] = value & 0x01; value >>= 1; + BX_DMA_THIS s[ma_sl].mask[1] = value & 0x01; value >>= 1; + BX_DMA_THIS s[ma_sl].mask[2] = value & 0x01; value >>= 1; + BX_DMA_THIS s[ma_sl].mask[3] = value & 0x01; + control_HRQ(ma_sl); + return; + break; + + case 0x81: /* DMA-1 page register, channel 2 */ + case 0x82: /* DMA-1 page register, channel 3 */ + case 0x83: /* DMA-1 page register, channel 1 */ + case 0x87: /* DMA-1 page register, channel 0 */ + /* address bits A16-A23 for DMA channel */ + channel = channelindex[address - 0x81]; + BX_DMA_THIS s[0].chan[channel].page_reg = value; + BX_DEBUG(("DMA-1: page register %d = %02x", channel, (unsigned) value)); + return; + break; + + case 0x89: /* DMA-2 page register, channel 2 */ + case 0x8a: /* DMA-2 page register, channel 3 */ + case 0x8b: /* DMA-2 page register, channel 1 */ + case 0x8f: /* DMA-2 page register, channel 0 */ + /* address bits A16-A23 for DMA channel */ + channel = channelindex[address - 0x89]; + BX_DMA_THIS s[1].chan[channel].page_reg = value; + BX_DEBUG(("DMA-2: page register %d = %02x", channel + 4, (unsigned) value)); + return; + break; + + case 0x0084: + case 0x0085: + case 0x0086: + case 0x0088: + case 0x008c: + case 0x008d: + case 0x008e: + BX_DEBUG(("write: extra page register 0x%04x unsupported", (unsigned) address)); + return; + break; + + default: + BX_ERROR(("write ignored: %04xh = %02xh", + (unsigned) address, (unsigned) value)); + } +} + + void +bx_dma_c::set_DRQ(unsigned channel, bx_bool val) +{ + Bit32u dma_base, dma_roof; + bx_bool ma_sl; + + if (channel > 7) { + BX_PANIC(("set_DRQ() channel > 7")); + return; + } + ma_sl = (channel > 3); + BX_DMA_THIS s[ma_sl].DRQ[channel & 0x03] = val; + if (!BX_DMA_THIS s[ma_sl].chan[channel & 0x03].used) { + BX_PANIC(("set_DRQ(): channel %d not connected to device", channel)); + return; + } + channel &= 0x03; + if (!val) { + //BX_DEBUG(("bx_dma_c::DRQ(): val == 0")); + // clear bit in status reg + BX_DMA_THIS s[ma_sl].status_reg &= ~(1 << (channel+4)); + + control_HRQ(ma_sl); + return; + } + +#if 0 + BX_INFO(("mask[%d]: %02x", channel, (unsigned) BX_DMA_THIS s[0].mask[channel])); + BX_INFO(("flip_flop: %u", (unsigned) BX_DMA_THIS s[0].flip_flop)); + BX_INFO(("status_reg: %02x", (unsigned) BX_DMA_THIS s[0].status_reg)); + BX_INFO(("mode_type: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].mode.mode_type)); + BX_INFO(("address_decrement: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].mode.address_decrement)); + BX_INFO(("autoinit_enable: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].mode.autoinit_enable)); + BX_INFO(("transfer_type: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].mode.transfer_type)); + BX_INFO(("base_address: %04x", (unsigned) BX_DMA_THIS s[0].chan[channel].base_address)); + BX_INFO(("current_address: %04x", (unsigned) BX_DMA_THIS s[0].chan[channel].current_address)); + BX_INFO(("base_count: %04x", (unsigned) BX_DMA_THIS s[0].chan[channel].base_count)); + BX_INFO(("current_count: %04x", (unsigned) BX_DMA_THIS s[0].chan[channel].current_count)); + BX_INFO(("page_reg: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].page_reg)); +#endif + + BX_DMA_THIS s[ma_sl].status_reg |= (1 << (channel+4)); + + if ( (BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type != DMA_MODE_SINGLE) && + (BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type != DMA_MODE_DEMAND) && + (BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type != DMA_MODE_CASCADE) ) + BX_PANIC(("set_DRQ: mode_type(%02x) not handled", + (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type)); + + dma_base = (BX_DMA_THIS s[ma_sl].chan[channel].page_reg << 16) | + (BX_DMA_THIS s[ma_sl].chan[channel].base_address << ma_sl); + if (BX_DMA_THIS s[ma_sl].chan[channel].mode.address_decrement==0) { + dma_roof = dma_base + (BX_DMA_THIS s[ma_sl].chan[channel].base_count << ma_sl); + } else { + dma_roof = dma_base - (BX_DMA_THIS s[ma_sl].chan[channel].base_count << ma_sl); + } + if ( (dma_base & (0x7fff0000 << ma_sl)) != (dma_roof & (0x7fff0000 << ma_sl)) ) { + BX_INFO(("dma_base = %08x", (unsigned) dma_base)); + BX_INFO(("dma_base_count = %08x", (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].base_count)); + BX_INFO(("dma_roof = %08x", (unsigned) dma_roof)); + BX_PANIC(("request outside %dk boundary", 64 << ma_sl)); + } + + control_HRQ(ma_sl); +} + + void +bx_dma_c::control_HRQ(bx_bool ma_sl) +{ + unsigned channel; + + // deassert HRQ if no DRQ is pending + if ((BX_DMA_THIS s[ma_sl].status_reg & 0xf0) == 0) { + if (ma_sl) { + bx_pc_system.set_HRQ(0); + } else { + BX_DMA_THIS set_DRQ(4, 0); + } + return; + } + // find highest priority channel + for (channel=0; channel<4; channel++) { + if ( (BX_DMA_THIS s[ma_sl].status_reg & (1 << (channel+4))) && + (BX_DMA_THIS s[ma_sl].mask[channel]==0) ) { + if (ma_sl) { + // assert Hold ReQuest line to CPU + bx_pc_system.set_HRQ(1); + } else { + // send DRQ to cascade channel of the master + BX_DMA_THIS set_DRQ(4, 1); + } + break; + } + } +} + + void +bx_dma_c::raise_HLDA(void) +{ + unsigned channel; + Bit32u phy_addr; + bx_bool count_expired = 0; + bx_bool ma_sl = 0; + + BX_DMA_THIS HLDA = 1; + // find highest priority channel + for (channel=0; channel<4; channel++) { + if ( (BX_DMA_THIS s[1].status_reg & (1 << (channel+4))) && + (BX_DMA_THIS s[1].mask[channel]==0) ) { + ma_sl = 1; + break; + } + } + if (channel == 0) { // master cascade channel + BX_DMA_THIS s[1].DACK[0] = 1; + for (channel=0; channel<4; channel++) { + if ( (BX_DMA_THIS s[0].status_reg & (1 << (channel+4))) && + (BX_DMA_THIS s[0].mask[channel]==0) ) { + ma_sl = 0; + break; + } + } + } + if (channel >= 4) { + // wait till they're unmasked + return; + } + + //BX_DEBUG(("hlda: OK in response to DRQ(%u)", (unsigned) channel)); + phy_addr = (BX_DMA_THIS s[ma_sl].chan[channel].page_reg << 16) | + (BX_DMA_THIS s[ma_sl].chan[channel].current_address << ma_sl); + + BX_DMA_THIS s[ma_sl].DACK[channel] = 1; + // check for expiration of count, so we can signal TC and DACK(n) + // at the same time. + if (BX_DMA_THIS s[ma_sl].chan[channel].mode.address_decrement==0) + BX_DMA_THIS s[ma_sl].chan[channel].current_address++; + else + BX_DMA_THIS s[ma_sl].chan[channel].current_address--; + BX_DMA_THIS s[ma_sl].chan[channel].current_count--; + if (BX_DMA_THIS s[ma_sl].chan[channel].current_count == 0xffff) { + // count expired, done with transfer + // assert TC, deassert HRQ & DACK(n) lines + BX_DMA_THIS s[ma_sl].status_reg |= (1 << channel); // hold TC in status reg + BX_DMA_THIS TC = 1; + count_expired = 1; + if (BX_DMA_THIS s[ma_sl].chan[channel].mode.autoinit_enable == 0) { + // set mask bit if not in autoinit mode + BX_DMA_THIS s[ma_sl].mask[channel] = 1; + } + else { + // count expired, but in autoinit mode + // reload count and base address + BX_DMA_THIS s[ma_sl].chan[channel].current_address = + BX_DMA_THIS s[ma_sl].chan[channel].base_address; + BX_DMA_THIS s[ma_sl].chan[channel].current_count = + BX_DMA_THIS s[ma_sl].chan[channel].base_count; + } + } + + Bit8u data_byte; + Bit16u data_word; + + if (BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type == 1) { // write + // DMA controlled xfer of byte from I/O to Memory + + if (!ma_sl) { + if (BX_DMA_THIS h[channel].dmaWrite8) + BX_DMA_THIS h[channel].dmaWrite8(&data_byte); + else + BX_PANIC(("no dmaWrite handler for channel %u.", channel)); + + BX_MEM_WRITE_PHYSICAL(phy_addr, 1, &data_byte); + + BX_DBG_DMA_REPORT(phy_addr, 1, BX_WRITE, data_byte); + } + else { + if (BX_DMA_THIS h[channel].dmaWrite16) + BX_DMA_THIS h[channel].dmaWrite16(&data_word); + else + BX_PANIC(("no dmaWrite handler for channel %u.", channel)); + + BX_MEM_WRITE_PHYSICAL(phy_addr, 2, &data_word); + + BX_DBG_DMA_REPORT(phy_addr, 2, BX_WRITE, data_word); + } + } + else if (BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type == 2) { // read + // DMA controlled xfer of byte from Memory to I/O + + if (!ma_sl) { + BX_MEM_READ_PHYSICAL(phy_addr, 1, &data_byte); + + if (BX_DMA_THIS h[channel].dmaRead8) + BX_DMA_THIS h[channel].dmaRead8(&data_byte); + + BX_DBG_DMA_REPORT(phy_addr, 1, BX_READ, data_byte); + } + else { + BX_MEM_READ_PHYSICAL(phy_addr, 2, &data_word); + + if (BX_DMA_THIS h[channel].dmaRead16) + BX_DMA_THIS h[channel].dmaRead16(&data_word); + + BX_DBG_DMA_REPORT(phy_addr, 2, BX_READ, data_word); + } + } + else if (BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type == 0) { + // verify + + if (!ma_sl) { + if (BX_DMA_THIS h[channel].dmaWrite8) + BX_DMA_THIS h[channel].dmaWrite8(&data_byte); + else + BX_PANIC(("no dmaWrite handler for channel %u.", channel)); + } + else { + if (BX_DMA_THIS h[channel].dmaWrite16) + BX_DMA_THIS h[channel].dmaWrite16(&data_word); + else + BX_PANIC(("no dmaWrite handler for channel %u.", channel)); + } + } + else { + BX_PANIC(("hlda: transfer_type 3 is undefined")); + } + + if (count_expired) { + BX_DMA_THIS TC = 0; // clear TC, adapter card already notified + BX_DMA_THIS HLDA = 0; + bx_pc_system.set_HRQ(0); // clear HRQ to CPU + BX_DMA_THIS s[ma_sl].DACK[channel] = 0; // clear DACK to adapter card + if (!ma_sl) { + BX_DMA_THIS set_DRQ(4, 0); // clear DRQ to cascade + BX_DMA_THIS s[1].DACK[0] = 0; // clear DACK to cascade + } + } +} diff --git a/tools/ioemu/iodev/dma.h b/tools/ioemu/iodev/dma.h new file mode 100644 index 0000000000..9f6c4eb60e --- /dev/null +++ b/tools/ioemu/iodev/dma.h @@ -0,0 +1,114 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: dma.h,v 1.15 2003/05/03 07:41:27 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + +#ifndef _PCDMA_H +#define _PCDMA_H + + +#if BX_USE_DMA_SMF +# define BX_DMA_SMF static +# define BX_DMA_THIS theDmaDevice-> +#else +# define BX_DMA_SMF +# define BX_DMA_THIS this-> +#endif + + + +class bx_dma_c : public bx_dma_stub_c { +public: + + bx_dma_c(); + ~bx_dma_c(void); + + virtual void init(void); + virtual void bios_init(void); + virtual void reset(unsigned type); + virtual void raise_HLDA(void); + virtual void set_DRQ(unsigned channel, bx_bool val); + virtual unsigned get_TC(void); + + virtual unsigned registerDMA8Channel(unsigned channel, + void (* dmaRead)(Bit8u *data_byte), + void (* dmaWrite)(Bit8u *data_byte), + const char *name); + virtual unsigned registerDMA16Channel(unsigned channel, + void (* dmaRead)(Bit16u *data_word), + void (* dmaWrite)(Bit16u *data_word), + const char *name); + virtual unsigned unregisterDMAChannel(unsigned channel); + +private: + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#if !BX_USE_DMA_SMF + Bit32u read( Bit32u address, unsigned io_len) BX_CPP_AttrRegparmN(2); + void write(Bit32u address, Bit32u value, unsigned io_len) BX_CPP_AttrRegparmN(3); +#endif + BX_DMA_SMF void control_HRQ(bx_bool ma_sl); + BX_DMA_SMF void reset_controller(unsigned num); + + struct { + bx_bool DRQ[4]; // DMA Request + bx_bool DACK[4]; // DMA Acknowlege + + bx_bool mask[4]; + bx_bool flip_flop; + Bit8u status_reg; + Bit8u command_reg; + Bit8u request_reg; + Bit8u temporary_reg; + struct { + struct { + Bit8u mode_type; + Bit8u address_decrement; + Bit8u autoinit_enable; + Bit8u transfer_type; + } mode; + Bit16u base_address; + Bit16u current_address; + Bit16u base_count; + Bit16u current_count; + Bit8u page_reg; + bx_bool used; + } chan[4]; /* DMA channels 0..3 */ + } s[2]; // state information DMA-1 / DMA-2 + + bx_bool HLDA; // Hold Acknowlege + bx_bool TC; // Terminal Count + + struct { + void (* dmaRead8)(Bit8u *data_byte); + void (* dmaWrite8)(Bit8u *data_byte); + void (* dmaRead16)(Bit16u *data_word); + void (* dmaWrite16)(Bit16u *data_word); + } h[4]; // DMA read and write handlers + + }; + +#endif // #ifndef _PCDMA_H diff --git a/tools/ioemu/iodev/eth.cc b/tools/ioemu/iodev/eth.cc new file mode 100644 index 0000000000..d6ee9d2948 --- /dev/null +++ b/tools/ioemu/iodev/eth.cc @@ -0,0 +1,194 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: eth.cc,v 1.16 2003/04/28 13:01:09 cbothamy 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 + +// eth.cc - helper code to find and create pktmover classes + +// Peter Grehan (grehan@iprg.nokia.com) coded all of this +// NE2000/ether stuff. + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#if BX_NE2K_SUPPORT + +#define LOG_THIS /* not needed */ + +eth_locator_c *eth_locator_c::all; + +// +// Each pktmover module has a static locator class that registers +// here +// +eth_locator_c::eth_locator_c(const char *type) +{ + next = all; + all = this; + this->type = type; +} + +#ifdef ETH_NULL +extern class bx_null_locator_c bx_null_match; +#endif +#ifdef ETH_FBSD +extern class bx_fbsd_locator_c bx_fbsd_match; +#endif +#ifdef ETH_LINUX +extern class bx_linux_locator_c bx_linux_match; +#endif +#ifdef ETH_WIN32 +extern class bx_win32_locator_c bx_win32_match; +#endif +#if HAVE_ETHERTAP +extern class bx_tap_locator_c bx_tap_match; +#endif +#if HAVE_TUNTAP +extern class bx_tuntap_locator_c bx_tuntap_match; +#endif +#ifdef ETH_TEST +extern bx_test_match; +#endif +#ifdef ETH_ARPBACK +extern class bx_arpback_locator_c bx_arpback_match; +#endif + +// +// Called by ethernet chip emulations to locate and create a pktmover +// object +// +eth_pktmover_c * +eth_locator_c::create(const char *type, const char *netif, + const char *macaddr, + eth_rx_handler_t rxh, void *rxarg) +{ +#ifdef eth_static_constructors + for (eth_locator_c *p = all; p != NULL; p = p->next) { + if (strcmp(type, p->type) == 0) + return (p->allocate(netif, macaddr, rxh, rxarg)); + } +#else + eth_locator_c *ptr = 0; + +#ifdef ETH_ARPBACK + { + if (!strcmp(type, "arpback")) + ptr = (eth_locator_c *) &bx_arpback_match; + } +#endif +#ifdef ETH_NULL + { + if (!strcmp(type, "null")) + ptr = (eth_locator_c *) &bx_null_match; + } +#endif +#ifdef ETH_FBSD + { + if (!strcmp(type, "fbsd")) + ptr = (eth_locator_c *) &bx_fbsd_match; + } +#endif +#ifdef ETH_LINUX + { + if (!strcmp(type, "linux")) + ptr = (eth_locator_c *) &bx_linux_match; + } +#endif +#if HAVE_TUNTAP + { + if (!strcmp(type, "tuntap")) + ptr = (eth_locator_c *) &bx_tuntap_match; + } +#endif +#if HAVE_ETHERTAP + { + if (!strcmp(type, "tap")) + ptr = (eth_locator_c *) &bx_tap_match; + } +#endif +#ifdef ETH_WIN32 + { + if(!strcmp(type, "win32")) + ptr = (eth_locator_c *) &bx_win32_match; + } +#endif +#ifdef ETH_TEST + { + if (!strcmp(type, "test")) + ptr = (eth_locator_c *) &bx_test_match; + } +#endif + if (ptr) + return (ptr->allocate(netif, macaddr, rxh, rxarg)); +#endif + + return (NULL); +} + +#if (HAVE_ETHERTAP==1) || (HAVE_TUNTAP==1) + +extern "C" { +#include <sys/wait.h> +}; + +#undef LOG_THIS +#define LOG_THIS bx_devices.pluginNE2kDevice-> + +// This is a utility script used for tuntap or ethertap +int execute_script( char* scriptname, char* arg1 ) +{ + int pid,status; + + if (!(pid=fork())) { + char filename[BX_PATHNAME_LEN]; + if ( scriptname[0]=='/' ) { + strcpy (filename, scriptname); + } + else { + getcwd (filename, BX_PATHNAME_LEN); + strcat (filename, "/"); + strcat (filename, scriptname); + } + + // execute the script + BX_INFO(("Executing script '%s %s'",filename,arg1)); + execle(filename, scriptname, arg1, NULL, NULL); + + // if we get here there has been a problem + exit(-1); + } + + wait (&status); + if (!WIFEXITED(status)) { + return -1; + } + return WEXITSTATUS(status); +} + +#endif // (HAVE_ETHERTAP==1) || (HAVE_TUNTAP==1) + +#endif /* if BX_NE2K_SUPPORT */ diff --git a/tools/ioemu/iodev/eth.h b/tools/ioemu/iodev/eth.h new file mode 100644 index 0000000000..8ac8c6ff3e --- /dev/null +++ b/tools/ioemu/iodev/eth.h @@ -0,0 +1,76 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: eth.h,v 1.12 2003/04/26 14:48:45 cbothamy 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 + +// Peter Grehan (grehan@iprg.nokia.com) coded all of this +// NE2000/ether stuff. + +// eth.h - see eth_null.cc for implementation details + +typedef void (*eth_rx_handler_t)(void *arg, const void *buf, unsigned len); + +int execute_script(char *name, char* arg1); + +// +// The eth_pktmover class is used by ethernet chip emulations +// to interface to the outside world. An instance of this +// would allow frames to be sent to and received from some +// entity. An example would be the packet filter on a Unix +// system, an NDIS driver in promisc mode on WinNT, or maybe +// a simulated network that talks to another process. +// +class eth_pktmover_c { +public: + virtual void sendpkt(void *buf, unsigned io_len) = 0; + virtual ~eth_pktmover_c (void) {} +protected: + eth_rx_handler_t rxh; // receive callback + void *rxarg; +}; + + +// +// The eth_locator class is used by pktmover classes to register +// their name. Chip emulations use the static 'create' method +// to locate and instantiate a pktmover class. +// +class eth_locator_c { +public: + static eth_pktmover_c *create(const char *type, const char *netif, + const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg); +protected: + eth_locator_c(const char *type); + virtual eth_pktmover_c *allocate(const char *netif, + const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg) = 0; +private: + static eth_locator_c *all; + eth_locator_c *next; + const char *type; +}; + diff --git a/tools/ioemu/iodev/eth_arpback.cc b/tools/ioemu/iodev/eth_arpback.cc new file mode 100644 index 0000000000..0f30711dfb --- /dev/null +++ b/tools/ioemu/iodev/eth_arpback.cc @@ -0,0 +1,214 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: eth_arpback.cc,v 1.11 2002/11/20 19:06:22 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 + +// eth_arpback.cc - basic ethernet packetmover, only responds to ARP + +// Various networking docs: +// http://www.graphcomp.com/info/rfc/ +// rfc0826: arp +// rfc0903: rarp + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" + +#if BX_NE2K_SUPPORT && defined(ETH_ARPBACK) + +#include "crc32.h" +#include "eth_packetmaker.h" +#define LOG_THIS bx_devices.pluginNE2kDevice-> + + +//static const Bit8u external_mac[]={0xB0, 0xC4, 0x20, 0x20, 0x00, 0x00, 0x00}; +//static const Bit8u internal_mac[]={0xB0, 0xC4, 0x20, 0x00, 0x00, 0x00, 0x00}; +//static const Bit8u external_ip[]={ 192, 168, 0, 2, 0x00 }; +//static const Bit8u broadcast_mac[]={0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}; +//static const Bit8u ethtype_arp[]={0x08, 0x06, 0x00}; + +#define MAX_FRAME_SIZE 2048 + +// +// Define the class. This is private to this module +// +class bx_arpback_pktmover_c : public eth_pktmover_c { +public: + bx_arpback_pktmover_c(const char *netif, const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg); + void sendpkt(void *buf, unsigned io_len); +private: + int rx_timer_index; + static void rx_timer_handler(void *); + void rx_timer(void); + FILE *txlog, *txlog_txt; + //Bit8u arpbuf[MAX_FRAME_SIZE]; + //Bit32u buflen; + //bx_bool bufvalid; + //CRC_Generator mycrc; + eth_ETHmaker packetmaker; +}; + + +// +// Define the static class that registers the derived pktmover class, +// and allocates one on request. +// +class bx_arpback_locator_c : public eth_locator_c { +public: + bx_arpback_locator_c(void) : eth_locator_c("arpback") {} +protected: + eth_pktmover_c *allocate(const char *netif, const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg) { + return (new bx_arpback_pktmover_c(netif, macaddr, rxh, rxarg)); + } +} bx_arpback_match; + + +// +// Define the methods for the bx_arpback_pktmover derived class +// + +// the constructor +bx_arpback_pktmover_c::bx_arpback_pktmover_c(const char *netif, + const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg) +{ + this->rx_timer_index = + bx_pc_system.register_timer(this, this->rx_timer_handler, 1000, + 1, 1, "eth_arpback"); // continuous, active + this->rxh = rxh; + this->rxarg = rxarg; + //bufvalid=0; + packetmaker.init(); +#if BX_ETH_NULL_LOGGING + // Start the rx poll + // eventually Bryce wants txlog to dump in pcap format so that + // tcpdump -r FILE can read it and interpret packets. + txlog = fopen ("ne2k-tx.log", "wb"); + if (!txlog) BX_PANIC (("open ne2k-tx.log failed")); + txlog_txt = fopen ("ne2k-txdump.txt", "wb"); + if (!txlog_txt) BX_PANIC (("open ne2k-txdump.txt failed")); + fprintf (txlog_txt, "arpback packetmover readable log file\n"); + fprintf (txlog_txt, "net IF = %s\n", netif); + fprintf (txlog_txt, "MAC address = "); + for (int i=0; i<6; i++) + fprintf (txlog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : ""); + fprintf (txlog_txt, "\n--\n"); + fflush (txlog_txt); +#endif +} + +void +bx_arpback_pktmover_c::sendpkt(void *buf, unsigned io_len) +{ + if(io_len<MAX_FRAME_SIZE) { + eth_packet barney; + memcpy(barney.buf,buf,io_len); + barney.len=io_len; + if(packetmaker.ishandler(barney)) { + packetmaker.sendpacket(barney); + } + /* + if(( (!memcmp(buf, external_mac, 6)) || (!memcmp(buf, broadcast_mac, 6)) ) + && (!memcmp(((Bit8u *)buf)+12, ethtype_arp, 2)) ) { + Bit32u tempcrc; + memcpy(arpbuf,buf,io_len); //move to temporary buffer + memcpy(arpbuf, arpbuf+6, 6); //set destination to sender + memcpy(arpbuf+6, external_mac, 6); //set sender to us + memcpy(arpbuf+32, arpbuf+22, 10); //move destination to sender + memcpy(arpbuf+22, external_mac, 6); //set sender to us + memcpy(arpbuf+28, external_ip, 4); //set sender to us + arpbuf[21]=2; //make this a reply and not a request + tempcrc=mycrc.get_CRC(arpbuf,io_len); + memcpy(arpbuf+io_len, &tempcrc, 4); + buflen=io_len;//+4 + bufvalid=1; + } + */ + } +#if BX_ETH_NULL_LOGGING + BX_DEBUG (("sendpkt length %u", io_len)); + // dump raw bytes to a file, eventually dump in pcap format so that + // tcpdump -r FILE can interpret them for us. + int n = fwrite (buf, io_len, 1, txlog); + if (n != 1) BX_ERROR (("fwrite to txlog failed", io_len)); + // dump packet in hex into an ascii log file + fprintf (txlog_txt, "NE2K transmitting a packet, length %u\n", io_len); + Bit8u *charbuf = (Bit8u *)buf; + for (n=0; n<io_len; n++) { + if (((n % 16) == 0) && n>0) + fprintf (txlog_txt, "\n"); + fprintf (txlog_txt, "%02x ", charbuf[n]); + } + fprintf (txlog_txt, "\n--\n"); + // flush log so that we see the packets as they arrive w/o buffering + fflush (txlog); + fflush (txlog_txt); +#endif +} + +void bx_arpback_pktmover_c::rx_timer_handler (void * this_ptr) +{ +#if BX_ETH_NULL_LOGGING + BX_DEBUG (("rx_timer_handler")); +#endif + bx_arpback_pktmover_c *class_ptr = ((bx_arpback_pktmover_c *)this_ptr); + + class_ptr->rx_timer(); +} + +void bx_arpback_pktmover_c::rx_timer (void) +{ + int nbytes = 0; + struct bpf_hdr *bhdr; + eth_packet rubble; + + if(packetmaker.getpacket(rubble)) { + //bufvalid=0; + void * buf=rubble.buf; + unsigned io_len=rubble.len; + Bit32u n; + fprintf (txlog_txt, "NE2K receiving a packet, length %u\n", io_len); + Bit8u *charbuf = (Bit8u *)buf; + for (n=0; n<io_len; n++) { + if (((n % 16) == 0) && n>0) + fprintf (txlog_txt, "\n"); + fprintf (txlog_txt, "%02x ", charbuf[n]); + } + fprintf (txlog_txt, "\n--\n"); + fflush (txlog_txt); + + (*rxh)(rxarg, buf, io_len); + } +} + +#endif /* if BX_NE2K_SUPPORT && defined(ETH_ARPBACK) */ + diff --git a/tools/ioemu/iodev/eth_fbsd.cc b/tools/ioemu/iodev/eth_fbsd.cc new file mode 100644 index 0000000000..0c24b9b828 --- /dev/null +++ b/tools/ioemu/iodev/eth_fbsd.cc @@ -0,0 +1,385 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: eth_fbsd.cc,v 1.26 2002/11/20 19:06:22 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 + +// Peter Grehan (grehan@iprg.nokia.com) coded all of this +// NE2000/ether stuff. + +// eth_fbsd.cc - A FreeBSD packet filter implementation of +// an ethernet pktmover. There are some problems and limitations +// with FreeBSD: +// - the source address of the frame is overwritten by +// the hosts's source address. This causes problems with +// learning bridges - since they cannot determine where +// BOCHS 'is', they broadcast the frame to all ports. +// - packets cannot be sent from BOCHS to the host +// - TCP performance seems to be abysmal; I think this is +// a timing problem somewhere. +// - I haven't handled the case where multiple frames arrive +// in a single BPF read. +// +// The /dev/bpf* devices need to be set up with the appropriate +// permissions for this to work. +// +// The config line in .bochsrc should look something like: +// +// ne2k: ioaddr=0x280, irq=9, mac=00:a:b:c:1:2, ethmod=fbsd, ethdev=fxp0 +// + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#if BX_NE2K_SUPPORT && defined(ETH_FBSD) + +#define LOG_THIS bx_devices.pluginNE2kDevice-> + +extern "C" { +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> +#include <net/bpf.h> +#include <errno.h> +}; + +#define BX_BPF_POLL 1000 // Poll for a frame every 250 usecs + +#define BX_BPF_BUFSIZ 2048 // enough for an ether frame + bpf hdr + +#define BX_BPF_INSNSIZ 8 // number of bpf insns + +// template filter for a unicast mac address and all +// multicast/broadcast frames +static const struct bpf_insn macfilter[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), // A <- P[2:4] + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xaaaaaaaa, 0, 2), // if A != 0xaaaaaaa GOTO LABEL-1 + BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 0), // A <- P[0:2] + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x0000aaaa, 2, 0), // if A == 0xaaaa GOTO ACCEPT + // LABEL-1 + BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0), // A <- P[0:1] + BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x01, 0, 1), // if !(A & 1) GOTO LABEL-REJECT + // LABEL-ACCEPT + BPF_STMT(BPF_RET, 1514), // Accept packet + // LABEL-REJECT + BPF_STMT(BPF_RET, 0), // Reject packet +}; + +// template filter for all frames +static const struct bpf_insn promiscfilter[] = { + BPF_STMT(BPF_RET, 1514) +}; + +// +// Define the class. This is private to this module +// +class bx_fbsd_pktmover_c : public eth_pktmover_c { +public: + bx_fbsd_pktmover_c(const char *netif, + const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg); + void sendpkt(void *buf, unsigned io_len); + +private: + char *fbsd_macaddr[6]; + int bpf_fd; + static void rx_timer_handler(void *); + void rx_timer(void); + int rx_timer_index; + struct bpf_insn filter[BX_BPF_INSNSIZ]; + FILE *ne2klog, *ne2klog_txt; +}; + + +// +// Define the static class that registers the derived pktmover class, +// and allocates one on request. +// +class bx_fbsd_locator_c : public eth_locator_c { +public: + bx_fbsd_locator_c(void) : eth_locator_c("fbsd") {} +protected: + eth_pktmover_c *allocate(const char *netif, + const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg) { + return (new bx_fbsd_pktmover_c(netif, macaddr, rxh, rxarg)); + } +} bx_fbsd_match; + + +// +// Define the methods for the bx_fbsd_pktmover derived class +// + +// the constructor +// +// Open a bpf file descriptor, and attempt to bind to +// the specified netif (Replicates libpcap open code) +// +bx_fbsd_pktmover_c::bx_fbsd_pktmover_c(const char *netif, + const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg) +{ + char device[sizeof "/dev/bpf000"]; + int tmpfd; + int n = 0; + struct ifreq ifr; + struct bpf_version bv; + struct bpf_program bp; + u_int v; + + memcpy(fbsd_macaddr, macaddr, 6); + + do { + (void)sprintf(device, "/dev/bpf%d", n++); + this->bpf_fd = open(device, O_RDWR); + BX_DEBUG(("tried %s, returned %d (%s)",device,this->bpf_fd,strerror(errno))); + if(errno == EACCES) + break; + } while (this->bpf_fd == -1); + + if (this->bpf_fd == -1) { + BX_PANIC(("eth_freebsd: could not open packet filter: %s", strerror(errno))); + return; + } + + if (ioctl(this->bpf_fd, BIOCVERSION, (caddr_t)&bv) < 0) { + BX_PANIC(("eth_freebsd: could not retrieve bpf version")); + close(this->bpf_fd); + this->bpf_fd = -1; + return; + } + if (bv.bv_major != BPF_MAJOR_VERSION || bv.bv_minor < BPF_MINOR_VERSION) { + BX_PANIC(("eth_freebsd: bpf version mismatch between compilation and runtime")); + close(this->bpf_fd); + this->bpf_fd = -1; + return; + } + + // Set buffer size + v = BX_BPF_BUFSIZ; + if (ioctl(this->bpf_fd, BIOCSBLEN, (caddr_t)&v) < 0) { + BX_PANIC(("eth_freebsd: could not set buffer size: %s", strerror(errno))); + close(this->bpf_fd); + this->bpf_fd = -1; + return; + } + + (void)strncpy(ifr.ifr_name, netif, sizeof(ifr.ifr_name)); + if (ioctl(this->bpf_fd, BIOCSETIF, (caddr_t)&ifr) < 0) { + BX_PANIC(("eth_freebsd: could not enable interface '%s': %s", netif, strerror(errno))); + close(this->bpf_fd); + this->bpf_fd == -1; + } + + // Verify that the device is an ethernet. + if (ioctl(this->bpf_fd, BIOCGDLT, (caddr_t)&v) < 0) { + BX_PANIC(("eth_freebsd: could not retrieve datalink type: %s", strerror(errno))); + close(this->bpf_fd); + this->bpf_fd = -1; + return; + } + if (v != DLT_EN10MB) { + BX_PANIC(("eth_freebsd: incorrect datalink type %d, expected 10mb ethernet", v)); + close(this->bpf_fd); + this->bpf_fd = -1; + return; + } + + // Put the device into promisc mode. This could be optimised + // to filter on a MAC address, broadcast, and all-multi, + // but this will do for now. + // + if (ioctl(this->bpf_fd, BIOCPROMISC, NULL) < 0) { + BX_PANIC(("eth_freebsd: could not enable promisc mode: %s", strerror(errno))); + close(this->bpf_fd); + this->bpf_fd = -1; + return; + } + + // Set up non-blocking i/o + v = 1; + if (ioctl(this->bpf_fd, FIONBIO, &v) < 0) { + BX_PANIC(("eth_freebsd: could not enable non-blocking i/o: %s", strerror(errno))); + close(this->bpf_fd); + this->bpf_fd = -1; + return; + } + + // Install a filter +#ifdef notdef + memcpy(&this->filter, promiscfilter, sizeof(promiscfilter)); + bp.bf_len = 1; +#else + memcpy(&this->filter, macfilter, sizeof(macfilter)); + this->filter[1].k = + (macaddr[2] & 0xff) << 24 | + (macaddr[3] & 0xff) << 16 | + (macaddr[4] & 0xff) << 8 | + (macaddr[5] & 0xff); + this->filter[3].k = + (macaddr[0] & 0xff) << 8 | + (macaddr[1] & 0xff); + bp.bf_len = 8; +#endif + bp.bf_insns = &this->filter[0]; + if (ioctl(this->bpf_fd, BIOCSETF, &bp) < 0) { + BX_PANIC(("eth_freebsd: could not set filter: %s", strerror(errno))); + close(this->bpf_fd); + this->bpf_fd = -1; + return; + } + + // Start the rx poll + this->rx_timer_index = + bx_pc_system.register_timer(this, this->rx_timer_handler, BX_BPF_POLL, + 1, 1, "eth_fbsd"); // continuous, active + + this->rxh = rxh; + this->rxarg = rxarg; + +#if BX_ETH_FBSD_LOGGING + // eventually Bryce wants ne2klog to dump in pcap format so that + // tcpdump -r FILE can read it and interpret packets. + ne2klog = fopen ("ne2k.raw", "wb"); + if (!ne2klog) BX_PANIC (("open ne2k-tx.log failed")); + ne2klog_txt = fopen ("ne2k.txt", "wb"); + if (!ne2klog_txt) BX_PANIC (("open ne2k-txdump.txt failed")); + fprintf (ne2klog_txt, "null packetmover readable log file\n"); + fprintf (ne2klog_txt, "net IF = %s\n", netif); + fprintf (ne2klog_txt, "MAC address = "); + for (int i=0; i<6; i++) + fprintf (ne2klog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : ""); + fprintf (ne2klog_txt, "\n--\n"); + fflush (ne2klog_txt); +#endif +} + +// the output routine - called with pre-formatted ethernet frame. +void +bx_fbsd_pktmover_c::sendpkt(void *buf, unsigned io_len) +{ +#if BX_ETH_FBSD_LOGGING + BX_DEBUG (("sendpkt length %u", io_len)); + // dump raw bytes to a file, eventually dump in pcap format so that + // tcpdump -r FILE can interpret them for us. + int n = fwrite (buf, io_len, 1, ne2klog); + if (n != 1) BX_ERROR (("fwrite to ne2klog failed", io_len)); + // dump packet in hex into an ascii log file + fprintf (ne2klog_txt, "NE2K TX packet, length %u\n", io_len); + Bit8u *charbuf = (Bit8u *)buf; + for (n=0; n<io_len; n++) { + if (((n % 16) == 0) && n>0) + fprintf (ne2klog_txt, "\n"); + fprintf (ne2klog_txt, "%02x ", charbuf[n]); + } + fprintf (ne2klog_txt, "\n--\n"); + // flush log so that we see the packets as they arrive w/o buffering + fflush (ne2klog); + fflush (ne2klog_txt); +#endif + int status; + + if (this->bpf_fd != -1) + status = write(this->bpf_fd, buf, io_len); +} + +// The receive poll process +void +bx_fbsd_pktmover_c::rx_timer_handler(void *this_ptr) +{ + bx_fbsd_pktmover_c *class_ptr = (bx_fbsd_pktmover_c *) this_ptr; + + class_ptr->rx_timer(); +} + + +void +bx_fbsd_pktmover_c::rx_timer(void) +{ + int nbytes = 0; + unsigned char rxbuf[BX_BPF_BUFSIZ]; + struct bpf_hdr *bhdr; + struct bpf_stat bstat; + static struct bpf_stat previous_bstat; + int counter = 10; +#define phdr ((unsigned char*)bhdr) + + bhdr = (struct bpf_hdr *) rxbuf; + nbytes = read(this->bpf_fd, rxbuf, sizeof(rxbuf)); + + while (phdr < (rxbuf + nbytes)) { + if (ioctl(this->bpf_fd, BIOCGSTATS, &bstat) < 0) { + BX_PANIC(("eth_freebsd: could not stat filter: %s", strerror(errno))); + } + if (bstat.bs_drop > previous_bstat.bs_drop) { + BX_INFO (("eth_freebsd: %d packets dropped by the kernel.", + bstat.bs_drop - previous_bstat.bs_drop)); + } + previous_bstat = bstat; + if (bhdr->bh_caplen < 20 || bhdr->bh_caplen > 1514) { + BX_ERROR(("eth_freebsd: received too weird packet length: %d", bhdr->bh_caplen)); + } + + // filter out packets sourced from this node + if (memcmp(bhdr + bhdr->bh_hdrlen + 6, this->fbsd_macaddr, 6)) { + (*rxh)(rxarg, phdr + bhdr->bh_hdrlen, bhdr->bh_caplen); + } + +#if BX_ETH_FBSD_LOGGING + /// hey wait there is no receive data with a NULL ethernet, is there.... + BX_DEBUG (("receive packet length %u", nbytes)); + // dump raw bytes to a file, eventually dump in pcap format so that + // tcpdump -r FILE can interpret them for us. + if (1 != fwrite (bhdr, bhdr->bh_caplen, 1, ne2klog)) { + BX_PANIC (("fwrite to ne2klog failed: %s", strerror(errno))); + } + // dump packet in hex into an ascii log file + fprintf (this->ne2klog_txt, "NE2K RX packet, length %u\n", bhdr->bh_caplen); + Bit8u *charrxbuf = (Bit8u *)rxbuf; + int n; + for (n=0; n<bhdr->bh_caplen; n++) { + if (((n % 16) == 0) && n>0) + fprintf (this->ne2klog_txt, "\n"); + fprintf (this->ne2klog_txt, "%02x ", phdr[n]); + } + fprintf (this->ne2klog_txt, "\n--\n"); + // flush log so that we see the packets as they arrive w/o buffering + fflush (this->ne2klog); + fflush (this->ne2klog_txt); +#endif + + // Advance bhdr and phdr pointers to next packet + bhdr = (struct bpf_hdr*) ((char*) bhdr + BPF_WORDALIGN(bhdr->bh_hdrlen + bhdr->bh_caplen)); + } +} + +#endif /* if BX_NE2K_SUPPORT && defined(ETH_FBSD) */ + diff --git a/tools/ioemu/iodev/eth_linux.cc b/tools/ioemu/iodev/eth_linux.cc new file mode 100644 index 0000000000..ec5f1fee73 --- /dev/null +++ b/tools/ioemu/iodev/eth_linux.cc @@ -0,0 +1,286 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: eth_linux.cc,v 1.14 2003/02/16 19:35:57 vruppert 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 + +// Peter Grehan (grehan@iprg.nokia.com) coded all of this +// NE2000/ether stuff. + +// eth_linux.cc - A Linux socket filter adaptation of the FreeBSD BPF driver +// <splite@purdue.edu> 21 June 2001 +// +// Problems and limitations: +// - packets cannot be sent from BOCHS to the host +// - Linux kernel sometimes gets network watchdog timeouts under emulation +// - author doesn't know C++ +// +// The config line in .bochsrc should look something like: +// +// ne2k: ioaddr=0x280, irq=10, mac=00:a:b:c:1:2, ethmod=linux, ethdev=eth0 +// + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#if BX_NE2K_SUPPORT && defined (ETH_LINUX) +#define LOG_THIS bx_devices.pluginNE2kDevice-> + +extern "C" { +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netpacket/packet.h> +#include <netinet/in.h> +#include <net/ethernet.h> +#include <net/if.h> +#include <linux/types.h> +#include <linux/filter.h> +}; + +#define BX_PACKET_POLL 1000 // Poll for a frame every 1000 usecs + +#define BX_PACKET_BUFSIZ 2048 // Enough for an ether frame + +// template filter for a unicast mac address and all +// multicast/broadcast frames +static const struct sock_filter macfilter[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xaaaaaaaa, 0, 2), + BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 0), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x0000aaaa, 2, 0), + BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0), + BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x01, 0, 1), + BPF_STMT(BPF_RET, 1514), + BPF_STMT(BPF_RET, 0), +}; +#define BX_LSF_ICNT 8 // number of lsf instructions in macfilter + +#if 0 +// template filter for all frames +static const struct sock_filter promiscfilter[] = { + BPF_STMT(BPF_RET, 1514) +}; +#endif + +// +// Define the class. This is private to this module +// +class bx_linux_pktmover_c : public eth_pktmover_c { +public: + bx_linux_pktmover_c(const char *netif, + const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg); + void sendpkt(void *buf, unsigned io_len); + +private: + unsigned char *linux_macaddr[6]; + int fd; + int ifindex; + static void rx_timer_handler(void *); + void rx_timer(void); + int rx_timer_index; + struct sock_filter filter[BX_LSF_ICNT]; +}; + + +// +// Define the static class that registers the derived pktmover class, +// and allocates one on request. +// +class bx_linux_locator_c : public eth_locator_c { +public: + bx_linux_locator_c(void) : eth_locator_c("linux") {} +protected: + eth_pktmover_c *allocate(const char *netif, + const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg) { + return (new bx_linux_pktmover_c(netif, macaddr, rxh, rxarg)); + } +} bx_linux_match; + + +// +// Define the methods for the bx_linux_pktmover derived class +// + +// the constructor +// +bx_linux_pktmover_c::bx_linux_pktmover_c(const char *netif, + const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg) +{ + struct sockaddr_ll sll; + struct packet_mreq mr; + struct ifreq ifr; + struct sock_fprog fp; + + memcpy(linux_macaddr, macaddr, 6); + + // Open packet socket + // + if ((this->fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) { + if (errno == EACCES) + BX_PANIC(("eth_linux: must be root or have CAP_NET_RAW capability to open socket")); + else + BX_PANIC(("eth_linux: could not open socket: %s", strerror(errno))); + this->fd = -1; + return; + } + + // Translate interface name to index + // + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, netif); + if (ioctl(this->fd, SIOCGIFINDEX, &ifr) == -1) { + BX_PANIC(("eth_linux: could not get index for interface '%s'\n", netif)); + close(fd); + this->fd = -1; + return; + } + this->ifindex = ifr.ifr_ifindex; + + + // Bind to given interface + // + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_ifindex = this->ifindex; + if (bind(fd, (struct sockaddr *)&sll, (socklen_t)sizeof(sll)) == -1) { + BX_PANIC(("eth_linux: could not bind to interface '%s': %s\n", netif, strerror(errno))); + close(fd); + this->fd = -1; + return; + } + + // Put the device into promisc mode. + // + memset(&mr, 0, sizeof(mr)); + mr.mr_ifindex = this->ifindex; + mr.mr_type = PACKET_MR_PROMISC; + if (setsockopt(this->fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, (void *)&mr, (socklen_t)sizeof(mr)) == -1) { + BX_PANIC(("eth_linux: could not enable promisc mode: %s\n", strerror(errno))); + close(this->fd); + this->fd = -1; + return; + } + + // Set up non-blocking i/o + if (fcntl(this->fd, F_SETFL, O_NONBLOCK) == -1) { + BX_PANIC(("eth_linux: could not set non-blocking i/o on socket")); + close(this->fd); + this->fd = -1; + return; + } + + // Install a filter +#ifdef notdef + memcpy(&this->filter, promiscfilter, sizeof(promiscfilter)); + fp.len = 1; +#endif + memcpy(&this->filter, macfilter, sizeof(macfilter)); + this->filter[1].k = (macaddr[2] & 0xff) << 24 | (macaddr[3] & 0xff) << 16 | + (macaddr[4] & 0xff) << 8 | (macaddr[5] & 0xff); + this->filter[3].k = (macaddr[0] & 0xff) << 8 | (macaddr[1] & 0xff); + fp.len = BX_LSF_ICNT; + fp.filter = this->filter; + BX_INFO(("eth_linux: fp.len=%d fp.filter=%x", fp.len, (unsigned) fp.filter)); + if (setsockopt(this->fd, SOL_SOCKET, SO_ATTACH_FILTER, &fp, sizeof(fp)) < 0) { + BX_PANIC(("eth_linux: could not set socket filter: %s", strerror(errno))); + close(this->fd); + this->fd = -1; + return; + } + + // Start the rx poll + this->rx_timer_index = + bx_pc_system.register_timer(this, this->rx_timer_handler, BX_PACKET_POLL, + 1, 1, "eth_linux"); // continuous, active + + this->rxh = rxh; + this->rxarg = rxarg; + BX_INFO(("eth_linux: enabled NE2K emulation on interface %s", netif)); +} + +// the output routine - called with pre-formatted ethernet frame. +void +bx_linux_pktmover_c::sendpkt(void *buf, unsigned io_len) +{ + int status; + + if (this->fd != -1) { + status = write(this->fd, buf, io_len); + if (status == -1) + BX_INFO(("eth_linux: write failed: %s", strerror(errno))); + } +} + +// The receive poll process +void +bx_linux_pktmover_c::rx_timer_handler(void *this_ptr) +{ + bx_linux_pktmover_c *class_ptr = (bx_linux_pktmover_c *) this_ptr; + + class_ptr->rx_timer(); +} + +void +bx_linux_pktmover_c::rx_timer(void) +{ + int nbytes = 0; + Bit8u rxbuf[BX_PACKET_BUFSIZ]; + struct sockaddr_ll sll; + socklen_t fromlen; +//static unsigned char bcast_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; + + if (this->fd == -1) + return; + + fromlen = sizeof(sll); + nbytes = recvfrom(this->fd, rxbuf, sizeof(rxbuf), 0, (struct sockaddr *)&sll, &fromlen); + + if (nbytes == -1) { + if (errno != EAGAIN) + BX_INFO(("eth_linux: error receiving packet: %s\n", strerror(errno))); + return; + } + + // this should be done with LSF someday + // filter out packets sourced by us + if (memcmp(sll.sll_addr, this->linux_macaddr, 6) == 0) + return; + // let through broadcast, multicast, and our mac address +// if ((memcmp(rxbuf, bcast_addr, 6) == 0) || (memcmp(rxbuf, this->linux_macaddr, 6) == 0) || rxbuf[0] & 0x01) { + BX_DEBUG(("eth_linux: got packet: %d bytes, dst=%x:%x:%x:%x:%x:%x, src=%x:%x:%x:%x:%x:%x\n", nbytes, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3], rxbuf[4], rxbuf[5], rxbuf[6], rxbuf[7], rxbuf[8], rxbuf[9], rxbuf[10], rxbuf[11])); + (*rxh)(rxarg, rxbuf, nbytes); +// } +} +#endif /* if BX_NE2K_SUPPORT && defined ETH_LINUX */ diff --git a/tools/ioemu/iodev/eth_null.cc b/tools/ioemu/iodev/eth_null.cc new file mode 100644 index 0000000000..11162798ef --- /dev/null +++ b/tools/ioemu/iodev/eth_null.cc @@ -0,0 +1,164 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: eth_null.cc,v 1.13 2002/11/20 19:06:23 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 + +// eth_null.cc - skeleton code for an ethernet pktmover + +// Various networking docs: +// http://www.graphcomp.com/info/rfc/ +// rfc0826: arp +// rfc0903: rarp + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#if BX_NE2K_SUPPORT + +#define LOG_THIS bx_devices.pluginNE2kDevice-> + + +// +// Define the class. This is private to this module +// +class bx_null_pktmover_c : public eth_pktmover_c { +public: + bx_null_pktmover_c(const char *netif, const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg); + void sendpkt(void *buf, unsigned io_len); +private: + int rx_timer_index; + static void rx_timer_handler(void *); + FILE *txlog, *txlog_txt, *rxlog, *rxlog_txt; +}; + + +// +// Define the static class that registers the derived pktmover class, +// and allocates one on request. +// +class bx_null_locator_c : public eth_locator_c { +public: + bx_null_locator_c(void) : eth_locator_c("null") {} +protected: + eth_pktmover_c *allocate(const char *netif, const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg) { + return (new bx_null_pktmover_c(netif, macaddr, rxh, rxarg)); + } +} bx_null_match; + + +// +// Define the methods for the bx_null_pktmover derived class +// + +// the constructor +bx_null_pktmover_c::bx_null_pktmover_c(const char *netif, + const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg) +{ +#if BX_ETH_NULL_LOGGING + // Start the rx poll + this->rx_timer_index = + bx_pc_system.register_timer(this, this->rx_timer_handler, 1000, + 1, 1, "eth_null"); // continuous, active + this->rxh = rxh; + this->rxarg = rxarg; + // eventually Bryce wants txlog to dump in pcap format so that + // tcpdump -r FILE can read it and interpret packets. + txlog = fopen ("ne2k-tx.log", "wb"); + if (!txlog) BX_PANIC (("open ne2k-tx.log failed")); + txlog_txt = fopen ("ne2k-txdump.txt", "wb"); + if (!txlog_txt) BX_PANIC (("open ne2k-txdump.txt failed")); + fprintf (txlog_txt, "null packetmover readable log file\n"); + fprintf (txlog_txt, "net IF = %s\n", netif); + fprintf (txlog_txt, "MAC address = "); + for (int i=0; i<6; i++) + fprintf (txlog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : ""); + fprintf (txlog_txt, "\n--\n"); + fflush (txlog_txt); +#endif +} + +void +bx_null_pktmover_c::sendpkt(void *buf, unsigned io_len) +{ +#if BX_ETH_NULL_LOGGING + BX_DEBUG (("sendpkt length %u", io_len)); + // dump raw bytes to a file, eventually dump in pcap format so that + // tcpdump -r FILE can interpret them for us. + unsigned int n = fwrite (buf, io_len, 1, txlog); + if (n != 1) BX_ERROR (("fwrite to txlog failed, io_len = %u", io_len)); + // dump packet in hex into an ascii log file + fprintf (txlog_txt, "NE2K transmitting a packet, length %u\n", io_len); + Bit8u *charbuf = (Bit8u *)buf; + for (n=0; n<io_len; n++) { + if (((n % 16) == 0) && n>0) + fprintf (txlog_txt, "\n"); + fprintf (txlog_txt, "%02x ", charbuf[n]); + } + fprintf (txlog_txt, "\n--\n"); + // flush log so that we see the packets as they arrive w/o buffering + fflush (txlog); + fflush (txlog_txt); +#endif +} + +void bx_null_pktmover_c::rx_timer_handler (void *this_ptr) +{ +#if BX_ETH_NULL_LOGGING + /// hey wait there is no receive data with a NULL ethernet, is there.... + + int io_len = 0; + Bit8u buf[1]; + bx_null_pktmover_c *class_ptr = (bx_null_pktmover_c *) this_ptr; + if (io_len > 0) { + BX_DEBUG (("receive packet length %u", io_len)); + // dump raw bytes to a file, eventually dump in pcap format so that + // tcpdump -r FILE can interpret them for us. + int n = fwrite (buf, io_len, 1, class_ptr->rxlog); + if (n != 1) BX_ERROR (("fwrite to rxlog failed, io_len = %u", io_len)); + // dump packet in hex into an ascii log file + fprintf (class_ptr->rxlog_txt, "NE2K transmitting a packet, length %u\n", io_len); + Bit8u *charbuf = (Bit8u *)buf; + for (n=0; n<io_len; n++) { + if (((n % 16) == 0) && n>0) + fprintf (class_ptr->rxlog_txt, "\n"); + fprintf (class_ptr->rxlog_txt, "%02x ", charbuf[n]); + } + fprintf (class_ptr->rxlog_txt, "\n--\n"); + // flush log so that we see the packets as they arrive w/o buffering + fflush (class_ptr->rxlog); + fflush (class_ptr->rxlog_txt); + } +#endif +} + +#endif /* if BX_NE2K_SUPPORT */ diff --git a/tools/ioemu/iodev/eth_packetmaker.cc b/tools/ioemu/iodev/eth_packetmaker.cc new file mode 100644 index 0000000000..5ed5e47c8c --- /dev/null +++ b/tools/ioemu/iodev/eth_packetmaker.cc @@ -0,0 +1,184 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: eth_packetmaker.cc,v 1.8 2002/11/20 19:06:23 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" + +#if BX_NE2K_SUPPORT && defined(ETH_ARPBACK) + +#include "eth_packetmaker.h" + + +bx_bool sendable(const eth_packet& outpacket) { + //FINISH ME! +} + +Bit32u eth_IPmaker::datalen(const eth_packet& outpacket) { + Bit32u out; + out=((outpacket.buf[16]<<8)+outpacket.buf[17])-(4*(0xF & outpacket.buf[14])); + return out; +} + +const Bit8u * eth_IPmaker::datagram(const eth_packet& outpacket) { + const Bit8u * out; + out=outpacket.buf+14+(4*(0xF & outpacket.buf[14])); + return out; +} + +Bit32u eth_IPmaker::build_packet_header(Bit32u source, Bit32u dest, Bit8u protocol, Bit32u datalen) { + Bit32u temp; + Bit32u i; + memcpy(pending.buf,internal_mac,6); + memcpy(pending.buf+6,external_mac,6); + memcpy(pending.buf+12,ethtype_ip,2); + pending.buf[14]=0x45; + pending.buf[15]=0; + temp=datalen+20; + pending.buf[16]=(temp>>8) & 0xFF; + pending.buf[17]=temp & 0xFF; + pending.buf[18]=0; + pending.buf[19]=0; + pending.buf[20]=0; + pending.buf[21]=0; + pending.buf[22]=30; + pending.buf[23]=protocol; + pending.buf[24]=0; + pending.buf[25]=0; + pending.buf[26]=(source>>24) & 0xFF; + pending.buf[27]=(source>>16) & 0xFF; + pending.buf[28]=(source>>8) & 0xFF; + pending.buf[29]=(source) & 0xFF; + pending.buf[30]=(dest>>24) & 0xFF; + pending.buf[31]=(dest>>16) & 0xFF; + pending.buf[32]=(dest>>8) & 0xFF; + pending.buf[33]=(dest) & 0xFF; + //Compute Checksum + temp=0; + for(i=14;i<34;i=i+2) { + Bit32u addee=pending.buf[i]; + addee=(addee<<8) & pending.buf[i+1]; + temp=temp+addee; + } + temp=(temp>>16)+(temp&0xFFFF); + temp=(temp>>16)+(temp&0xFFFF); + pending.buf[24]=~(Bit8u)((temp>>8) & 0xFF); + pending.buf[25]=~(Bit8u)(temp & 0xFF); + return(34); +} + +Bit8u eth_IPmaker::protocol(const eth_packet& outpacket) { + Bit8u out; + out=0xFF & *(outpacket.buf+23); +} + +Bit32u eth_IPmaker::source(const eth_packet& outpacket) { + Bit32u out; + out=0xFF & *(outpacket.buf+26); + out=(out<<8) | (0xFF & *(outpacket.buf+27)); + out=(out<<8) | (0xFF & *(outpacket.buf+28)); + out=(out<<8) | (0xFF & *(outpacket.buf+29)); + return out; +} + +Bit32u eth_IPmaker::destination(const eth_packet& outpacket) { + Bit32u out; + out=0xFF & *(outpacket.buf+30); + out=(out<<8) | (0xFF & *(outpacket.buf+31)); + out=(out<<8) | (0xFF & *(outpacket.buf+32)); + out=(out<<8) | (0xFF & *(outpacket.buf+33)); + return out; +} + +void eth_IPmaker::init(void) { + is_pending=0; +} + +void +eth_ETHmaker::init(void) { + arper.init(); +} + +bx_bool +eth_ETHmaker::getpacket(eth_packet& inpacket) { + return arper.getpacket(inpacket); +} + +bx_bool +eth_ETHmaker::ishandler(const eth_packet& outpacket) { + if((outpacket.len>=60) && + ( (!memcmp(outpacket.buf, external_mac, 6)) + || (!memcmp(outpacket.buf, broadcast_mac, 6)) ) && + ( (!memcmp(outpacket.buf+12, ethtype_arp, 2)) || + (!memcmp(outpacket.buf+12, ethtype_ip, 2)) ) && + (outpacket.len<PACKET_BUF_SIZE) + ) { + return 1; + } + return 0; +} + +bx_bool +eth_ETHmaker::sendpacket(const eth_packet& outpacket) { + return arper.sendpacket(outpacket); +} + + + +void +eth_ARPmaker::init(void) { + is_pending=0; + pending.len=0; +} + +bx_bool +eth_ARPmaker::getpacket(eth_packet& inpacket) { + if(is_pending) { + memcpy(inpacket.buf,pending.buf,pending.len); + inpacket.len=pending.len; + is_pending=0; + return 1; + } + return 0; +} + +bx_bool +eth_ARPmaker::ishandler(const eth_packet& outpacket) { + if((outpacket.len>=60) && + (!memcmp(outpacket.buf+12, ethtype_arp, 2)) && + (outpacket.len<PACKET_BUF_SIZE) && + ( (!memcmp(outpacket.buf, external_mac, 6)) + || (!memcmp(outpacket.buf, broadcast_mac, 6)) ) && + (!memcmp(outpacket.buf+38, external_ip, 4)) + ) { + return 1; + } + return 0; +} + +bx_bool +eth_ARPmaker::sendpacket(const eth_packet& outpacket) { + if(is_pending || !ishandler(outpacket)) { + return 0; + } else { + Bit32u tempcrc; + memcpy(pending.buf,outpacket.buf,outpacket.len); //move to temporary buffer + memcpy(pending.buf, pending.buf+6, 6); //set destination to sender + memcpy(pending.buf+6, external_mac, 6); //set sender to us + memcpy(pending.buf+32, pending.buf+22, 10); //move destination to sender + memcpy(pending.buf+22, external_mac, 6); //set sender to us + memcpy(pending.buf+28, external_ip, 4); //set sender to us + pending.buf[21]=2; //make this a reply and not a request + //tempcrc=mycrc.get_CRC(pending.buf,len); + //memcpy(pending.buf+len, &tempcrc, 4); + pending.len=outpacket.len; //+4 + is_pending=1; + return 1; + } +} + +#endif /* if BX_NE2K_SUPPORT && defined(ETH_ARPBACK) */ diff --git a/tools/ioemu/iodev/eth_packetmaker.h b/tools/ioemu/iodev/eth_packetmaker.h new file mode 100644 index 0000000000..d442325a9f --- /dev/null +++ b/tools/ioemu/iodev/eth_packetmaker.h @@ -0,0 +1,135 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: eth_packetmaker.h,v 1.6 2002/10/25 11:44:39 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// + +#ifndef _ETH_PACKETMAKER_H_ +#define _ETH_PACKETMAKER_H_ + +#include "../config.h" + +#ifdef ETH_ARPBACK + +#define PACKET_BUF_SIZE 2048 +static const Bit8u internal_mac[]={0xB0, 0xC4, 0x20, 0x20, 0x00, 0x00, 0x00}; +static const Bit8u external_mac[]={0xB0, 0xC4, 0x20, 0x20, 0x00, 0x00, 0x00}; +static const Bit8u external_ip[]={ 192, 168, 0, 2, 0x00 }; +static const Bit8u broadcast_mac[]={0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}; +static const Bit8u ethtype_arp[]={0x08, 0x06, 0x00}; +static const Bit8u ethtype_ip[]={0x08, 0x00, 0x00}; +static const Bit8u prot_udp=17; +static const Bit8u prot_tcp=6; + + +class eth_packet { +public: + Bit8u buf[PACKET_BUF_SIZE]; + Bit32u len; +}; + + +class eth_packetmaker { +public: + virtual bx_bool getpacket(eth_packet& inpacket) = 0; + virtual bx_bool ishandler(const eth_packet& outpacket) = 0; + virtual bx_bool sendpacket(const eth_packet& outpacket) = 0; +}; + + +class eth_ARPmaker : public eth_packetmaker { +public: + void init(void); + bx_bool ishandler(const eth_packet& outpacket); + bx_bool sendpacket(const eth_packet& outpacket); + bx_bool getpacket(eth_packet& inpacket); +private: + eth_packet pending; + bx_bool is_pending; +}; + + +class eth_IPmaker : eth_packetmaker { +public: + void init(void); + virtual bx_bool ishandler(const eth_packet& outpacket)=0; + virtual bx_bool sendpacket(const eth_packet& outpacket)=0; + virtual bx_bool getpacket(eth_packet& inpacket)=0; + +protected: + bx_bool sendable(const eth_packet& outpacket); + + Bit32u source(const eth_packet& outpacket); + Bit32u destination(const eth_packet& outpacket); + Bit8u protocol(const eth_packet& outpacket); + + const Bit8u * datagram(const eth_packet& outpacket); + Bit32u datalen(const eth_packet& outpacket); + + //Build a header in pending, return header length in bytes. + Bit32u build_packet_header(Bit32u source, Bit32u dest, Bit8u protocol, Bit32u datalen); + + eth_packet pending; + bx_bool is_pending; + + //Bit8u Version; //=4 (4 bits) + //It better be! + + //Bit8u IHL; //Header length in 32-bit bytes (4 bits) + //Used to strip layer + + //Bit8u Type_of_Service; //not relevent, set to 0; + //Ignore on receive, set to 0 on send. + + //Bit16u Total_Length;//length of the datagram in octets. use 576 or less; + //Use 576 or less on send. + //Use to get length on receive + + //Bit16u Identification;//Identifier for assembling fragments + //Ignore, we'll drop fragments + + //Bit8u Flags;//0,Don't fragment, More Fragments (vs last fragment) + //Set to 0 on send + //Drop if more fragments set. + + //Bit16u Fragment Offset;//where in the datagram this fragment belongs + //Should be 0 for send and receive. + + //Bit8u TTL;//Set to something sorta big. + //Shouldn't be 0 on receive + //Set to something big on send + + //Bit8u Protocol; + //Defines Protocol. + //TCP=?, UDP=? + + //Bit16u Header_Checksum;//16-bit one's complement of the one's complement + //sum of all 16-bit words in header; + //Could check on receive, must set on send. + + //Bit32u Source;//source address + //Bit32u Destination;//destination address +}; + +/* +class eth_TCPmaker : eth_packetmaker { +}; + +class eth_UDPmaker : eth_packetmaker { +}; +*/ + +class eth_ETHmaker : public eth_packetmaker { +public: + //handles all packets to a MAC addr. + void init(void); + virtual bx_bool getpacket(eth_packet& inpacket); + virtual bx_bool ishandler(const eth_packet& outpacket); + virtual bx_bool sendpacket(const eth_packet& outpacket); +private: + eth_ARPmaker arper; +}; + + +#endif // ETH_ARPBACK +#endif // _ETH_PACKETMAKER_H_ + diff --git a/tools/ioemu/iodev/eth_tap.cc b/tools/ioemu/iodev/eth_tap.cc new file mode 100644 index 0000000000..cb1bf1df89 --- /dev/null +++ b/tools/ioemu/iodev/eth_tap.cc @@ -0,0 +1,370 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: eth_tap.cc,v 1.16 2003/10/02 11:33:41 danielg4 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 + +// eth_tap.cc - TAP interface by Bryce Denney +// +// Here's how to get this working. On the host machine: +// $ su root +// # /sbin/insmod ethertap +// Using /lib/modules/2.2.14-5.0/net/ethertap.o +// # mknod /dev/tap0 c 36 16 # if not already there +// # /sbin/ifconfig tap0 10.0.0.1 +// # /sbin/route add -host 10.0.0.2 gw 10.0.0.1 +// +// Now you have a tap0 device which you can on the ifconfig output. The +// tap0 interface has the IP address of 10.0.0.1. The bochs machine will have +// the IP address 10.0.0.2. +// +// Compile a bochs version from March 8, 2002 or later with --enable-ne2000. +// Add this ne2k line to your .bochsrc to activate the tap device. +// ne2k: ioaddr=0x280, irq=9, mac=fe:fd:00:00:00:01, ethmod=tap, ethdev=tap0 +// Don't change the mac or ethmod! +// +// Boot up DLX Linux in Bochs. Log in as root and then type the following +// commands to set up networking: +// # ifconfig eth0 10.0.0.2 +// # route add -net 10.0.0.0 +// # route add default gw 10.0.0.1 +// Now you should be able to ping from guest OS to your host machine, if +// you give its IP number. I'm still having trouble with pings from the +// host machine to the guest, so something is still not right. Symptoms: I +// ping from the host to the guest's IP address 10.0.0.2. With tcpdump I can +// see the ping going to Bochs, and then the ping reply coming from Bochs. +// But the ping program itself does not see the responses....well every +// once in a while it does, like 1 in 60 pings. +// +// host$ ping 10.0.0.2 +// PING 10.0.0.2 (10.0.0.2) from 10.0.0.1 : 56(84) bytes of data. +// +// Netstat output: +// 20:29:59.018776 fe:fd:0:0:0:0 fe:fd:0:0:0:1 0800 98: 10.0.0.1 > 10.0.0.2: icmp: echo request +// 4500 0054 2800 0000 4001 3ea7 0a00 0001 +// 0a00 0002 0800 09d3 a53e 0400 9765 893c +// 3949 0000 0809 0a0b 0c0d 0e0f 1011 1213 +// 1415 1617 1819 +// 20:29:59.023017 fe:fd:0:0:0:1 fe:fd:0:0:0:0 0800 98: 10.0.0.2 > 10.0.0.1: icmp: echo reply +// 4500 0054 004a 0000 4001 665d 0a00 0002 +// 0a00 0001 0000 11d3 a53e 0400 9765 893c +// 3949 0000 0809 0a0b 0c0d 0e0f 1011 1213 +// 1415 1617 1819 +// +// I suspect it may be related to the fact that ping 10.0.0.1 from the +// host also doesn't work. Why wouldn't the host respond to its own IP +// address on the tap0 device? +// +// Theoretically, if you set up packet forwarding (with masquerading) on the +// host, you should be able to get Bochs talking to anyone on the internet. +// + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#if BX_NE2K_SUPPORT + +#define LOG_THIS bx_devices.pluginNE2kDevice-> + +#include <signal.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#ifndef __APPLE__ +#include <sys/poll.h> +#endif +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/wait.h> +#if defined(__FreeBSD__) || defined(__APPLE__) // Should be fixed for other *BSD +#include <net/if.h> +#else +#include <asm/types.h> +#include <linux/netlink.h> +#include <linux/if.h> +#endif +#include <assert.h> +#include <fcntl.h> +#include <errno.h> + +#define TAP_VIRTUAL_HW_ADDR 0xDEADBEEF +#define BX_ETH_TAP_LOGGING 1 +#define BX_PACKET_BUFSIZ 2048 // Enough for an ether frame + +// +// Define the class. This is private to this module +// +class bx_tap_pktmover_c : public eth_pktmover_c { +public: + bx_tap_pktmover_c(const char *netif, const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg); + void sendpkt(void *buf, unsigned io_len); +private: + int fd; + int rx_timer_index; + static void rx_timer_handler(void *); + void rx_timer (); + FILE *txlog, *txlog_txt, *rxlog, *rxlog_txt; +}; + + +// +// Define the static class that registers the derived pktmover class, +// and allocates one on request. +// +class bx_tap_locator_c : public eth_locator_c { +public: + bx_tap_locator_c(void) : eth_locator_c("tap") {} +protected: + eth_pktmover_c *allocate(const char *netif, const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg) { + return (new bx_tap_pktmover_c(netif, macaddr, rxh, rxarg)); + } +} bx_tap_match; + + +// +// Define the methods for the bx_tap_pktmover derived class +// + +// the constructor +bx_tap_pktmover_c::bx_tap_pktmover_c(const char *netif, + const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg) +{ + int flags; + char filename[BX_PATHNAME_LEN]; + if (strncmp (netif, "tap", 3) != 0) { + BX_PANIC (("eth_tap: interface name (%s) must be tap0..tap15", netif)); + } + sprintf (filename, "/dev/%s", netif); + +#if defined(__linux__) + // check if the TAP devices is running, and turn on ARP. This is based + // on code from the Mac-On-Linux project. http://http://www.maconlinux.org/ + int sock = socket( AF_INET, SOCK_DGRAM, 0 ); + if (sock < 0) { + BX_PANIC (("socket creation: %s", strerror(errno))); + return; + } + struct ifreq ifr; + memset( &ifr, 0, sizeof(ifr) ); + strncpy( ifr.ifr_name, netif, sizeof(ifr.ifr_name) ); + if( ioctl( sock, SIOCGIFFLAGS, &ifr ) < 0 ){ + BX_PANIC (("SIOCGIFFLAGS on %s: %s", netif, strerror (errno))); + close(sock); + return; + } + if( !(ifr.ifr_flags & IFF_RUNNING ) ){ + BX_PANIC (("%s device is not running", netif)); + close(sock); + return; + } + if( (ifr.ifr_flags & IFF_NOARP ) ){ + BX_INFO (("turn on ARP for %s device", netif)); + ifr.ifr_flags &= ~IFF_NOARP; + if( ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0 ) { + BX_PANIC (("SIOCSIFFLAGS: %s", strerror(errno))); + close(sock); + return; + } + } + close(sock); +#endif + + fd = open (filename, O_RDWR); + if (fd < 0) { + BX_PANIC (("open failed on %s: %s", netif, strerror (errno))); + return; + } + + /* set O_ASYNC flag so that we can poll with read() */ + if ((flags = fcntl( fd, F_GETFL)) < 0) { + BX_PANIC (("getflags on tap device: %s", strerror (errno))); + } + flags |= O_NONBLOCK; + if (fcntl( fd, F_SETFL, flags ) < 0) { + BX_PANIC (("set tap device flags: %s", strerror (errno))); + } + + BX_INFO (("eth_tap: opened %s device", netif)); + + /* Execute the configuration script */ + char intname[IFNAMSIZ]; + strcpy(intname,netif); + char *scriptname=bx_options.ne2k.Oscript->getptr(); + if((scriptname != NULL) + &&(strcmp(scriptname, "") != 0) + &&(strcmp(scriptname, "none") != 0)) { + if (execute_script(scriptname, intname) < 0) + BX_ERROR (("execute script '%s' on %s failed", scriptname, intname)); + } + + // Start the rx poll + this->rx_timer_index = + bx_pc_system.register_timer(this, this->rx_timer_handler, 1000, + 1, 1, "eth_tap"); // continuous, active + this->rxh = rxh; + this->rxarg = rxarg; +#if BX_ETH_TAP_LOGGING + // eventually Bryce wants txlog to dump in pcap format so that + // tcpdump -r FILE can read it and interpret packets. + txlog = fopen ("ne2k-tx.log", "wb"); + if (!txlog) BX_PANIC (("open ne2k-tx.log failed")); + txlog_txt = fopen ("ne2k-txdump.txt", "wb"); + if (!txlog_txt) BX_PANIC (("open ne2k-txdump.txt failed")); + fprintf (txlog_txt, "tap packetmover readable log file\n"); + fprintf (txlog_txt, "net IF = %s\n", netif); + fprintf (txlog_txt, "MAC address = "); + for (int i=0; i<6; i++) + fprintf (txlog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : ""); + fprintf (txlog_txt, "\n--\n"); + fflush (txlog_txt); + + rxlog = fopen ("ne2k-rx.log", "wb"); + if (!rxlog) BX_PANIC (("open ne2k-rx.log failed")); + rxlog_txt = fopen ("ne2k-rxdump.txt", "wb"); + if (!rxlog_txt) BX_PANIC (("open ne2k-rxdump.txt failed")); + fprintf (rxlog_txt, "tap packetmover readable log file\n"); + fprintf (rxlog_txt, "net IF = %s\n", netif); + fprintf (rxlog_txt, "MAC address = "); + for (int i=0; i<6; i++) + fprintf (rxlog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : ""); + fprintf (rxlog_txt, "\n--\n"); + fflush (rxlog_txt); + +#endif +} + +void +bx_tap_pktmover_c::sendpkt(void *buf, unsigned io_len) +{ + Bit8u txbuf[BX_PACKET_BUFSIZ]; + txbuf[0] = 0; + txbuf[1] = 0; +#if defined(__FreeBSD__) || defined(__APPLE__) // Should be fixed for other *BSD + memcpy (txbuf, buf, io_len); + unsigned int size = write (fd, txbuf, io_len); + if (size != io_len) { +#else + memcpy (txbuf+2, buf, io_len); + unsigned int size = write (fd, txbuf, io_len+2); + if (size != io_len+2) { +#endif + BX_PANIC (("write on tap device: %s", strerror (errno))); + } else { + BX_INFO (("wrote %d bytes + 2 byte pad on tap", io_len)); + } +#if BX_ETH_TAP_LOGGING + BX_DEBUG (("sendpkt length %u", io_len)); + // dump raw bytes to a file, eventually dump in pcap format so that + // tcpdump -r FILE can interpret them for us. + int n = fwrite (buf, io_len, 1, txlog); + if (n != 1) BX_ERROR (("fwrite to txlog failed, io_len = %u", io_len)); + // dump packet in hex into an ascii log file + fprintf (txlog_txt, "NE2K transmitting a packet, length %u\n", io_len); + Bit8u *charbuf = (Bit8u *)buf; + for (n=0; n<(int)io_len; n++) { + if (((n % 16) == 0) && n>0) + fprintf (txlog_txt, "\n"); + fprintf (txlog_txt, "%02x ", charbuf[n]); + } + fprintf (txlog_txt, "\n--\n"); + // flush log so that we see the packets as they arrive w/o buffering + fflush (txlog); + fflush (txlog_txt); +#endif +} + +void bx_tap_pktmover_c::rx_timer_handler (void *this_ptr) +{ + bx_tap_pktmover_c *class_ptr = (bx_tap_pktmover_c *) this_ptr; + class_ptr->rx_timer(); +} + +void bx_tap_pktmover_c::rx_timer () +{ + int nbytes; + Bit8u buf[BX_PACKET_BUFSIZ]; + Bit8u *rxbuf; + if (fd<0) return; + nbytes = read (fd, buf, sizeof(buf)); + + // hack: discard first two bytes +#if defined(__FreeBSD__) || defined(__APPLE__) // Should be fixed for other *BSD + rxbuf = buf; +#else + rxbuf = buf+2; + nbytes-=2; +#endif + + // hack: TAP device likes to create an ethernet header which has + // the same source and destination address FE:FD:00:00:00:00. + // Change the dest address to FE:FD:00:00:00:01. +#if defined(__linux__) + rxbuf[5] = 1; +#endif + + if (nbytes>0) + BX_INFO (("tap read returned %d bytes", nbytes)); + if (nbytes<0) { + if (errno != EAGAIN) + BX_ERROR (("tap read error: %s", strerror(errno))); + return; + } +#if BX_ETH_TAP_LOGGING + if (nbytes > 0) { + BX_DEBUG (("receive packet length %u", nbytes)); + // dump raw bytes to a file, eventually dump in pcap format so that + // tcpdump -r FILE can interpret them for us. + int n = fwrite (rxbuf, nbytes, 1, rxlog); + if (n != 1) BX_ERROR (("fwrite to rxlog failed, nbytes = %d", nbytes)); + // dump packet in hex into an ascii log file + fprintf (rxlog_txt, "NE2K received a packet, length %u\n", nbytes); + for (n=0; n<nbytes; n++) { + if (((n % 16) == 0) && n>0) + fprintf (rxlog_txt, "\n"); + fprintf (rxlog_txt, "%02x ", rxbuf[n]); + } + fprintf (rxlog_txt, "\n--\n"); + // flush log so that we see the packets as they arrive w/o buffering + fflush (rxlog); + fflush (rxlog_txt); + } +#endif + BX_DEBUG(("eth_tap: got packet: %d bytes, dst=%x:%x:%x:%x:%x:%x, src=%x:%x:%x:%x:%x:%x\n", nbytes, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3], rxbuf[4], rxbuf[5], rxbuf[6], rxbuf[7], rxbuf[8], rxbuf[9], rxbuf[10], rxbuf[11])); + if (nbytes < 60) { + BX_INFO (("packet too short (%d), padding to 60", nbytes)); + nbytes = 60; + } + (*rxh)(rxarg, rxbuf, nbytes); +} + +#endif /* if BX_NE2K_SUPPORT */ diff --git a/tools/ioemu/iodev/eth_tuntap.cc b/tools/ioemu/iodev/eth_tuntap.cc new file mode 100644 index 0000000000..f910fd55f1 --- /dev/null +++ b/tools/ioemu/iodev/eth_tuntap.cc @@ -0,0 +1,401 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: eth_tuntap.cc,v 1.9 2003/04/26 14:48:45 cbothamy 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 + +// eth_tuntap.cc - TUN/TAP interface by Renzo Davoli <renzo@cs.unibo.it> +// +// WARNING: These instructions were written for ethertap, not TUN/TAP. +// +// Here's how to get this working. On the host machine: +// $ su root +// # /sbin/insmod ethertap +// Using /lib/modules/2.2.14-5.0/net/ethertap.o +// # mknod /dev/tap0 c 36 16 # if not already there +// # /sbin/ifconfig tap0 10.0.0.1 +// # /sbin/route add -host 10.0.0.2 gw 10.0.0.1 +// +// Now you have a tap0 device which you can on the ifconfig output. The +// tap0 interface has the IP address of 10.0.0.1. The bochs machine will have +// the IP address 10.0.0.2. +// +// Compile a bochs version from March 8, 2002 or later with --enable-ne2000. +// Add this ne2k line to your .bochsrc to activate the tap device. +// ne2k: ioaddr=0x280, irq=9, mac=fe:fd:00:00:00:01, ethmod=tap, ethdev=tap0 +// Don't change the mac or ethmod! +// +// Boot up DLX Linux in Bochs. Log in as root and then type the following +// commands to set up networking: +// # ifconfig eth0 10.0.0.2 +// # route add -net 10.0.0.0 +// # route add default gw 10.0.0.1 +// Now you should be able to ping from guest OS to your host machine, if +// you give its IP number. I'm still having trouble with pings from the +// host machine to the guest, so something is still not right. Symptoms: I +// ping from the host to the guest's IP address 10.0.0.2. With tcpdump I can +// see the ping going to Bochs, and then the ping reply coming from Bochs. +// But the ping program itself does not see the responses....well every +// once in a while it does, like 1 in 60 pings. +// +// host$ ping 10.0.0.2 +// PING 10.0.0.2 (10.0.0.2) from 10.0.0.1 : 56(84) bytes of data. +// +// Netstat output: +// 20:29:59.018776 fe:fd:0:0:0:0 fe:fd:0:0:0:1 0800 98: 10.0.0.1 > 10.0.0.2: icmp: echo request +// 4500 0054 2800 0000 4001 3ea7 0a00 0001 +// 0a00 0002 0800 09d3 a53e 0400 9765 893c +// 3949 0000 0809 0a0b 0c0d 0e0f 1011 1213 +// 1415 1617 1819 +// 20:29:59.023017 fe:fd:0:0:0:1 fe:fd:0:0:0:0 0800 98: 10.0.0.2 > 10.0.0.1: icmp: echo reply +// 4500 0054 004a 0000 4001 665d 0a00 0002 +// 0a00 0001 0000 11d3 a53e 0400 9765 893c +// 3949 0000 0809 0a0b 0c0d 0e0f 1011 1213 +// 1415 1617 1819 +// +// I suspect it may be related to the fact that ping 10.0.0.1 from the +// host also doesn't work. Why wouldn't the host respond to its own IP +// address on the tap0 device? +// +// Theoretically, if you set up packet forwarding (with masquerading) on the +// host, you should be able to get Bochs talking to anyone on the internet. +// + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#if BX_NE2K_SUPPORT + +#define LOG_THIS bx_devices.pluginNE2kDevice-> + +#include <signal.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <asm/types.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/wait.h> +#include <linux/netlink.h> +#include <linux/if.h> +#include <linux/if_tun.h> +#include <assert.h> +#include <fcntl.h> +#include <errno.h> + +#define TUNTAP_VIRTUAL_HW_ADDR 0xDEADBEEF +#define BX_ETH_TUNTAP_LOGGING 0 +#define BX_PACKET_BUFSIZ 2048 // Enough for an ether frame + +int tun_alloc(char *dev); + +// +// Define the class. This is private to this module +// +class bx_tuntap_pktmover_c : public eth_pktmover_c { +public: + bx_tuntap_pktmover_c(const char *netif, const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg); + void sendpkt(void *buf, unsigned io_len); +private: + int fd; + int rx_timer_index; + static void rx_timer_handler(void *); + void rx_timer (); + FILE *txlog, *txlog_txt, *rxlog, *rxlog_txt; +}; + + +// +// Define the static class that registers the derived pktmover class, +// and allocates one on request. +// +class bx_tuntap_locator_c : public eth_locator_c { +public: + bx_tuntap_locator_c(void) : eth_locator_c("tuntap") {} +protected: + eth_pktmover_c *allocate(const char *netif, const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg) { + return (new bx_tuntap_pktmover_c(netif, macaddr, rxh, rxarg)); + } +} bx_tuntap_match; + + +// +// Define the methods for the bx_tuntap_pktmover derived class +// + +// the constructor +bx_tuntap_pktmover_c::bx_tuntap_pktmover_c(const char *netif, + const char *macaddr, + eth_rx_handler_t rxh, + void *rxarg) +{ + int flags; + if (strncmp (netif, "tun", 3) != 0) { + BX_PANIC (("eth_tuntap: interface name (%s) must be tun", netif)); + } +#ifdef NEVERDEF + char filename[BX_PATHNAME_LEN]; + sprintf (filename, "/dev/net/%s", netif); + + // check if the TUN/TAP devices is running, and turn on ARP. This is based + // on code from the Mac-On-Linux project. http://http://www.maconlinux.org/ + int sock = socket( AF_INET, SOCK_DGRAM, 0 ); + if (sock < 0) { + BX_PANIC (("socket creation: %s", strerror(errno))); + return; + } + struct ifreq ifr; + memset( &ifr, 0, sizeof(ifr) ); + strncpy( ifr.ifr_name, netif, sizeof(ifr.ifr_name) ); + if( ioctl( sock, SIOCGIFFLAGS, &ifr ) < 0 ){ + BX_PANIC (("SIOCGIFFLAGS on %s: %s", netif, strerror (errno))); + close(sock); + return; + } + if( !(ifr.ifr_flags & IFF_RUNNING ) ){ + BX_PANIC (("%s device is not running", netif)); + close(sock); + return; + } + if( (ifr.ifr_flags & IFF_NOARP ) ){ + BX_INFO (("turn on ARP for %s device", netif)); + ifr.ifr_flags &= ~IFF_NOARP; + if( ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0 ) { + BX_PANIC (("SIOCSIFFLAGS: %s", strerror(errno))); + close(sock); + return; + } + } + close(sock); + + fd = open (filename, O_RDWR); +#endif + char intname[IFNAMSIZ]; + strcpy(intname,netif); + fd=tun_alloc(intname); + if (fd < 0) { + BX_PANIC (("open failed on %s: %s", netif, strerror (errno))); + return; + } + + /* set O_ASYNC flag so that we can poll with read() */ + if ((flags = fcntl( fd, F_GETFL)) < 0) { + BX_PANIC (("getflags on tun device: %s", strerror (errno))); + } + flags |= O_NONBLOCK; + if (fcntl( fd, F_SETFL, flags ) < 0) { + BX_PANIC (("set tun device flags: %s", strerror (errno))); + } + + BX_INFO (("eth_tuntap: opened %s device", netif)); + + /* Execute the configuration script */ + char *scriptname=bx_options.ne2k.Oscript->getptr(); + if((scriptname != NULL) + &&(strcmp(scriptname, "") != 0) + &&(strcmp(scriptname, "none") != 0)) { + if (execute_script(scriptname, intname) < 0) + BX_ERROR (("execute script '%s' on %s failed", scriptname, intname)); + } + + // Start the rx poll + this->rx_timer_index = + bx_pc_system.register_timer(this, this->rx_timer_handler, 1000, + 1, 1, "eth_tuntap"); // continuous, active + this->rxh = rxh; + this->rxarg = rxarg; +#if BX_ETH_TUNTAP_LOGGING + // eventually Bryce wants txlog to dump in pcap format so that + // tcpdump -r FILE can read it and interpret packets. + txlog = fopen ("ne2k-tx.log", "wb"); + if (!txlog) BX_PANIC (("open ne2k-tx.log failed")); + txlog_txt = fopen ("ne2k-txdump.txt", "wb"); + if (!txlog_txt) BX_PANIC (("open ne2k-txdump.txt failed")); + fprintf (txlog_txt, "tuntap packetmover readable log file\n"); + fprintf (txlog_txt, "net IF = %s\n", netif); + fprintf (txlog_txt, "MAC address = "); + for (int i=0; i<6; i++) + fprintf (txlog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : ""); + fprintf (txlog_txt, "\n--\n"); + fflush (txlog_txt); + + rxlog = fopen ("ne2k-rx.log", "wb"); + if (!rxlog) BX_PANIC (("open ne2k-rx.log failed")); + rxlog_txt = fopen ("ne2k-rxdump.txt", "wb"); + if (!rxlog_txt) BX_PANIC (("open ne2k-rxdump.txt failed")); + fprintf (rxlog_txt, "tuntap packetmover readable log file\n"); + fprintf (rxlog_txt, "net IF = %s\n", netif); + fprintf (rxlog_txt, "MAC address = "); + for (int i=0; i<6; i++) + fprintf (rxlog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : ""); + fprintf (rxlog_txt, "\n--\n"); + fflush (rxlog_txt); + +#endif +} + +void +bx_tuntap_pktmover_c::sendpkt(void *buf, unsigned io_len) +{ +#ifdef NEVERDEF + Bit8u txbuf[BX_PACKET_BUFSIZ]; + txbuf[0] = 0; + txbuf[1] = 0; + memcpy (txbuf+2, buf, io_len); + unsigned int size = write (fd, txbuf, io_len+2); + if (size != io_len+2) { + BX_PANIC (("write on tuntap device: %s", strerror (errno))); + } else { + BX_INFO (("wrote %d bytes + 2 byte pad on tuntap", io_len)); + } +#endif + unsigned int size = write (fd, buf, io_len); + if (size != io_len) { + BX_PANIC (("write on tuntap device: %s", strerror (errno))); + } else { + BX_INFO (("wrote %d bytes on tuntap", io_len)); + } +#if BX_ETH_TUNTAP_LOGGING + BX_DEBUG (("sendpkt length %u", io_len)); + // dump raw bytes to a file, eventually dump in pcap format so that + // tcpdump -r FILE can interpret them for us. + int n = fwrite (buf, io_len, 1, txlog); + if (n != 1) BX_ERROR (("fwrite to txlog failed", io_len)); + // dump packet in hex into an ascii log file + fprintf (txlog_txt, "NE2K transmitting a packet, length %u\n", io_len); + Bit8u *charbuf = (Bit8u *)buf; + for (n=0; n<io_len; n++) { + if (((n % 16) == 0) && n>0) + fprintf (txlog_txt, "\n"); + fprintf (txlog_txt, "%02x ", charbuf[n]); + } + fprintf (txlog_txt, "\n--\n"); + // flush log so that we see the packets as they arrive w/o buffering + fflush (txlog); + fflush (txlog_txt); +#endif +} + +void bx_tuntap_pktmover_c::rx_timer_handler (void *this_ptr) +{ + bx_tuntap_pktmover_c *class_ptr = (bx_tuntap_pktmover_c *) this_ptr; + class_ptr->rx_timer(); +} + +void bx_tuntap_pktmover_c::rx_timer () +{ + int nbytes; + Bit8u buf[BX_PACKET_BUFSIZ]; + Bit8u *rxbuf; + if (fd<0) return; + nbytes = read (fd, buf, sizeof(buf)); + +#ifdef NEVERDEF + // hack: discard first two bytes + rxbuf = buf+2; + nbytes-=2; +#else + rxbuf=buf; +#endif + + // hack: TUN/TAP device likes to create an ethernet header which has + // the same source and destination address FE:FD:00:00:00:00. + // Change the dest address to FE:FD:00:00:00:01. + rxbuf[5] = 1; + + if (nbytes>0) + BX_INFO (("tuntap read returned %d bytes", nbytes)); + if (nbytes<0) { + if (errno != EAGAIN) + BX_ERROR (("tuntap read error: %s", strerror(errno))); + return; + } +#if BX_ETH_TUNTAP_LOGGING + if (nbytes > 0) { + BX_DEBUG (("receive packet length %u", nbytes)); + // dump raw bytes to a file, eventually dump in pcap format so that + // tcpdump -r FILE can interpret them for us. + int n = fwrite (rxbuf, nbytes, 1, rxlog); + if (n != 1) BX_ERROR (("fwrite to rxlog failed", nbytes)); + // dump packet in hex into an ascii log file + fprintf (rxlog_txt, "NE2K received a packet, length %u\n", nbytes); + for (n=0; n<nbytes; n++) { + if (((n % 16) == 0) && n>0) + fprintf (rxlog_txt, "\n"); + fprintf (rxlog_txt, "%02x ", rxbuf[n]); + } + fprintf (rxlog_txt, "\n--\n"); + // flush log so that we see the packets as they arrive w/o buffering + fflush (rxlog); + fflush (rxlog_txt); + } +#endif + BX_DEBUG(("eth_tuntap: got packet: %d bytes, dst=%x:%x:%x:%x:%x:%x, src=%x:%x:%x:%x:%x:%x\n", nbytes, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3], rxbuf[4], rxbuf[5], rxbuf[6], rxbuf[7], rxbuf[8], rxbuf[9], rxbuf[10], rxbuf[11])); + if (nbytes < 60) { + BX_INFO (("packet too short (%d), padding to 60", nbytes)); + nbytes = 60; + } + (*rxh)(rxarg, rxbuf, nbytes); +} + + + int tun_alloc(char *dev) + { + struct ifreq ifr; + int fd, err; + + if( (fd = open("/dev/net/tun", O_RDWR)) < 0 ) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + + /* Flags: IFF_TUN - TUN device (no Ethernet headers) + * IFF_TAP - TAP device + * + * IFF_NO_PI - Do not provide packet information + */ + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + if( *dev ) + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + + if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){ + close(fd); + return err; + } + + //strcpy(dev, ifr.ifr_name); + ioctl( fd, TUNSETNOCSUM, 1 ); + + return fd; + } + +#endif /* if BX_NE2K_SUPPORT */ diff --git a/tools/ioemu/iodev/extfpuirq.cc b/tools/ioemu/iodev/extfpuirq.cc new file mode 100644 index 0000000000..5d1ed986bf --- /dev/null +++ b/tools/ioemu/iodev/extfpuirq.cc @@ -0,0 +1,107 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: extfpuirq.cc,v 1.5 2003/07/31 12:04:48 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + +// +// External circuit for MSDOS compatible FPU exceptions +// + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" + +#define LOG_THIS theExternalFpuIrq-> + +bx_extfpuirq_c *theExternalFpuIrq = NULL; + + int +libextfpuirq_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + theExternalFpuIrq = new bx_extfpuirq_c (); + bx_devices.pluginExtFpuIrq = theExternalFpuIrq; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theExternalFpuIrq, BX_PLUGIN_EXTFPUIRQ); + return(0); // Success +} + + void +libextfpuirq_LTX_plugin_fini(void) +{ +} + +bx_extfpuirq_c::bx_extfpuirq_c(void) +{ + put("EFIRQ"); + settype(EXTFPUIRQLOG); +} + +bx_extfpuirq_c::~bx_extfpuirq_c(void) +{ + // nothing for now + BX_DEBUG(("Exit.")); +} + + + void +bx_extfpuirq_c::init(void) +{ + // called once when bochs initializes + DEV_register_iowrite_handler(this, write_handler, 0x00F0, "External FPU IRQ", 1); + DEV_register_irq(13, "External FPU IRQ"); +} + + void +bx_extfpuirq_c::reset(unsigned type) +{ + // We should handle IGNNE here + DEV_pic_lower_irq(13); +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_extfpuirq_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_EFI_SMF + bx_extfpuirq_c *class_ptr = (bx_extfpuirq_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + void +bx_extfpuirq_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_EFI_SMF + + // We should handle IGNNE here + DEV_pic_lower_irq(13); +} + diff --git a/tools/ioemu/iodev/extfpuirq.h b/tools/ioemu/iodev/extfpuirq.h new file mode 100644 index 0000000000..c32e71aa93 --- /dev/null +++ b/tools/ioemu/iodev/extfpuirq.h @@ -0,0 +1,51 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: extfpuirq.h,v 1.2 2003/01/07 08:17:15 cbothamy Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + +#if BX_USE_EFI_SMF +# define BX_EXTFPUIRQ_SMF static +# define BX_EXTFPUIRQ_THIS theExternalFpuIrq-> +#else +# define BX_EXTFPUIRQ_SMF +# define BX_EXTFPUIRQ_THIS this-> +#endif + + +class bx_extfpuirq_c : public bx_devmodel_c { + +public: + bx_extfpuirq_c(void); + ~bx_extfpuirq_c(void); + virtual void init(void); + virtual void reset(unsigned type); + +private: + + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#if !BX_USE_EFI_SMF + void write(Bit32u address, Bit32u value, unsigned io_len); +#endif + }; diff --git a/tools/ioemu/iodev/floppy.cc b/tools/ioemu/iodev/floppy.cc new file mode 100644 index 0000000000..e4090d57b9 --- /dev/null +++ b/tools/ioemu/iodev/floppy.cc @@ -0,0 +1,1633 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: floppy.cc,v 1.69 2003/12/18 20:04:49 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 +// +// +// Floppy Disk Controller Docs: +// Intel 82077A Data sheet +// ftp://void-core.2y.net/pub/docs/fdc/82077AA_FloppyControllerDatasheet.pdf +// Intel 82078 Data sheet +// ftp://download.intel.com/design/periphrl/datashts/29047403.PDF +// Other FDC references +// http://debs.future.easyspace.com/Programming/Hardware/FDC/floppy.html +// And a port list: +// http://mudlist.eorbit.net/~adam/pickey/ports.html +// + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + + +extern "C" { +#include <errno.h> +} + +#ifdef __linux__ +extern "C" { +#include <sys/ioctl.h> +#include <linux/fd.h> +} +#endif +#include "bochs.h" +// windows.h included by bochs.h +#ifdef WIN32 +extern "C" { +#include <winioctl.h> +} +#endif +#define LOG_THIS theFloppyController-> + +bx_floppy_ctrl_c *theFloppyController; + +/* for main status register */ +#define FD_MS_MRQ 0x80 +#define FD_MS_DIO 0x40 +#define FD_MS_NDMA 0x20 +#define FD_MS_BUSY 0x10 +#define FD_MS_ACTD 0x08 +#define FD_MS_ACTC 0x04 +#define FD_MS_ACTB 0x02 +#define FD_MS_ACTA 0x01 + +#define FROM_FLOPPY 10 +#define TO_FLOPPY 11 + +#define FLOPPY_DMA_CHAN 2 + +typedef struct { + unsigned id; + Bit8u trk; + Bit8u hd; + Bit8u spt; + unsigned sectors; +} floppy_type_t; + +static floppy_type_t floppy_type[8] = { + {BX_FLOPPY_160K, 40, 1, 8, 320}, + {BX_FLOPPY_180K, 40, 1, 9, 360}, + {BX_FLOPPY_320K, 40, 2, 8, 640}, + {BX_FLOPPY_360K, 40, 2, 9, 720}, + {BX_FLOPPY_720K, 80, 2, 9, 1440}, + {BX_FLOPPY_1_2, 80, 2, 15, 2400}, + {BX_FLOPPY_1_44, 80, 2, 18, 2880}, + {BX_FLOPPY_2_88, 80, 2, 36, 5760} +}; + + + int +libfloppy_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + theFloppyController = new bx_floppy_ctrl_c (); + bx_devices.pluginFloppyDevice = theFloppyController; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theFloppyController, BX_PLUGIN_FLOPPY); + return(0); // Success +} + + void +libfloppy_LTX_plugin_fini(void) +{ +} + + +bx_floppy_ctrl_c::bx_floppy_ctrl_c(void) +{ + put("FDD"); + settype(FDLOG); + s.floppy_timer_index = BX_NULL_TIMER_HANDLE; +} + +bx_floppy_ctrl_c::~bx_floppy_ctrl_c(void) +{ + // nothing for now + BX_DEBUG(("Exit.")); +} + + + void +bx_floppy_ctrl_c::init(void) +{ + Bit8u i; + + BX_DEBUG(("Init $Id: floppy.cc,v 1.69 2003/12/18 20:04:49 vruppert Exp $")); + DEV_dma_register_8bit_channel(2, dma_read, dma_write, "Floppy Drive"); + DEV_register_irq(6, "Floppy Drive"); + for (unsigned addr=0x03F2; addr<=0x03F7; addr++) { + DEV_register_ioread_handler(this, read_handler, addr, "Floppy Drive", 1); + DEV_register_iowrite_handler(this, write_handler, addr, "Floppy Drive", 1); + } + + + DEV_cmos_set_reg(0x10, 0x00); /* start out with: no drive 0, no drive 1 */ + + BX_FD_THIS s.num_supported_floppies = 0; + + for (i=0; i<4; i++) { + BX_FD_THIS s.device_type[i] = BX_FLOPPY_NONE; + BX_FD_THIS s.media[i].type = BX_FLOPPY_NONE; + } + + // + // Floppy A setup + // + BX_FD_THIS s.media[0].sectors_per_track = 0; + BX_FD_THIS s.media[0].tracks = 0; + BX_FD_THIS s.media[0].heads = 0; + BX_FD_THIS s.media[0].sectors = 0; + BX_FD_THIS s.media[0].fd = -1; + BX_FD_THIS s.media_present[0] = 0; + BX_FD_THIS s.device_type[0] = bx_options.floppya.Odevtype->get (); + + switch (BX_FD_THIS s.device_type[0]) { + case BX_FLOPPY_NONE: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x00); + break; + case BX_FLOPPY_360K: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x10); + break; + case BX_FLOPPY_1_2: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x20); + break; + case BX_FLOPPY_720K: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x30); + break; + case BX_FLOPPY_1_44: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x40); + break; + case BX_FLOPPY_2_88: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x50); + break; + + // use CMOS reserved types + case BX_FLOPPY_160K: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x60); + BX_INFO(("WARNING: 1st floppy uses of reserved CMOS floppy drive type 6")); + break; + case BX_FLOPPY_180K: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x70); + BX_INFO(("WARNING: 1st floppy uses of reserved CMOS floppy drive type 7")); + break; + case BX_FLOPPY_320K: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x80); + BX_INFO(("WARNING: 1st floppy uses of reserved CMOS floppy drive type 8")); + break; + + default: + BX_PANIC(("unknown floppya type")); + } + if (BX_FD_THIS s.device_type[0] != BX_FLOPPY_NONE) + BX_FD_THIS s.num_supported_floppies++; + + if (bx_options.floppya.Otype->get () != BX_FLOPPY_NONE) { + if ( bx_options.floppya.Ostatus->get () == BX_INSERTED) { + if (evaluate_media(bx_options.floppya.Otype->get (), bx_options.floppya.Opath->getptr (), + & BX_FD_THIS s.media[0])) + BX_FD_THIS s.media_present[0] = 1; + else + bx_options.floppya.Ostatus->set(BX_EJECTED); +#define MED (BX_FD_THIS s.media[0]) + BX_INFO(("fd0: '%s' ro=%d, h=%d,t=%d,spt=%d", bx_options.floppya.Opath->getptr(), + MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track)); +#undef MED + } + } + + + // + // Floppy B setup + // + BX_FD_THIS s.media[1].sectors_per_track = 0; + BX_FD_THIS s.media[1].tracks = 0; + BX_FD_THIS s.media[1].heads = 0; + BX_FD_THIS s.media[1].sectors = 0; + BX_FD_THIS s.media[1].fd = -1; + BX_FD_THIS s.media_present[1] = 0; + BX_FD_THIS s.device_type[1] = bx_options.floppyb.Odevtype->get (); + + switch (BX_FD_THIS s.device_type[1]) { + case BX_FLOPPY_NONE: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x00); + break; + case BX_FLOPPY_360K: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x01); + break; + case BX_FLOPPY_1_2: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x02); + break; + case BX_FLOPPY_720K: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x03); + break; + case BX_FLOPPY_1_44: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x04); + break; + case BX_FLOPPY_2_88: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x05); + break; + + // use CMOS reserved types + case BX_FLOPPY_160K: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x06); + BX_INFO(("WARNING: 2nd floppy uses of reserved CMOS floppy drive type 6")); + break; + case BX_FLOPPY_180K: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x07); + BX_INFO(("WARNING: 2nd floppy uses of reserved CMOS floppy drive type 7")); + break; + case BX_FLOPPY_320K: + DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x08); + BX_INFO(("WARNING: 2nd floppy uses of reserved CMOS floppy drive type 8")); + break; + + default: + BX_PANIC(("unknown floppyb type")); + } + if (BX_FD_THIS s.device_type[1] != BX_FLOPPY_NONE) + BX_FD_THIS s.num_supported_floppies++; + + if (bx_options.floppyb.Otype->get () != BX_FLOPPY_NONE) { + if ( bx_options.floppyb.Ostatus->get () == BX_INSERTED) { + if (evaluate_media(bx_options.floppyb.Otype->get (), bx_options.floppyb.Opath->getptr (), + & BX_FD_THIS s.media[1])) + BX_FD_THIS s.media_present[1] = 1; + else + bx_options.floppyb.Ostatus->set(BX_EJECTED); +#define MED (BX_FD_THIS s.media[1]) + BX_INFO(("fd1: '%s' ro=%d, h=%d,t=%d,spt=%d", bx_options.floppyb.Opath->getptr(), + MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track)); +#undef MED + } + } + + + + /* CMOS Equipment Byte register */ + if (BX_FD_THIS s.num_supported_floppies > 0) { + DEV_cmos_set_reg(0x14, (DEV_cmos_get_reg(0x14) & 0x3e) | + ((BX_FD_THIS s.num_supported_floppies-1) << 6) | 1); + } + else + DEV_cmos_set_reg(0x14, (DEV_cmos_get_reg(0x14) & 0x3e)); + + + if (BX_FD_THIS s.floppy_timer_index == BX_NULL_TIMER_HANDLE) { + BX_FD_THIS s.floppy_timer_index = + bx_pc_system.register_timer( this, timer_handler, + bx_options.Ofloppy_command_delay->get (), 0,0, "floppy"); + } + + BX_DEBUG(("bx_options.Ofloppy_command_delay = %u", + (unsigned) bx_options.Ofloppy_command_delay->get ())); +} + + + + void +bx_floppy_ctrl_c::reset(unsigned type) +{ + Bit32u i; + + BX_FD_THIS s.pending_irq = 0; + BX_FD_THIS s.reset_sensei = 0; /* no reset result present */ + + BX_FD_THIS s.main_status_reg = 0; + BX_FD_THIS s.status_reg0 = 0; + BX_FD_THIS s.status_reg1 = 0; + BX_FD_THIS s.status_reg2 = 0; + BX_FD_THIS s.status_reg3 = 0; + + // software reset (via DOR port 0x3f2 bit 2) does not change DOR + if (type == BX_RESET_HARDWARE) { + BX_FD_THIS s.DOR = 0x0c; + // motor off, drive 3..0 + // DMA/INT enabled + // normal operation + // drive select 0 + + // DIR and CCR affected only by hard reset + for (i=0; i<4; i++) { + BX_FD_THIS s.DIR[i] |= 0x80; // disk changed + } + BX_FD_THIS s.data_rate = 0; /* 500 Kbps */ + } + + for (i=0; i<4; i++) { + BX_FD_THIS s.cylinder[i] = 0; + BX_FD_THIS s.head[i] = 0; + BX_FD_THIS s.sector[i] = 0; + } + + DEV_pic_lower_irq(6); + DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0); + enter_idle_phase(); +} + + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_floppy_ctrl_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_FD_SMF + bx_floppy_ctrl_c *class_ptr = (bx_floppy_ctrl_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + /* reads from the floppy io ports */ + Bit32u +bx_floppy_ctrl_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_FD_SMF + Bit8u status, value; + + if (bx_dbg.floppy) + BX_INFO(("read access to port %04x", (unsigned) address)); + + switch (address) { +#if BX_DMA_FLOPPY_IO + case 0x3F2: // diskette controller digital output register + value = BX_FD_THIS s.DOR; + return(value); + break; + + case 0x3F4: /* diskette controller main status register */ + status = BX_FD_THIS s.main_status_reg; + return(status); + break; + + case 0x3F5: /* diskette controller data */ + if (BX_FD_THIS s.result_size == 0) { + BX_ERROR(("port 0x3f5: no results to read")); + BX_FD_THIS s.main_status_reg = 0; + return BX_FD_THIS s.result[0]; + } + + value = BX_FD_THIS s.result[BX_FD_THIS s.result_index++]; + BX_FD_THIS s.main_status_reg &= 0xF0; + if (BX_FD_THIS s.result_index >= BX_FD_THIS s.result_size) { + if (!BX_FD_THIS s.reset_sensei) BX_FD_THIS s.pending_irq = 0; + DEV_pic_lower_irq(6); + enter_idle_phase(); + } + return(value); + break; +#endif // #if BX_DMA_FLOPPY_IO + + case 0x3F3: // Tape Drive Register + // see http://www.smsc.com/main/datasheets/37c93x.pdf page 18 for more details + + switch( BX_FD_THIS s.DOR & 0x03 ) + { + case 0x00: + if( (BX_FD_THIS s.DOR & 0x10) == 0) break; + return(2); + case 0x01: + if( (BX_FD_THIS s.DOR & 0x20) == 0) break; + return(1); + } + return(3); + + case 0x3F6: // Reserved for future floppy controllers + // This address shared with the hard drive controller + value = DEV_hd_read_handler(bx_devices.pluginHardDrive, address, io_len); + return( value ); + break; + + case 0x3F7: // diskette controller digital input register + // This address shared with the hard drive controller: + // Bit 7 : floppy + // Bits 6..0: hard drive + value = DEV_hd_read_handler(bx_devices.pluginHardDrive, address, io_len); + value &= 0x7f; + // add in diskette change line + value |= (BX_FD_THIS s.DIR[BX_FD_THIS s.DOR & 0x03] & 0x80); + return( value ); + break; + default: + BX_ERROR(("io_read: unsupported address 0x%04x", (unsigned) address)); + return(0); + break; + } +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_floppy_ctrl_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_FD_SMF + bx_floppy_ctrl_c *class_ptr = (bx_floppy_ctrl_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + /* writes to the floppy io ports */ + void +bx_floppy_ctrl_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_FD_SMF + Bit8u dma_and_interrupt_enable; + Bit8u normal_operation, prev_normal_operation; + Bit8u drive_select; + Bit8u motor_on_drive0, motor_on_drive1; + + if (bx_dbg.floppy) + BX_INFO(("write access to port %04x, value=%02x", + (unsigned) address, (unsigned) value)); + + switch (address) { +#if BX_DMA_FLOPPY_IO + case 0x3F2: /* diskette controller digital output register */ + motor_on_drive1 = value & 0x20; + motor_on_drive0 = value & 0x10; + dma_and_interrupt_enable = value & 0x08; + if (!dma_and_interrupt_enable) + BX_DEBUG(("DMA and interrupt capabilities disabled")); + normal_operation = value & 0x04; + drive_select = value & 0x03; + + prev_normal_operation = BX_FD_THIS s.DOR & 0x04; + BX_FD_THIS s.DOR = value; + + if (prev_normal_operation==0 && normal_operation) { + // transition from RESET to NORMAL + bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index, + bx_options.Ofloppy_command_delay->get (), 0 ); + } + else if (prev_normal_operation && normal_operation==0) { + // transition from NORMAL to RESET + BX_FD_THIS s.main_status_reg = FD_MS_BUSY; + BX_FD_THIS s.pending_command = 0xfe; // RESET pending + + } + BX_DEBUG(("io_write: digital output register")); + BX_DEBUG((" motor on, drive1 = %d", motor_on_drive1 > 0)); + BX_DEBUG((" motor on, drive0 = %d", motor_on_drive0 > 0)); + BX_DEBUG((" dma_and_interrupt_enable=%02x", + (unsigned) dma_and_interrupt_enable)); + BX_DEBUG((" normal_operation=%02x", + (unsigned) normal_operation)); + BX_DEBUG((" drive_select=%02x", + (unsigned) drive_select)); + if (BX_FD_THIS s.device_type[drive_select] == BX_FLOPPY_NONE) { + BX_DEBUG(("WARNING: not existing drive selected")); + } + break; + + case 0x3f4: /* diskette controller data rate select register */ + BX_ERROR(("io_write: data rate select register unsupported")); + break; + + case 0x3F5: /* diskette controller data */ + BX_DEBUG(("command = %02x", (unsigned) value)); + if (BX_FD_THIS s.command_complete) { + if (BX_FD_THIS s.pending_command!=0) + BX_PANIC(("io: 3f5: receiving new comm, old one (%02x) pending", + (unsigned) BX_FD_THIS s.pending_command)); + BX_FD_THIS s.command[0] = value; + BX_FD_THIS s.command_complete = 0; + BX_FD_THIS s.command_index = 1; + /* read/write command in progress */ + BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_BUSY; + switch (value) { + case 0x03: /* specify */ + BX_FD_THIS s.command_size = 3; + break; + case 0x04: // get status + BX_FD_THIS s.command_size = 2; + break; + case 0x07: /* recalibrate */ + BX_FD_THIS s.command_size = 2; + break; + case 0x08: /* sense interrupt status */ + BX_FD_THIS s.command_size = 1; + break; + case 0x0f: /* seek */ + BX_FD_THIS s.command_size = 3; + break; + case 0x4a: /* read ID */ + BX_FD_THIS s.command_size = 2; + break; + case 0x4d: /* format track */ + BX_FD_THIS s.command_size = 6; + break; + case 0x45: + case 0xc5: /* write normal data */ + BX_FD_THIS s.command_size = 9; + break; + case 0x46: + case 0x66: + case 0xc6: + case 0xe6: /* read normal data */ + BX_FD_THIS s.command_size = 9; + break; + + case 0x13: // Configure command (Enhanced) + BX_FD_THIS s.command_size = 4; + break; + + case 0x0e: // dump registers (Enhanced drives) + case 0x10: // Version command, standard controller returns 80h + case 0x18: // National Semiconductor version command; return 80h + // These commands are not implemented on the standard + // controller and return an error. They are available on + // the enhanced controller. + BX_DEBUG(("io_write: 0x3f5: unsupported floppy command 0x%02x", + (unsigned) value)); + BX_FD_THIS s.command_size = 0; // make sure we don't try to process this command + BX_FD_THIS s.status_reg0 = 0x80; // status: invalid command + enter_result_phase(); + break; + + default: + BX_ERROR(("io_write: 0x3f5: invalid floppy command 0x%02x", + (unsigned) value)); + BX_FD_THIS s.command_size = 0; // make sure we don't try to process this command + BX_FD_THIS s.status_reg0 = 0x80; // status: invalid command + enter_result_phase(); + break; + } + } + else { + BX_FD_THIS s.command[BX_FD_THIS s.command_index++] = + value; + } + if (BX_FD_THIS s.command_index == + BX_FD_THIS s.command_size) { + /* read/write command not in progress any more */ + floppy_command(); + BX_FD_THIS s.command_complete = 1; + } + BX_DEBUG(("io_write: diskette controller data")); + return; + break; +#endif // #if BX_DMA_FLOPPY_IO + + case 0x3F6: /* diskette controller (reserved) */ + BX_DEBUG(("io_write: reserved register 0x3f6 unsupported")); + // this address shared with the hard drive controller + DEV_hd_write_handler(bx_devices.pluginHardDrive, address, value, io_len); + break; + +#if BX_DMA_FLOPPY_IO + case 0x3F7: /* diskette controller configuration control register */ + BX_DEBUG(("io_write: config control register")); + BX_FD_THIS s.data_rate = value & 0x03; + switch (BX_FD_THIS s.data_rate) { + case 0: BX_DEBUG((" 500 Kbps")); break; + case 1: BX_DEBUG((" 300 Kbps")); break; + case 2: BX_DEBUG((" 250 Kbps")); break; + case 3: BX_DEBUG((" 1 Mbps")); break; + } + return; + break; + + default: + BX_ERROR(("io_write ignored: 0x%04x = 0x%02x", (unsigned) address, (unsigned) value)); + break; +#endif // #if BX_DMA_FLOPPY_IO + } +} + + + + void +bx_floppy_ctrl_c::floppy_command(void) +{ +#if BX_PROVIDE_CPU_MEMORY==0 + BX_PANIC(("floppy_command(): uses DMA: not supported for" + " external environment")); +#else + unsigned i; + Bit8u step_rate_time; + Bit8u head_unload_time; + Bit8u head_load_time; + Bit8u motor_on; + Bit8u head, drive, cylinder, sector, eot; + Bit8u sector_size, data_length; + Bit32u logical_sector; + + + BX_DEBUG(("FLOPPY COMMAND: ")); + for (i=0; i<BX_FD_THIS s.command_size; i++) + BX_DEBUG(("[%02x] ", (unsigned) BX_FD_THIS s.command[i])); + +#if 0 + /* execute phase of command is in progress (non DMA mode) */ + BX_FD_THIS s.main_status_reg |= 20; +#endif + + BX_FD_THIS s.pending_command = BX_FD_THIS s.command[0]; + switch (BX_FD_THIS s.pending_command) { + case 0x03: // specify + // execution: specified parameters are loaded + // result: no result bytes, no interrupt + step_rate_time = BX_FD_THIS s.command[1] >> 4; + head_unload_time = BX_FD_THIS s.command[1] & 0x0f; + head_load_time = BX_FD_THIS s.command[2] >> 1; + if (BX_FD_THIS s.command[2] & 0x01) + BX_ERROR(("non DMA mode selected")); + enter_idle_phase(); + return; + break; + + case 0x04: // get status + drive = (BX_FD_THIS s.command[1] & 0x03); + BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01; + BX_FD_THIS s.status_reg3 = 0x28 | (BX_FD_THIS s.head[drive]<<2) | drive + | (BX_FD_THIS s.media[drive].write_protected ? 0x40 : 0x00); + if (BX_FD_THIS s.cylinder[drive] == 0) BX_FD_THIS s.status_reg3 |= 0x10; + enter_result_phase(); + return; + break; + + case 0x07: // recalibrate + drive = (BX_FD_THIS s.command[1] & 0x03); + BX_FD_THIS s.DOR &= 0xfc; + BX_FD_THIS s.DOR |= drive; + BX_DEBUG(("floppy_command(): recalibrate drive %u", + (unsigned) drive)); + motor_on = ( (BX_FD_THIS s.DOR>>(drive+4)) + & 0x01 ); + if (motor_on == 0) { + BX_INFO(("floppy_command(): recal drive with motor off")); + } + if (drive==0) + BX_FD_THIS s.DOR |= 0x10; // turn on MOTA + else + BX_FD_THIS s.DOR |= 0x20; // turn on MOTB + bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index, + bx_options.Ofloppy_command_delay->get (), 0 ); + /* command head to track 0 + * controller set to non-busy + * error condition noted in Status reg 0's equipment check bit + * seek end bit set to 1 in Status reg 0 regardless of outcome + * The last two are taken care of in timer(). + */ + BX_FD_THIS s.cylinder[drive] = 0; + BX_FD_THIS s.main_status_reg = (1 << drive); + return; + break; + + case 0x08: /* sense interrupt status */ + /* execution: + * get status + * result: + * no interupt + * byte0 = status reg0 + * byte1 = current cylinder number (0 to 79) + */ + drive = BX_FD_THIS s.DOR & 0x03; + if (!BX_FD_THIS s.pending_irq) { + BX_FD_THIS s.status_reg0 = 0x80; + } + else { + if (BX_FD_THIS s.reset_sensei > 0) { + drive = 4 - BX_FD_THIS s.reset_sensei; + BX_FD_THIS s.status_reg0 &= 0xf8; + BX_FD_THIS s.status_reg0 |= (BX_FD_THIS s.head[drive] << 2) | drive; + BX_FD_THIS s.reset_sensei--; + } + } + + BX_DEBUG(("sense interrupt status")); + enter_result_phase(); + return; + break; + + case 0x0f: /* seek */ + /* command: + * byte0 = 0F + * byte1 = drive & head select + * byte2 = cylinder number + * execution: + * postion head over specified cylinder + * result: + * no result bytes, issues an interrupt + */ + drive = BX_FD_THIS s.command[1] & 0x03; + BX_FD_THIS s.DOR &= 0xfc; + BX_FD_THIS s.DOR |= drive; + + BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01; + BX_FD_THIS s.cylinder[drive] = BX_FD_THIS s.command[2]; + /* ??? should also check cylinder validity */ + bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index, + bx_options.Ofloppy_command_delay->get (), 0 ); + /* data reg not ready, drive busy */ + BX_FD_THIS s.main_status_reg = (1 << drive); + return; + break; + + case 0x13: // Configure + BX_DEBUG(("configure (eis = 0x%02x)", BX_FD_THIS s.command[2] & 0x40 )); + BX_DEBUG(("configure (efifo = 0x%02x)", BX_FD_THIS s.command[2] & 0x20 )); + BX_DEBUG(("configure (no poll = 0x%02x)", BX_FD_THIS s.command[2] & 0x10 )); + BX_DEBUG(("configure (fifothr = 0x%02x)", BX_FD_THIS s.command[2] & 0x0f )); + BX_DEBUG(("configure (pretrk = 0x%02x)", BX_FD_THIS s.command[3] )); + enter_idle_phase(); + return; + break; + + case 0x4a: // read ID + drive = BX_FD_THIS s.command[1] & 0x03; + BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01; + BX_FD_THIS s.DOR &= 0xfc; + BX_FD_THIS s.DOR |= drive; + + motor_on = (BX_FD_THIS s.DOR>>(drive+4)) & 0x01; + if (motor_on == 0) { + BX_ERROR(("floppy_command(): 0x4a: motor not on")); + BX_FD_THIS s.main_status_reg = FD_MS_BUSY; + return; + } + if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE) + BX_PANIC(("floppy_command(): read ID: bad drive #%d", drive)); + BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive]<<2) | drive; + bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index, + bx_options.Ofloppy_command_delay->get (), 0 ); + /* data reg not ready, controller busy */ + BX_FD_THIS s.main_status_reg = FD_MS_BUSY; + return; + break; + + case 0x4d: // format track + drive = BX_FD_THIS s.command[1] & 0x03; + BX_FD_THIS s.DOR &= 0xfc; + BX_FD_THIS s.DOR |= drive; + + motor_on = (BX_FD_THIS s.DOR>>(drive+4)) & 0x01; + if (motor_on == 0) + BX_PANIC(("floppy_command(): format track: motor not on")); + BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01; + sector_size = BX_FD_THIS s.command[2]; + BX_FD_THIS s.format_count = BX_FD_THIS s.command[3]; + BX_FD_THIS s.format_fillbyte = BX_FD_THIS s.command[5]; + if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE) + BX_PANIC(("floppy_command(): format track: bad drive #%d", drive)); + + if (sector_size != 0x02) { // 512 bytes + BX_PANIC(("format track: sector size %d not supported", 128<<sector_size)); + } + if (BX_FD_THIS s.format_count != BX_FD_THIS s.media[drive].sectors_per_track) { + BX_PANIC(("format track: %d sectors/track requested (%d expected)", + BX_FD_THIS s.format_count, BX_FD_THIS s.media[drive].sectors_per_track)); + } + if ( BX_FD_THIS s.media_present[drive] == 0 ) { + // media not in drive, return error + BX_INFO(("attempt to format track with media not present")); + BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination + BX_FD_THIS s.status_reg1 = 0x25; // 0010 0101 + BX_FD_THIS s.status_reg2 = 0x31; // 0011 0001 + enter_result_phase(); + return; + } + if (BX_FD_THIS s.media[drive].write_protected) { + // media write-protected, return error + BX_INFO(("attempt to format track with media write-protected")); + BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination + BX_FD_THIS s.status_reg1 = 0x27; // 0010 0111 + BX_FD_THIS s.status_reg2 = 0x31; // 0011 0001 + enter_result_phase(); + return; + } + + /* 4 header bytes per sector are required */ + BX_FD_THIS s.format_count <<= 2; + + DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1); + + /* data reg not ready, controller busy */ + BX_FD_THIS s.main_status_reg = FD_MS_BUSY; + BX_DEBUG(("format track")); + return; + break; + + case 0x46: // read normal data, MT=0, SK=0 + case 0x66: // read normal data, MT=0, SK=1 + case 0xc6: // read normal data, MT=1, SK=0 + case 0xe6: // read normal data, MT=1, SK=1 + case 0x45: // write normal data, MT=0 + case 0xc5: // write normal data, MT=1 + BX_FD_THIS s.multi_track = (BX_FD_THIS s.command[0] >> 7); + if ( (BX_FD_THIS s.DOR & 0x08) == 0 ) + BX_PANIC(("read/write command with DMA and int disabled")); + drive = BX_FD_THIS s.command[1] & 0x03; + BX_FD_THIS s.DOR &= 0xfc; + BX_FD_THIS s.DOR |= drive; + + motor_on = (BX_FD_THIS s.DOR>>(drive+4)) & 0x01; + if (motor_on == 0) + BX_PANIC(("floppy_command(): read/write: motor not on")); + head = BX_FD_THIS s.command[3] & 0x01; + cylinder = BX_FD_THIS s.command[2]; /* 0..79 depending */ + sector = BX_FD_THIS s.command[4]; /* 1..36 depending */ + eot = BX_FD_THIS s.command[6]; /* 1..36 depending */ + sector_size = BX_FD_THIS s.command[5]; + data_length = BX_FD_THIS s.command[8]; + BX_DEBUG(("read/write normal data")); + BX_DEBUG(("BEFORE")); + BX_DEBUG((" drive = %u", (unsigned) drive)); + BX_DEBUG((" head = %u", (unsigned) head)); + BX_DEBUG((" cylinder = %u", (unsigned) cylinder)); + BX_DEBUG((" sector = %u", (unsigned) sector)); + BX_DEBUG((" eot = %u", (unsigned) eot)); + if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE) + BX_PANIC(("floppy_command(): read/write: bad drive #%d", drive)); + + // check that head number in command[1] bit two matches the head + // reported in the head number field. Real floppy drives are + // picky about this, as reported in SF bug #439945, (Floppy drive + // read input error checking). + if (head != ((BX_FD_THIS s.command[1]>>2)&1)) { + BX_ERROR(("head number in command[1] doesn't match head field")); + BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination + BX_FD_THIS s.status_reg1 = 0x04; // 0000 0100 + BX_FD_THIS s.status_reg2 = 0x00; // 0000 0000 + enter_result_phase(); + return; + } + + if ( BX_FD_THIS s.media_present[drive] == 0 ) { + // media not in drive, return error + + BX_INFO(("attempt to read/write sector %u," + " sectors/track=%u with media not present", + (unsigned) sector, + (unsigned) BX_FD_THIS s.media[drive].sectors_per_track)); + BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination + BX_FD_THIS s.status_reg1 = 0x25; // 0010 0101 + BX_FD_THIS s.status_reg2 = 0x31; // 0011 0001 + enter_result_phase(); + return; + } + + if (sector_size != 0x02) { // 512 bytes + BX_PANIC(("read/write command: sector size %d not supported", 128<<sector_size)); + } + if ( cylinder >= BX_FD_THIS s.media[drive].tracks ) { + BX_PANIC(("io: norm r/w parms out of range: sec#%02xh cyl#%02xh eot#%02xh head#%02xh", + (unsigned) sector, (unsigned) cylinder, (unsigned) eot, + (unsigned) head)); + return; + } + + if (sector > BX_FD_THIS s.media[drive].sectors_per_track) { + // requested sector > last sector on track + BX_INFO(("attempt to read/write sector %u," + " sectors/track=%u", (unsigned) sector, + (unsigned) BX_FD_THIS s.media[drive].sectors_per_track)); + // set controller to where drive would have left off + // after it discovered the sector was past EOT + BX_FD_THIS s.cylinder[drive] = cylinder; + BX_FD_THIS s.head[drive] = head; + BX_FD_THIS s.sector[drive] = BX_FD_THIS s.media[drive].sectors_per_track; + + // 0100 0HDD abnormal termination + BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; + // 1000 0101 end of cyl/NDAT/NID + BX_FD_THIS s.status_reg1 = 0x85; + // 0000 0000 + BX_FD_THIS s.status_reg2 = 0x00; + enter_result_phase(); + return; + } + + if (cylinder != BX_FD_THIS s.cylinder[drive]) + BX_DEBUG(("io: cylinder request != current cylinder")); + + // original assumed all floppies had two sides...now it does not *delete this comment line* + logical_sector = (cylinder * BX_FD_THIS s.media[drive].heads * BX_FD_THIS s.media[drive].sectors_per_track) + + (head * BX_FD_THIS s.media[drive].sectors_per_track) + + (sector - 1); + + if (logical_sector >= BX_FD_THIS s.media[drive].sectors) { + BX_PANIC(("io: logical sector out of bounds")); + } + + BX_FD_THIS s.cylinder[drive] = cylinder; + BX_FD_THIS s.sector[drive] = sector; + BX_FD_THIS s.head[drive] = head; + + if ((BX_FD_THIS s.command[0] & 0x4f) == 0x46) { // read + floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer, + 512, FROM_FLOPPY); + + DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1); + + /* data reg not ready, controller busy */ + BX_FD_THIS s.main_status_reg = FD_MS_BUSY; + return; + } + else if ((BX_FD_THIS s.command[0] & 0x7f) == 0x45) { // write + + DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1); + + /* data reg not ready, controller busy */ + BX_FD_THIS s.main_status_reg = FD_MS_BUSY; + return; + } + else + BX_PANIC(("floppy_command(): unknown read/write command")); + + return; + break; + + default: // invalid or unsupported command; these are captured in write() above + BX_PANIC(("You should never get here! cmd = 0x%02x", + BX_FD_THIS s.command[0])); + } +#endif +} + + void +bx_floppy_ctrl_c::floppy_xfer(Bit8u drive, Bit32u offset, Bit8u *buffer, + Bit32u bytes, Bit8u direction) +{ + int ret; + + if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE) + BX_PANIC(("floppy_xfer: bad drive #%d", drive)); + + if (bx_dbg.floppy) { + BX_INFO(("drive=%u", (unsigned) drive)); + BX_INFO(("offset=%u", (unsigned) offset)); + BX_INFO(("bytes=%u", (unsigned) bytes)); + BX_INFO(("direction=%s", (direction==FROM_FLOPPY)? "from" : "to")); + } + +#if BX_WITH_MACOS + if (strcmp(bx_options.floppya.Opath->getptr (), SuperDrive)) +#endif + { + ret = lseek(BX_FD_THIS s.media[drive].fd, offset, SEEK_SET); + if (ret < 0) { + BX_PANIC(("could not perform lseek() on floppy image file")); + } + } + + if (direction == FROM_FLOPPY) { +#if BX_WITH_MACOS + if (!strcmp(bx_options.floppya.Opath->getptr (), SuperDrive)) + ret = fd_read((char *) buffer, offset, bytes); + else +#endif + ret = ::read(BX_FD_THIS s.media[drive].fd, (bx_ptr_t) buffer, bytes); + if (ret < int(bytes)) { + /* ??? */ + if (ret > 0) { + BX_INFO(("partial read() on floppy image returns %u/%u", + (unsigned) ret, (unsigned) bytes)); + memset(buffer + ret, 0, bytes - ret); + } + else { + BX_INFO(("read() on floppy image returns 0")); + memset(buffer, 0, bytes); + } + } + } + + else { // TO_FLOPPY + BX_ASSERT (!BX_FD_THIS s.media[drive].write_protected); +#if BX_WITH_MACOS + if (!strcmp(bx_options.floppya.Opath->getptr (), SuperDrive)) + ret = fd_write((char *) buffer, offset, bytes); + else +#endif + ret = ::write(BX_FD_THIS s.media[drive].fd, (bx_ptr_t) buffer, bytes); + if (ret < int(bytes)) { + BX_PANIC(("could not perform write() on floppy image file")); + } + } +} + + + + void +bx_floppy_ctrl_c::timer_handler(void *this_ptr) +{ + + bx_floppy_ctrl_c *class_ptr = (bx_floppy_ctrl_c *) this_ptr; + + class_ptr->timer(); +} + + void +bx_floppy_ctrl_c::timer() +{ + Bit8u drive; + + drive = BX_FD_THIS s.DOR & 0x03; + switch ( BX_FD_THIS s.pending_command ) { + case 0x07: // recal + case 0x0f: // seek + BX_FD_THIS s.status_reg0 = 0x20 | (BX_FD_THIS s.head[drive]<<2) | drive; + if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE) { + BX_FD_THIS s.status_reg0 |= 0x50; + } + else if (BX_FD_THIS s.media_present[drive] == 0) { + BX_FD_THIS s.status_reg0 |= 0x40; + BX_FD_THIS s.status_reg1 = 0x25; + BX_FD_THIS s.status_reg2 = 0x31; + } + + /* reset changeline */ + if (drive > 1) return; + if (BX_FD_THIS s.media_present[drive]) + BX_FD_THIS s.DIR[drive] &= ~0x80; // clear disk change line + + enter_idle_phase(); + raise_interrupt(); + break; + + case 0x4a: /* read ID */ + enter_result_phase(); + break; + + case 0xfe: // (contrived) RESET + theFloppyController->reset(BX_RESET_SOFTWARE); + BX_FD_THIS s.pending_command = 0; + BX_FD_THIS s.status_reg0 = 0xc0; + raise_interrupt(); + BX_FD_THIS s.reset_sensei = 4; + break; + + case 0x00: // nothing pending? + break; + + default: + BX_PANIC(("floppy:timer(): unknown case %02x", + (unsigned) BX_FD_THIS s.pending_command)); + } + return; +} + + void +bx_floppy_ctrl_c::dma_write(Bit8u *data_byte) +{ + // A DMA write is from I/O to Memory + // We need to return then next data byte from the floppy buffer + // to be transfered via the DMA to memory. (read block from floppy) + + + *data_byte = BX_FD_THIS s.floppy_buffer[BX_FD_THIS s.floppy_buffer_index++]; + + if (BX_FD_THIS s.floppy_buffer_index >= 512) { + Bit8u drive; + + drive = BX_FD_THIS s.DOR & 0x03; + increment_sector(); // increment to next sector before retrieving next one + BX_FD_THIS s.floppy_buffer_index = 0; + if (DEV_dma_get_tc()) { // Terminal Count line, done + BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive] << 2) | drive; + BX_FD_THIS s.status_reg1 = 0; + BX_FD_THIS s.status_reg2 = 0; + + if (bx_dbg.floppy) { + BX_INFO(("<<READ DONE>>")); + BX_INFO(("AFTER")); + BX_INFO((" drive = %u", (unsigned) drive)); + BX_INFO((" head = %u", (unsigned) BX_FD_THIS s.head[drive])); + BX_INFO((" cylinder = %u", (unsigned) BX_FD_THIS s.cylinder[drive])); + BX_INFO((" sector = %u", (unsigned) BX_FD_THIS s.sector[drive])); + } + + DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0); + enter_result_phase(); + } + else { // more data to transfer + Bit32u logical_sector; + + // original assumed all floppies had two sides...now it does not *delete this comment line* + logical_sector = (BX_FD_THIS s.cylinder[drive] * BX_FD_THIS s.media[drive].heads * + BX_FD_THIS s.media[drive].sectors_per_track) + + (BX_FD_THIS s.head[drive] * + BX_FD_THIS s.media[drive].sectors_per_track) + + (BX_FD_THIS s.sector[drive] - 1); + + floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer, + 512, FROM_FLOPPY); + } + } +} + + void +bx_floppy_ctrl_c::dma_read(Bit8u *data_byte) +{ + // A DMA read is from Memory to I/O + // We need to write the data_byte which was already transfered from memory + // via DMA to I/O (write block to floppy) + + Bit8u drive; + Bit32u logical_sector; + + drive = BX_FD_THIS s.DOR & 0x03; + if (BX_FD_THIS s.pending_command == 0x4d) { // format track in progress + --BX_FD_THIS s.format_count; + switch (3 - (BX_FD_THIS s.format_count & 0x03)) { + case 0: + BX_FD_THIS s.cylinder[drive] = *data_byte; + break; + case 1: + if (*data_byte != BX_FD_THIS s.head[drive]) + BX_ERROR(("head number does not match head field")); + break; + case 2: + BX_FD_THIS s.sector[drive] = *data_byte; + break; + case 3: + if (*data_byte != 2) BX_ERROR(("dma_read: sector size %d not supported", 128<<(*data_byte))); + BX_DEBUG(("formatting cylinder %u head %u sector %u", + BX_FD_THIS s.cylinder[drive], BX_FD_THIS s.head[drive], + BX_FD_THIS s.sector[drive])); + for (unsigned i = 0; i < 512; i++) { + BX_FD_THIS s.floppy_buffer[i] = BX_FD_THIS s.format_fillbyte; + } + // original assumed all floppies had two sides...now it does not *delete this comment line* + logical_sector = (BX_FD_THIS s.cylinder[drive] * BX_FD_THIS s.media[drive].heads * BX_FD_THIS s.media[drive].sectors_per_track) + + (BX_FD_THIS s.head[drive] * BX_FD_THIS s.media[drive].sectors_per_track) + + (BX_FD_THIS s.sector[drive] - 1); + floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer, + 512, TO_FLOPPY); + break; + } + if ((BX_FD_THIS s.format_count == 0) || (DEV_dma_get_tc())) { + BX_FD_THIS s.format_count = 0; + BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive] << 2) | drive; + DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0); + enter_result_phase(); + } + return; + } + + BX_FD_THIS s.floppy_buffer[BX_FD_THIS s.floppy_buffer_index++] = *data_byte; + + if (BX_FD_THIS s.floppy_buffer_index >= 512) { + // original assumed all floppies had two sides...now it does not *delete this comment line* + logical_sector = (BX_FD_THIS s.cylinder[drive] * BX_FD_THIS s.media[drive].heads * BX_FD_THIS s.media[drive].sectors_per_track) + + (BX_FD_THIS s.head[drive] * BX_FD_THIS s.media[drive].sectors_per_track) + + (BX_FD_THIS s.sector[drive] - 1); + if ( BX_FD_THIS s.media[drive].write_protected ) { + // write protected error + BX_INFO(("tried to write disk %u, which is write-protected", drive)); + // ST0: IC1,0=01 (abnormal termination: started execution but failed) + BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; + // ST1: DataError=1, NDAT=1, NotWritable=1, NID=1 + BX_FD_THIS s.status_reg1 = 0x27; // 0010 0111 + // ST2: CRCE=1, SERR=1, BCYL=1, NDAM=1. + BX_FD_THIS s.status_reg2 = 0x31; // 0011 0001 + enter_result_phase(); + return; + } + floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer, + 512, TO_FLOPPY); + increment_sector(); // increment to next sector after writing current one + BX_FD_THIS s.floppy_buffer_index = 0; + if (DEV_dma_get_tc()) { // Terminal Count line, done + BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive] << 2) | drive; + BX_FD_THIS s.status_reg1 = 0; + BX_FD_THIS s.status_reg2 = 0; + + if (bx_dbg.floppy) { + BX_INFO(("<<WRITE DONE>>")); + BX_INFO(("AFTER")); + BX_INFO((" drive = %u", (unsigned) drive)); + BX_INFO((" head = %u", (unsigned) BX_FD_THIS s.head[drive])); + BX_INFO((" cylinder = %u", (unsigned) BX_FD_THIS s.cylinder[drive])); + BX_INFO((" sector = %u", (unsigned) BX_FD_THIS s.sector[drive])); + } + + DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0); + enter_result_phase(); + } + else { // more data to transfer + } // else + } // if BX_FD_THIS s.floppy_buffer_index >= 512 +} + + void +bx_floppy_ctrl_c::raise_interrupt(void) +{ + DEV_pic_raise_irq(6); + BX_FD_THIS s.pending_irq = 1; + BX_FD_THIS s.reset_sensei = 0; +} + + + void +bx_floppy_ctrl_c::increment_sector(void) +{ + Bit8u drive; + + drive = BX_FD_THIS s.DOR & 0x03; + + // values after completion of data xfer + // ??? calculation depends on base_count being multiple of 512 + BX_FD_THIS s.sector[drive] ++; + if (BX_FD_THIS s.sector[drive] > BX_FD_THIS s.media[drive].sectors_per_track) { + BX_FD_THIS s.sector[drive] = 1; + if (BX_FD_THIS s.multi_track) { + BX_FD_THIS s.head[drive] ++; + if (BX_FD_THIS s.head[drive] > 1) { + BX_FD_THIS s.head[drive] = 0; + BX_FD_THIS s.cylinder[drive] ++; + } + } + else { + BX_FD_THIS s.cylinder[drive] ++; + } + if (BX_FD_THIS s.cylinder[drive] >= BX_FD_THIS s.media[drive].tracks) { + // Set to 1 past last possible cylinder value. + // I notice if I set it to tracks-1, prama linux won't boot. + BX_FD_THIS s.cylinder[drive] = BX_FD_THIS s.media[drive].tracks; + BX_INFO(("increment_sector: clamping cylinder to max")); + } + } +} + + unsigned +bx_floppy_ctrl_c::set_media_status(unsigned drive, unsigned status) +{ + char *path; + unsigned type; + + if (drive == 0) + type = bx_options.floppya.Otype->get (); + else + type = bx_options.floppyb.Otype->get (); + + // if setting to the current value, nothing to do + if ((status == BX_FD_THIS s.media_present[drive]) && + ((status == 0) || (type == BX_FD_THIS s.media[drive].type))) + return(status); + + if (status == 0) { + // eject floppy + if (BX_FD_THIS s.media[drive].fd >= 0) { + close( BX_FD_THIS s.media[drive].fd ); + BX_FD_THIS s.media[drive].fd = -1; + } + BX_FD_THIS s.media_present[drive] = 0; + if (drive == 0) { + bx_options.floppya.Ostatus->set(BX_EJECTED); + } else { + bx_options.floppyb.Ostatus->set(BX_EJECTED); + } + BX_FD_THIS s.DIR[drive] |= 0x80; // disk changed line + return(0); + } + else { + // insert floppy + if (drive == 0) { + path = bx_options.floppya.Opath->getptr (); + } + else { + path = bx_options.floppyb.Opath->getptr (); + } + if (!strcmp(path, "none")) + return(0); + if (evaluate_media(type, path, & BX_FD_THIS s.media[drive])) { + BX_FD_THIS s.media_present[drive] = 1; + if (drive == 0) { +#define MED (BX_FD_THIS s.media[0]) + BX_INFO(("fd0: '%s' ro=%d, h=%d,t=%d,spt=%d", bx_options.floppya.Opath->getptr(), + MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track)); +#undef MED + bx_options.floppya.Ostatus->set(BX_INSERTED); + } else { +#define MED (BX_FD_THIS s.media[1]) + BX_INFO(("fd1: '%s' ro=%d, h=%d,t=%d,spt=%d", bx_options.floppyb.Opath->getptr(), + MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track)); +#undef MED + bx_options.floppyb.Ostatus->set(BX_INSERTED); + } + BX_FD_THIS s.DIR[drive] |= 0x80; // disk changed line + return(1); + } + else { + BX_FD_THIS s.media_present[drive] = 0; + if (drive == 0) { + bx_options.floppya.Ostatus->set(BX_EJECTED); + } else { + bx_options.floppyb.Ostatus->set(BX_EJECTED); + } + return(0); + } + } +} + + unsigned +bx_floppy_ctrl_c::get_media_status(unsigned drive) +{ + return( BX_FD_THIS s.media_present[drive] ); +} + +#ifdef O_BINARY +#define BX_RDONLY O_RDONLY | O_BINARY +#define BX_RDWR O_RDWR | O_BINARY +#else +#define BX_RDONLY O_RDONLY +#define BX_RDWR O_RDWR +#endif + + bx_bool +bx_floppy_ctrl_c::evaluate_media(unsigned type, char *path, floppy_t *media) +{ + struct stat stat_buf; + int i, ret; + int idx = -1; +#ifdef __linux__ + struct floppy_struct floppy_geom; +#endif +#ifdef WIN32 + char sTemp[1024]; + bx_bool raw_floppy = 0; + HANDLE hFile; + DWORD bytes; + DISK_GEOMETRY dg; + unsigned tracks, heads, spt; +#endif + + if (type == BX_FLOPPY_NONE) + return(0); + + //If media file is already open, close it before reopening. + if(media->fd >=0) { + close(media->fd); + media->fd=-1; + } + + // open media file (image file or device) + media->write_protected = 0; +#ifdef macintosh + media->fd = 0; + if (strcmp(bx_options.floppya.Opath->getptr (), SuperDrive)) +#endif +#ifdef WIN32 + if ( (isalpha(path[0])) && (path[1] == ':') && (strlen(path) == 2) ) { + raw_floppy = 1; + wsprintf(sTemp, "\\\\.\\%s", path); + hFile = CreateFile(sTemp, GENERIC_READ, FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + BX_ERROR(("Cannot open floppy drive")); + return(0); + } else { + if (!DeviceIoControl(hFile, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &dg, sizeof(dg), &bytes, NULL)) { + BX_ERROR(("No media in floppy drive")); + CloseHandle(hFile); + return(0); + } else { + tracks = (unsigned)dg.Cylinders.QuadPart; + heads = (unsigned)dg.TracksPerCylinder; + spt = (unsigned)dg.SectorsPerTrack; + } + CloseHandle(hFile); + } + media->fd = open(sTemp, BX_RDWR); + } else { + media->fd = open(path, BX_RDWR); + } +#else + media->fd = open(path, BX_RDWR); +#endif + + if (media->fd < 0) { + BX_INFO(( "tried to open '%s' read/write: %s",path,strerror(errno) )); + // try opening the file read-only + media->write_protected = 1; +#ifdef macintosh + media->fd = 0; + if (strcmp(bx_options.floppya.Opath->getptr (), SuperDrive)) +#endif +#ifdef WIN32 + if (raw_floppy == 1) { + media->fd = open(sTemp, BX_RDONLY); + } else { + media->fd = open(path, BX_RDONLY); + } +#else + media->fd = open(path, BX_RDONLY); +#endif + if (media->fd < 0) { + // failed to open read-only too + BX_INFO(( "tried to open '%s' read only: %s",path,strerror(errno) )); + media->type = type; + return(0); + } + } + +#if BX_WITH_MACOS + if (!strcmp(bx_options.floppya.Opath->getptr (), SuperDrive)) + ret = fd_stat(&stat_buf); + else + ret = fstat(media->fd, &stat_buf); +#elif defined(WIN32) + if (raw_floppy) { + memset (&stat_buf, 0, sizeof(stat_buf)); + stat_buf.st_mode = S_IFCHR; + ret = 0; + } else { + ret = fstat(media->fd, &stat_buf); + } +#else + // unix + ret = fstat(media->fd, &stat_buf); +#endif + if (ret) { + BX_PANIC(("fstat floppy 0 drive image file returns error: %s", strerror(errno))); + return(0); + } + + for (i = 0; i < 8; i++) { + if (type == floppy_type[i].id) idx = i; + } + if (idx == -1 ) { + BX_PANIC(("evaluate_media: unknown media type")); + return(0); + } + if ( S_ISREG(stat_buf.st_mode) ) { + // regular file + switch (type) { + // use CMOS reserved types + case BX_FLOPPY_160K: // 160K 5.25" + case BX_FLOPPY_180K: // 180K 5.25" + case BX_FLOPPY_320K: // 320K 5.25" + // standard floppy types + case BX_FLOPPY_360K: // 360K 5.25" + case BX_FLOPPY_720K: // 720K 3.5" + case BX_FLOPPY_1_2: // 1.2M 5.25" + case BX_FLOPPY_2_88: // 2.88M 3.5" + media->type = type; + media->tracks = floppy_type[idx].trk; + media->heads = floppy_type[idx].hd; + media->sectors_per_track = floppy_type[idx].spt; + media->sectors = floppy_type[idx].sectors; + if (stat_buf.st_size > (media->sectors * 512)) { + BX_INFO(("evaluate_media: size of file '%s' (%lu) too large for selected type", + path, (unsigned long) stat_buf.st_size)); + return(0); + } + break; + default: // 1.44M 3.5" + media->type = type; + if (stat_buf.st_size <= 1474560) { + media->tracks = floppy_type[idx].trk; + media->heads = floppy_type[idx].hd; + media->sectors_per_track = floppy_type[idx].spt; + } + else if (stat_buf.st_size == 1720320) { + media->sectors_per_track = 21; + media->tracks = 80; + media->heads = 2; + } + else if (stat_buf.st_size == 1763328) { + media->sectors_per_track = 21; + media->tracks = 82; + media->heads = 2; + } + else { + BX_INFO(("evaluate_media: file '%s' of unknown size %lu", + path, (unsigned long) stat_buf.st_size)); + return(0); + } + media->sectors = media->heads * media->tracks * media->sectors_per_track; + } + return(1); // success + } + + else if ( S_ISCHR(stat_buf.st_mode) +#if BX_WITH_MACOS == 0 +#ifdef S_ISBLK + || S_ISBLK(stat_buf.st_mode) +#endif +#endif + ) { + // character or block device + // assume media is formatted to typical geometry for drive + media->type = type; +#ifdef __linux__ + if (ioctl(media->fd, FDGETPRM, &floppy_geom) < 0) { + BX_ERROR(("cannot determine media geometry")); + return(0); + } + media->tracks = floppy_geom.track; + media->heads = floppy_geom.head; + media->sectors_per_track = floppy_geom.sect; + media->sectors = floppy_geom.size; +#elif defined(WIN32) + media->tracks = tracks; + media->heads = heads; + media->sectors_per_track = spt; + media->sectors = media->heads * media->tracks * media->sectors_per_track; +#else + media->tracks = floppy_type[idx].trk; + media->heads = floppy_type[idx].hd; + media->sectors_per_track = floppy_type[idx].spt; + media->sectors = floppy_type[idx].sectors; +#endif + return(1); // success + } + else { + // unknown file type + BX_INFO(("unknown mode type")); + return(0); + } +} + + +void +bx_floppy_ctrl_c::enter_result_phase(void) +{ + + Bit8u drive; + + drive = BX_FD_THIS s.DOR & 0x03; + + /* these are always the same */ + BX_FD_THIS s.result_index = 0; + BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY; + + /* invalid command */ + if ((BX_FD_THIS s.status_reg0 & 0xc0) == 0x80) { + BX_FD_THIS s.result_size = 1; + BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg0; + return; + } + + switch (BX_FD_THIS s.pending_command) { + case 0x04: // get status + BX_FD_THIS s.result_size = 1; + BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg3; + break; + case 0x08: // sense interrupt + BX_FD_THIS s.result_size = 2; + BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg0; + BX_FD_THIS s.result[1] = BX_FD_THIS s.cylinder[drive]; + break; + case 0x4a: // read ID + case 0x4d: // format track + case 0x46: // read normal data + case 0x66: + case 0xc6: + case 0xe6: + case 0x45: // write normal data + case 0xc5: + BX_FD_THIS s.result_size = 7; + BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg0; + BX_FD_THIS s.result[1] = BX_FD_THIS s.status_reg1; + BX_FD_THIS s.result[2] = BX_FD_THIS s.status_reg2; + BX_FD_THIS s.result[3] = BX_FD_THIS s.cylinder[drive]; + BX_FD_THIS s.result[4] = BX_FD_THIS s.head[drive]; + BX_FD_THIS s.result[5] = BX_FD_THIS s.sector[drive]; + BX_FD_THIS s.result[6] = 2; /* sector size code */ + raise_interrupt(); + break; + } +} + +void +bx_floppy_ctrl_c::enter_idle_phase(void) +{ + BX_FD_THIS s.main_status_reg &= 0x0f; // leave drive status untouched + BX_FD_THIS s.main_status_reg |= FD_MS_MRQ; // data register ready + + BX_FD_THIS s.command_complete = 1; /* waiting for new command */ + BX_FD_THIS s.command_index = 0; + BX_FD_THIS s.command_size = 0; + BX_FD_THIS s.pending_command = 0; + + BX_FD_THIS s.floppy_buffer_index = 0; +} + diff --git a/tools/ioemu/iodev/floppy.h b/tools/ioemu/iodev/floppy.h new file mode 100644 index 0000000000..d874539900 --- /dev/null +++ b/tools/ioemu/iodev/floppy.h @@ -0,0 +1,138 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: floppy.h,v 1.16 2002/11/30 09:39:29 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 FROM_FLOPPY 10 +#define TO_FLOPPY 11 + +#if BX_USE_FD_SMF +# define BX_FD_SMF static +# define BX_FD_THIS theFloppyController-> +#else +# define BX_FD_SMF +# define BX_FD_THIS this-> +#endif + +typedef struct { + int fd; /* file descriptor of floppy image file */ + unsigned sectors_per_track; /* number of sectors/track */ + unsigned sectors; /* number of formatted sectors on diskette */ + unsigned tracks; /* number of tracks */ + unsigned heads; /* number of heads */ + unsigned type; + unsigned write_protected; + } floppy_t; + +class bx_floppy_ctrl_c : public bx_floppy_stub_c { +public: + + bx_floppy_ctrl_c(void); + ~bx_floppy_ctrl_c(void); + virtual void init(void); + virtual void reset(unsigned type); + virtual unsigned set_media_status(unsigned drive, unsigned status); + virtual unsigned get_media_status(unsigned drive); + +private: + + struct { + Bit8u data_rate; + + Bit8u command[10]; /* largest command size ??? */ + Bit8u command_index; + Bit8u command_size; + bx_bool command_complete; + Bit8u pending_command; + + bx_bool multi_track; + bx_bool pending_irq; + Bit8u reset_sensei; + Bit8u format_count; + Bit8u format_fillbyte; + + Bit8u result[10]; + Bit8u result_index; + Bit8u result_size; + + Bit8u DOR; // Digital Ouput Register + Bit8u TDR; // Tape Drive Register + Bit8u cylinder[4]; // really only using 2 drives + Bit8u head[4]; // really only using 2 drives + Bit8u sector[4]; // really only using 2 drives + + /* MAIN STATUS REGISTER + * b7: MRQ: main request 1=data register ready 0=data register not ready + * b6: DIO: data input/output: + * 1=controller->CPU (ready for data read) + * 0=CPU->controller (ready for data write) + * b5: NDMA: non-DMA mode: 1=controller not in DMA modes + * 0=controller in DMA mode + * b4: BUSY: instruction(device busy) 1=active 0=not active + * b3-0: ACTD, ACTC, ACTB, ACTA: + * drive D,C,B,A in positioning mode 1=active 0=not active + */ + Bit8u main_status_reg; + + Bit8u status_reg0; + Bit8u status_reg1; + Bit8u status_reg2; + Bit8u status_reg3; + + // drive field allows up to 4 drives, even though probably only 2 will + // ever be used. + floppy_t media[4]; + unsigned num_supported_floppies; + Bit8u floppy_buffer[512+2]; // 2 extra for good measure + unsigned floppy_buffer_index; + int floppy_timer_index; + bx_bool media_present[2]; + Bit8u device_type[4]; + Bit8u DIR[4]; // Digital Input Register: + // b7: 0=diskette is present and has not been changed + // 1=diskette missing or changed + } s; // state information + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#if !BX_USE_FD_SMF + Bit32u read(Bit32u address, unsigned io_len); + void write(Bit32u address, Bit32u value, unsigned io_len); +#endif + BX_FD_SMF void dma_write(Bit8u *data_byte); + BX_FD_SMF void dma_read(Bit8u *data_byte); + BX_FD_SMF void floppy_command(void); + BX_FD_SMF void floppy_xfer(Bit8u drive, Bit32u offset, Bit8u *buffer, Bit32u bytes, Bit8u direction); + BX_FD_SMF void raise_interrupt(void); + BX_FD_SMF void enter_idle_phase(void); + BX_FD_SMF void enter_result_phase(void); + static void timer_handler(void *); + +public: + BX_FD_SMF void timer(void); + BX_FD_SMF void increment_sector(void); + BX_FD_SMF bx_bool evaluate_media(unsigned type, char *path, floppy_t *floppy); + }; diff --git a/tools/ioemu/iodev/gameport.cc b/tools/ioemu/iodev/gameport.cc new file mode 100644 index 0000000000..9adefa6551 --- /dev/null +++ b/tools/ioemu/iodev/gameport.cc @@ -0,0 +1,242 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: gameport.cc,v 1.5 2003/12/29 21:48:56 cbothamy Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2003 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 + +// +// Standard PC gameport +// + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" + +#ifdef __linux__ + +#include <linux/joystick.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> + +#elif defined(WIN32) + +#ifndef JOY_BUTTON1 +#define JOY_BUTTON1 1 +#define JOY_BUTTON2 2 +UINT STDCALL joyGetPos(UINT, LPJOYINFO); +#endif + +#define JOYSTICKID1 0 + +#endif + +#define LOG_THIS theGameport-> + +bx_gameport_c *theGameport = NULL; + + int +libgameport_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + theGameport = new bx_gameport_c (); + bx_devices.pluginGameport = theGameport; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theGameport, BX_PLUGIN_GAMEPORT); + return(0); // Success +} + + void +libgameport_LTX_plugin_fini(void) +{ +} + +bx_gameport_c::bx_gameport_c(void) +{ + put("GAME"); + settype(EXTFPUIRQLOG); +} + +bx_gameport_c::~bx_gameport_c(void) +{ + if (joyfd >= 0) close(joyfd); + BX_DEBUG(("Exit.")); +} + + + void +bx_gameport_c::init(void) +{ + // Allocate the gameport IO address range 0x200..0x207 + for (unsigned addr=0x200; addr<0x208; addr++) { + DEV_register_ioread_handler(this, read_handler, addr, "Gameport", 1); + DEV_register_iowrite_handler(this, write_handler, addr, "Gameport", 1); + } + + BX_GAMEPORT_THIS port = 0xf0; + BX_GAMEPORT_THIS write_usec = 0; + BX_GAMEPORT_THIS timer_x = 0; + BX_GAMEPORT_THIS timer_y = 0; + +#ifdef __linux__ + BX_GAMEPORT_THIS joyfd = open("/dev/input/js0", O_RDONLY); + if (BX_GAMEPORT_THIS joyfd >= 0) { + for (unsigned i=0; i<4; i++) poll_joydev(); + } +#elif defined(WIN32) + JOYINFO joypos; + if (joyGetPos(JOYSTICKID1, &joypos) == JOYERR_NOERROR) { + BX_GAMEPORT_THIS joyfd = 1; + } else { + BX_GAMEPORT_THIS joyfd = -1; + } +#else + BX_GAMEPORT_THIS joyfd = -1; +#endif +} + + void +bx_gameport_c::reset(unsigned type) +{ + // nothing for now +} + + void +bx_gameport_c::poll_joydev(void) +{ +#ifdef __linux__ + struct js_event e; + fd_set joyfds; + struct timeval tv; + + memset(&tv, 0, sizeof(tv)); + FD_ZERO(&joyfds); + FD_SET(BX_GAMEPORT_THIS joyfd, &joyfds); + e.type = 0; + if (select(BX_GAMEPORT_THIS joyfd+1, &joyfds, NULL, NULL, &tv)) { + read(BX_GAMEPORT_THIS joyfd, &e, sizeof(struct js_event)); + if (e.type & JS_EVENT_BUTTON) { + if (e.value == 1) { + BX_GAMEPORT_THIS port &= ~(0x10 << e.number); + } else { + BX_GAMEPORT_THIS port |= (0x10 << e.number); + } + } + if (e.type & JS_EVENT_AXIS) { + if (e.number == 0) { + BX_GAMEPORT_THIS delay_x = 25 + ((e.value + 0x8000) / 60); + } + if (e.number == 1) { + BX_GAMEPORT_THIS delay_y = 25 + ((e.value + 0x8000) / 62); + } + } + } +#elif defined(WIN32) + JOYINFO joypos; + if (joyGetPos(JOYSTICKID1, &joypos) == JOYERR_NOERROR) { + if (joypos.wButtons & JOY_BUTTON1) { + BX_GAMEPORT_THIS port &= ~0x10; + } else { + BX_GAMEPORT_THIS port |= 0x10; + } + if (joypos.wButtons & JOY_BUTTON2) { + BX_GAMEPORT_THIS port &= ~0x20; + } else { + BX_GAMEPORT_THIS port |= 0x20; + } + BX_GAMEPORT_THIS delay_x = 25 + (joypos.wXpos / 60); + BX_GAMEPORT_THIS delay_y = 25 + (joypos.wYpos / 60); + } +#endif +} + + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_gameport_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_GAME_SMF + bx_gameport_c *class_ptr = (bx_gameport_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + Bit32u +bx_gameport_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_GAME_SMF + Bit64u usec; + + if (BX_GAMEPORT_THIS joyfd >= 0) { + poll_joydev(); + usec = bx_pc_system.time_usec(); + if (BX_GAMEPORT_THIS timer_x) { + if ((usec - BX_GAMEPORT_THIS write_usec) >= BX_GAMEPORT_THIS delay_x) { + BX_GAMEPORT_THIS port &= 0xfe; + BX_GAMEPORT_THIS timer_x = 0; + } + } + if (BX_GAMEPORT_THIS timer_y) { + if ((usec - BX_GAMEPORT_THIS write_usec) >= BX_GAMEPORT_THIS delay_y) { + BX_GAMEPORT_THIS port &= 0xfd; + BX_GAMEPORT_THIS timer_y = 0; + } + } + } else { + BX_DEBUG(("read: joystick not present")); + } + return BX_GAMEPORT_THIS port; +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_gameport_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_GAME_SMF + bx_gameport_c *class_ptr = (bx_gameport_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + void +bx_gameport_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_GAME_SMF + + BX_GAMEPORT_THIS write_usec = bx_pc_system.time_usec(); + BX_GAMEPORT_THIS timer_x = 1; + BX_GAMEPORT_THIS timer_y = 1; + BX_GAMEPORT_THIS port |= 0x0f; +} diff --git a/tools/ioemu/iodev/gameport.h b/tools/ioemu/iodev/gameport.h new file mode 100644 index 0000000000..d4acdddf69 --- /dev/null +++ b/tools/ioemu/iodev/gameport.h @@ -0,0 +1,63 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: gameport.h,v 1.1 2003/06/21 12:55:19 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2003 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 + + +#if BX_USE_GAME_SMF +# define BX_GAMEPORT_SMF static +# define BX_GAMEPORT_THIS theGameport-> +#else +# define BX_GAMEPORT_SMF +# define BX_GAMEPORT_THIS this-> +#endif + + +class bx_gameport_c : public bx_devmodel_c { + +public: + bx_gameport_c(void); + ~bx_gameport_c(void); + virtual void init(void); + virtual void reset(unsigned type); + +private: + + int joyfd; + Bit8u port; + Bit16u delay_x; + Bit16u delay_y; + bx_bool timer_x; + bx_bool timer_y; + Bit64u write_usec; + + BX_GAMEPORT_SMF void poll_joydev(void); + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#if !BX_USE_GAME_SMF + Bit32u read(Bit32u address, unsigned io_len); + void write(Bit32u address, Bit32u value, unsigned io_len); +#endif + }; diff --git a/tools/ioemu/iodev/guest2host.h b/tools/ioemu/iodev/guest2host.h new file mode 100644 index 0000000000..0003662aa2 --- /dev/null +++ b/tools/ioemu/iodev/guest2host.h @@ -0,0 +1,77 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: guest2host.h,v 1.8 2002/12/06 18:48:08 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 BX_MAX_G2H_CHANNELS 8 +#define BX_G2H_ERROR ((unsigned) -1) + // IO port number for this interface. Align on dword boundary. +#define BX_G2H_PORT 0x2000 + // Magic number which is first dword passed by guest +#define BX_G2H_MAGIC 0xffeeddcc + // Number of dwords in packet from guest +#define BX_G2H_PACKET_SIZE 5 + + + +typedef Bit32u bx_guest_packet_t[BX_G2H_PACKET_SIZE]; +typedef void (*bx_g2h_callback_t)(bx_guest_packet_t *); + + + +class bx_g2h_c : public logfunctions { +public: + bx_g2h_c(void); + ~bx_g2h_c(void); + static void init(void); + void reset (unsigned type); + unsigned acquire_channel(bx_g2h_callback_t); + unsigned deacquire_channel(unsigned channel); + +private: + + static Bit32u inp_handler(void *this_ptr, Bit32u addr, unsigned io_len); + static void outp_handler(void *this_ptr, Bit32u addr, + Bit32u value, unsigned io_len); + // state info + struct { + struct { + bx_g2h_callback_t f; + bx_bool used; + } callback[BX_MAX_G2H_CHANNELS]; + + // Define the data received from the guest OS. + // dword0: magic number, should be BX_G2H_MAGIC + // dword1: channel ID + // dword2: address of data structure in guest physical memory + // dword3: size of data structure in guest physical memory + // dword4: address of return data structure in guest physical memory + unsigned packet_count; + bx_guest_packet_t guest_packet; + } s; + }; + +extern bx_g2h_c bx_g2h; diff --git a/tools/ioemu/iodev/harddrv.cc b/tools/ioemu/iodev/harddrv.cc new file mode 100644 index 0000000000..24a9d41efa --- /dev/null +++ b/tools/ioemu/iodev/harddrv.cc @@ -0,0 +1,4880 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: harddrv.cc,v 1.114.2.2 2004/02/06 22:14:35 danielg4 Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + +// Useful docs: +// AT Attachment with Packet Interface +// working draft by T13 at www.t13.org + + + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" + +#if BX_HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif + +#define LOG_THIS theHardDrive-> + +// WARNING: dangerous options! +// These options provoke certain kinds of errors for testing purposes when they +// are set to a nonzero value. DO NOT ENABLE THEM when using any disk image +// you care about. +#define TEST_READ_BEYOND_END 0 +#define TEST_WRITE_BEYOND_END 0 +#ifdef __GNUC__ +# if TEST_READ_BEYOND_END || TEST_WRITE_BEYOND_END +# warning BEWARE: Dangerous options are enabled in harddrv.cc. If you are not trying to provoke hard drive errors you should disable them right now. +# endif +#endif +// end of dangerous options. + + +#define INDEX_PULSE_CYCLE 10 + +#define PACKET_SIZE 12 + +static unsigned max_multiple_sectors = 0; // was 0x3f +static unsigned curr_multiple_sectors = 0; // was 0x3f + +// some packet handling macros +#define EXTRACT_FIELD(arr,byte,start,num_bits) (((arr)[(byte)] >> (start)) & ((1 << (num_bits)) - 1)) +#define get_packet_field(c,b,s,n) (EXTRACT_FIELD((BX_SELECTED_CONTROLLER((c)).buffer),(b),(s),(n))) +#define get_packet_byte(c,b) (BX_SELECTED_CONTROLLER((c)).buffer[(b)]) +#define get_packet_word(c,b) (((uint16)BX_SELECTED_CONTROLLER((c)).buffer[(b)] << 8) | BX_SELECTED_CONTROLLER((c)).buffer[(b)+1]) + + +#define BX_CONTROLLER(c,a) (BX_HD_THIS channels[(c)].drives[(a)]).controller +#define BX_DRIVE(c,a) (BX_HD_THIS channels[(c)].drives[(a)]) + +#define BX_DRIVE_IS_PRESENT(c,a) (BX_HD_THIS channels[(c)].drives[(a)].device_type != IDE_NONE) +#define BX_DRIVE_IS_HD(c,a) (BX_HD_THIS channels[(c)].drives[(a)].device_type == IDE_DISK) +#define BX_DRIVE_IS_CD(c,a) (BX_HD_THIS channels[(c)].drives[(a)].device_type == IDE_CDROM) + +#define BX_MASTER_IS_PRESENT(c) BX_DRIVE_IS_PRESENT((c),0) +#define BX_SLAVE_IS_PRESENT(c) BX_DRIVE_IS_PRESENT((c),1) +#define BX_ANY_IS_PRESENT(c) (BX_DRIVE_IS_PRESENT((c),0) || BX_DRIVE_IS_PRESENT((c),1)) + +#define BX_SELECTED_CONTROLLER(c) (BX_CONTROLLER((c),BX_HD_THIS channels[(c)].drive_select)) +#define BX_SELECTED_DRIVE(c) (BX_DRIVE((c),BX_HD_THIS channels[(c)].drive_select)) +#define BX_MASTER_SELECTED(c) (!BX_HD_THIS channels[(c)].drive_select) +#define BX_SLAVE_SELECTED(c) (BX_HD_THIS channels[(c)].drive_select) + +#define BX_SELECTED_IS_PRESENT(c) (BX_DRIVE_IS_PRESENT((c),BX_SLAVE_SELECTED((c)))) +#define BX_SELECTED_IS_HD(c) (BX_DRIVE_IS_HD((c),BX_SLAVE_SELECTED((c)))) +#define BX_SELECTED_IS_CD(c) (BX_DRIVE_IS_CD((c),BX_SLAVE_SELECTED((c)))) + +#define BX_SELECTED_MODEL(c) (BX_HD_THIS channels[(c)].drives[BX_HD_THIS channels[(c)].drive_select].model_no) +#define BX_SELECTED_TYPE_STRING(channel) ((BX_SELECTED_IS_CD(channel)) ? "CD-ROM" : "DISK") + +#define WRITE_FEATURES(c,a) do { uint8 _a = a; BX_CONTROLLER((c),0).features = _a; BX_CONTROLLER((c),1).features = _a; } while(0) +#define WRITE_SECTOR_COUNT(c,a) do { uint8 _a = a; BX_CONTROLLER((c),0).sector_count = _a; BX_CONTROLLER((c),1).sector_count = _a; } while(0) +#define WRITE_SECTOR_NUMBER(c,a) do { uint8 _a = a; BX_CONTROLLER((c),0).sector_no = _a; BX_CONTROLLER((c),1).sector_no = _a; } while(0) +#define WRITE_CYLINDER_LOW(c,a) do { uint8 _a = a; BX_CONTROLLER((c),0).cylinder_no = (BX_CONTROLLER((c),0).cylinder_no & 0xff00) | _a; BX_CONTROLLER((c),1).cylinder_no = (BX_CONTROLLER((c),1).cylinder_no & 0xff00) | _a; } while(0) +#define WRITE_CYLINDER_HIGH(c,a) do { uint16 _a = a; BX_CONTROLLER((c),0).cylinder_no = (_a << 8) | (BX_CONTROLLER((c),0).cylinder_no & 0xff); BX_CONTROLLER((c),1).cylinder_no = (_a << 8) | (BX_CONTROLLER((c),1).cylinder_no & 0xff); } while(0) +#define WRITE_HEAD_NO(c,a) do { uint8 _a = a; BX_CONTROLLER((c),0).head_no = _a; BX_CONTROLLER((c),1).head_no = _a; } while(0) +#define WRITE_LBA_MODE(c,a) do { uint8 _a = a; BX_CONTROLLER((c),0).lba_mode = _a; BX_CONTROLLER((c),1).lba_mode = _a; } while(0) + +bx_hard_drive_c *theHardDrive = NULL; + + int +libharddrv_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + theHardDrive = new bx_hard_drive_c (); + bx_devices.pluginHardDrive = theHardDrive; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theHardDrive, BX_PLUGIN_HARDDRV); + return(0); // Success +} + + void +libharddrv_LTX_plugin_fini(void) +{ +} + +bx_hard_drive_c::bx_hard_drive_c(void) +{ +#if DLL_HD_SUPPORT +# error code must be fixed to use DLL_HD_SUPPORT and 4 ata channels +#endif + + for (Bit8u channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) { + channels[channel].drives[0].hard_drive = NULL; + channels[channel].drives[1].hard_drive = NULL; + put("HD"); + settype(HDLOG); + } +} + + +bx_hard_drive_c::~bx_hard_drive_c(void) +{ + BX_DEBUG(("Exit.")); + for (Bit8u channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) { + if (channels[channel].drives[0].hard_drive != NULL ) /* DT 17.12.2001 21:55 */ + { + delete channels[channel].drives[0].hard_drive; + channels[channel].drives[0].hard_drive = NULL; + } + if ( channels[channel].drives[1].hard_drive != NULL ) + { + delete channels[channel].drives[1].hard_drive; + channels[channel].drives[1].hard_drive = NULL; /* DT 17.12.2001 21:56 */ + } + } +} + + + + + void +bx_hard_drive_c::init(void) +{ + Bit8u channel; + char string[5]; + + BX_DEBUG(("Init $Id: harddrv.cc,v 1.114.2.2 2004/02/06 22:14:35 danielg4 Exp $")); + + for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) { + if (bx_options.ata[channel].Opresent->get() == 1) { + BX_HD_THIS channels[channel].ioaddr1 = bx_options.ata[channel].Oioaddr1->get(); + BX_HD_THIS channels[channel].ioaddr2 = bx_options.ata[channel].Oioaddr2->get(); + BX_HD_THIS channels[channel].irq = bx_options.ata[channel].Oirq->get(); + + // Coherency check + if ( (BX_HD_THIS channels[channel].ioaddr1 == 0) || + (BX_HD_THIS channels[channel].ioaddr2 == 0) || + (BX_HD_THIS channels[channel].irq == 0) ) { + BX_PANIC(("incoherency for ata channel %d: io1=0x%x, io2=%x, irq=%d", + channel, + BX_HD_THIS channels[channel].ioaddr1, + BX_HD_THIS channels[channel].ioaddr2, + BX_HD_THIS channels[channel].irq)); + } + } + else { + BX_HD_THIS channels[channel].ioaddr1 = 0; + BX_HD_THIS channels[channel].ioaddr2 = 0; + BX_HD_THIS channels[channel].irq = 0; + } + } + + for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) { + sprintf(string ,"ATA%d", channel); + + if (BX_HD_THIS channels[channel].irq != 0) + DEV_register_irq(BX_HD_THIS channels[channel].irq, strdup(string)); + + if (BX_HD_THIS channels[channel].ioaddr1 != 0) { + DEV_register_ioread_handler(this, read_handler, + BX_HD_THIS channels[channel].ioaddr1, strdup(string), 6); + DEV_register_iowrite_handler(this, write_handler, + BX_HD_THIS channels[channel].ioaddr1, strdup(string), 6); + for (unsigned addr=0x1; addr<=0x7; addr++) { + DEV_register_ioread_handler(this, read_handler, + BX_HD_THIS channels[channel].ioaddr1+addr, strdup(string), 1); + DEV_register_iowrite_handler(this, write_handler, + BX_HD_THIS channels[channel].ioaddr1+addr, strdup(string), 1); + } + } + + // We don't want to register addresses 0x3f6 and 0x3f7 as they are handled by the floppy controller + if ((BX_HD_THIS channels[channel].ioaddr2 != 0) && (BX_HD_THIS channels[channel].ioaddr2 != 0x3f0)) { + for (unsigned addr=0x6; addr<=0x7; addr++) { + DEV_register_ioread_handler(this, read_handler, + BX_HD_THIS channels[channel].ioaddr2+addr, strdup(string), 1); + DEV_register_iowrite_handler(this, write_handler, + BX_HD_THIS channels[channel].ioaddr2+addr, strdup(string), 1); + } + } + + BX_HD_THIS channels[channel].drive_select = 0; + } + + channel = 0; + for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) { + for (Bit8u device=0; device<2; device ++) { + + // Initialize controller state, even if device is not present + BX_CONTROLLER(channel,device).status.busy = 0; + BX_CONTROLLER(channel,device).status.drive_ready = 1; + BX_CONTROLLER(channel,device).status.write_fault = 0; + BX_CONTROLLER(channel,device).status.seek_complete = 1; + BX_CONTROLLER(channel,device).status.drq = 0; + BX_CONTROLLER(channel,device).status.corrected_data = 0; + BX_CONTROLLER(channel,device).status.index_pulse = 0; + BX_CONTROLLER(channel,device).status.index_pulse_count = 0; + BX_CONTROLLER(channel,device).status.err = 0; + + BX_CONTROLLER(channel,device).error_register = 0x01; // diagnostic code: no error + BX_CONTROLLER(channel,device).head_no = 0; + BX_CONTROLLER(channel,device).sector_count = 1; + BX_CONTROLLER(channel,device).sector_no = 1; + BX_CONTROLLER(channel,device).cylinder_no = 0; + BX_CONTROLLER(channel,device).current_command = 0x00; + BX_CONTROLLER(channel,device).buffer_index = 0; + + BX_CONTROLLER(channel,device).control.reset = 0; + BX_CONTROLLER(channel,device).control.disable_irq = 0; + BX_CONTROLLER(channel,device).reset_in_progress = 0; + + BX_CONTROLLER(channel,device).sectors_per_block = 0x80; + BX_CONTROLLER(channel,device).lba_mode = 0; + + BX_CONTROLLER(channel,device).features = 0; + + // If not present + BX_HD_THIS channels[channel].drives[device].device_type = IDE_NONE; + if (!bx_options.atadevice[channel][device].Opresent->get()) { + continue; + } + + // Make model string + strncpy((char*)BX_HD_THIS channels[channel].drives[device].model_no, + bx_options.atadevice[channel][device].Omodel->getptr(), 40); + while (strlen((char *)BX_HD_THIS channels[channel].drives[device].model_no) < 40) { + strcat ((char*)BX_HD_THIS channels[channel].drives[device].model_no, " "); + } + + if (bx_options.atadevice[channel][device].Otype->get() == BX_ATA_DEVICE_DISK) { + BX_DEBUG(( "Hard-Disk on target %d/%d",channel,device)); + BX_HD_THIS channels[channel].drives[device].device_type = IDE_DISK; + + int cyl = bx_options.atadevice[channel][device].Ocylinders->get (); + int heads = bx_options.atadevice[channel][device].Oheads->get (); + int spt = bx_options.atadevice[channel][device].Ospt->get (); + Bit64u disk_size = (Bit64u)cyl * heads * spt * 512; + + /* instantiate the right class */ + switch (bx_options.atadevice[channel][device].Omode->get()) { + + case BX_ATA_MODE_FLAT: + BX_INFO(("HD on ata%d-%d: '%s' 'flat' mode ", channel, device, + bx_options.atadevice[channel][device].Opath->getptr ())); + channels[channel].drives[device].hard_drive = new default_image_t(); + break; + + case BX_ATA_MODE_CONCAT: + BX_INFO(("HD on ata%d-%d: '%s' 'concat' mode ", channel, device, + bx_options.atadevice[channel][device].Opath->getptr ())); + channels[channel].drives[device].hard_drive = new concat_image_t(); + break; + +#if EXTERNAL_DISK_SIMULATOR + case BX_ATA_MODE_EXTDISKSIM: + BX_INFO(("HD on ata%d-%d: '%s' 'External Simulator' mode ", channel, device, + bx_options.atadevice[channel][device].Opath->getptr ())); + channels[channel].drives[device].hard_drive = new EXTERNAL_DISK_SIMULATOR_CLASS(); + break; +#endif //EXTERNAL_DISK_SIMULATOR + +#if DLL_HD_SUPPORT + case BX_ATA_MODE_DLL_HD: + BX_INFO(("HD on ata%d-%d: '%s' 'dll' mode ", channel, device, + bx_options.atadevice[channel][device].Opath->getptr ())); + channels[channel].drives[device].hard_drive = new dll_image_t(); + break; +#endif //DLL_HD_SUPPORT + + case BX_ATA_MODE_SPARSE: + BX_INFO(("HD on ata%d-%d: '%s' 'sparse' mode ", channel, device, + bx_options.atadevice[channel][device].Opath->getptr ())); + channels[channel].drives[device].hard_drive = new sparse_image_t(); + break; + +#if 0 + case BX_ATA_MODE_VMWARE3: + BX_INFO(("HD on ata%d-%d: '%s' 'vmware3' mode ", channel, device, + bx_options.atadevice[channel][device].Opath->getptr ())); + channels[channel].drives[device].hard_drive = new vmware3_image_t(); + break; + + case BX_ATA_MODE_SPLIT: + BX_INFO(("HD on ata%d-%d: '%s' 'split' mode ", channel, device, + bx_options.atadevice[channel][device].Opath->getptr ())); + channels[channel].drives[device].hard_drive = new split_image_t(); + break; +#endif + + case BX_ATA_MODE_UNDOABLE: + BX_INFO(("HD on ata%d-%d: '%s' 'undoable' mode ", channel, device, + bx_options.atadevice[channel][device].Opath->getptr ())); + channels[channel].drives[device].hard_drive = new undoable_image_t(disk_size, + bx_options.atadevice[channel][device].Ojournal->getptr()); + break; + + case BX_ATA_MODE_GROWING: + BX_INFO(("HD on ata%d-%d: '%s' 'growing' mode ", channel, device, + bx_options.atadevice[channel][device].Opath->getptr ())); + channels[channel].drives[device].hard_drive = new growing_image_t(disk_size); + break; + + case BX_ATA_MODE_VOLATILE: + BX_INFO(("HD on ata%d-%d: '%s' 'volatile' mode ", channel, device, + bx_options.atadevice[channel][device].Opath->getptr ())); + channels[channel].drives[device].hard_drive = new volatile_image_t(disk_size, + bx_options.atadevice[channel][device].Ojournal->getptr()); + break; + +#if 0 +#if BX_COMPRESSED_HD_SUPPORT + case BX_ATA_MODE_Z_UNDOABLE: + BX_PANIC(("z-undoable disk support not implemented")); + BX_INFO(("HD on ata%d-%d: '%s' 'z-undoable' mode ", channel, device, + bx_options.atadevice[channel][device].Opath->getptr ())); + channels[channel].drives[device].hard_drive = new z_undoable_image_t(disk_size, + bx_options.atadevice[channel][device].Ojournal->getptr()); + break; + + case BX_ATA_MODE_Z_VOLATILE: + BX_PANIC(("z-volatile disk support not implemented")); + BX_INFO(("HD on ata%d-%d: '%s' 'z-volatile' mode ", channel, device, + bx_options.atadevice[channel][device].Opath->getptr ())); + channels[channel].drives[device].hard_drive = new z_volatile_image_t(disk_size, + bx_options.atadevice[channel][device].Ojournal->getptr()); + break; +#endif //BX_COMPRESSED_HD_SUPPORT +#endif + + default: + BX_PANIC(("HD on ata%d-%d: '%s' unsupported HD mode : %s", channel, device, + bx_options.atadevice[channel][device].Opath->getptr (), + atadevice_mode_names[bx_options.atadevice[channel][device].Omode->get()])); + break; + } + + BX_HD_THIS channels[channel].drives[device].hard_drive->cylinders = cyl; + BX_HD_THIS channels[channel].drives[device].hard_drive->heads = heads; + BX_HD_THIS channels[channel].drives[device].hard_drive->sectors = spt; + + if (cyl == 0 || heads == 0 || spt == 0) { + BX_PANIC(("ata%d/%d cannot have zero cylinders, heads, or sectors/track", channel, device)); + } + + /* open hard drive image file */ + if ((BX_HD_THIS channels[channel].drives[device].hard_drive->open(bx_options.atadevice[channel][device].Opath->getptr ())) < 0) { + BX_PANIC(("ata%d-%d: could not open hard drive image file '%s'", channel, device, bx_options.atadevice[channel][device].Opath->getptr ())); + } + } + else if (bx_options.atadevice[channel][device].Otype->get() == BX_ATA_DEVICE_CDROM) { + BX_DEBUG(( "CDROM on target %d/%d",channel,device)); + BX_HD_THIS channels[channel].drives[device].device_type = IDE_CDROM; + BX_HD_THIS channels[channel].drives[device].cdrom.locked = 0; + BX_HD_THIS channels[channel].drives[device].sense.sense_key = SENSE_NONE; + BX_HD_THIS channels[channel].drives[device].sense.asc = 0; + BX_HD_THIS channels[channel].drives[device].sense.ascq = 0; + + // Check bit fields + BX_CONTROLLER(channel,device).sector_count = 0; + BX_CONTROLLER(channel,device).interrupt_reason.c_d = 1; + if (BX_CONTROLLER(channel,device).sector_count != 0x01) + BX_PANIC(("interrupt reason bit field error")); + + BX_CONTROLLER(channel,device).sector_count = 0; + BX_CONTROLLER(channel,device).interrupt_reason.i_o = 1; + if (BX_CONTROLLER(channel,device).sector_count != 0x02) + BX_PANIC(("interrupt reason bit field error")); + + BX_CONTROLLER(channel,device).sector_count = 0; + BX_CONTROLLER(channel,device).interrupt_reason.rel = 1; + if (BX_CONTROLLER(channel,device).sector_count != 0x04) + BX_PANIC(("interrupt reason bit field error")); + + BX_CONTROLLER(channel,device).sector_count = 0; + BX_CONTROLLER(channel,device).interrupt_reason.tag = 3; + if (BX_CONTROLLER(channel,device).sector_count != 0x18) + BX_PANIC(("interrupt reason bit field error")); + BX_CONTROLLER(channel,device).sector_count = 0; + + // allocate low level driver +#ifdef LOWLEVEL_CDROM + BX_HD_THIS channels[channel].drives[device].cdrom.cd = new LOWLEVEL_CDROM(bx_options.atadevice[channel][device].Opath->getptr ()); + BX_INFO(("CD on ata%d-%d: '%s'",channel, device, bx_options.atadevice[channel][device].Opath->getptr ())); + + if (bx_options.atadevice[channel][device].Ostatus->get () == BX_INSERTED) { + if (BX_HD_THIS channels[channel].drives[device].cdrom.cd->insert_cdrom()) { + BX_INFO(( "Media present in CD-ROM drive")); + BX_HD_THIS channels[channel].drives[device].cdrom.ready = 1; + BX_HD_THIS channels[channel].drives[device].cdrom.capacity = BX_HD_THIS channels[channel].drives[device].cdrom.cd->capacity(); + } else { + BX_INFO(( "Could not locate CD-ROM, continuing with media not present")); + BX_HD_THIS channels[channel].drives[device].cdrom.ready = 0; + bx_options.atadevice[channel][device].Ostatus->set(BX_EJECTED); + } + } else { +#endif + BX_INFO(( "Media not present in CD-ROM drive" )); + BX_HD_THIS channels[channel].drives[device].cdrom.ready = 0; +#ifdef LOWLEVEL_CDROM + } +#endif + } + } + } + +#if BX_PDC20230C_VLBIDE_SUPPORT + BX_HD_THIS pdc20230c.prog_mode = 0; + BX_HD_THIS pdc20230c.prog_count = 0; + BX_HD_THIS pdc20230c.p1f3_value = 0; + BX_HD_THIS pdc20230c.p1f4_value = 0; +#endif + + + // generate CMOS values for hard drive if not using a CMOS image + if (!bx_options.cmos.OcmosImage->get ()) { + DEV_cmos_set_reg(0x12, 0x00); // start out with: no drive 0, no drive 1 + + if (BX_DRIVE_IS_HD(0,0)) { + // Flag drive type as Fh, use extended CMOS location as real type + DEV_cmos_set_reg(0x12, (DEV_cmos_get_reg(0x12) & 0x0f) | 0xf0); + DEV_cmos_set_reg(0x19, 47); // user definable type + // AMI BIOS: 1st hard disk #cyl low byte + DEV_cmos_set_reg(0x1b, (bx_options.atadevice[0][0].Ocylinders->get () & 0x00ff)); + // AMI BIOS: 1st hard disk #cyl high byte + DEV_cmos_set_reg(0x1c, (bx_options.atadevice[0][0].Ocylinders->get () & 0xff00) >> 8); + // AMI BIOS: 1st hard disk #heads + DEV_cmos_set_reg(0x1d, (bx_options.atadevice[0][0].Oheads->get ())); + // AMI BIOS: 1st hard disk write precompensation cylinder, low byte + DEV_cmos_set_reg(0x1e, 0xff); // -1 + // AMI BIOS: 1st hard disk write precompensation cylinder, high byte + DEV_cmos_set_reg(0x1f, 0xff); // -1 + // AMI BIOS: 1st hard disk control byte + DEV_cmos_set_reg(0x20, (0xc0 | ((bx_options.atadevice[0][0].Oheads->get () > 8) << 3))); + // AMI BIOS: 1st hard disk landing zone, low byte + DEV_cmos_set_reg(0x21, DEV_cmos_get_reg(0x1b)); + // AMI BIOS: 1st hard disk landing zone, high byte + DEV_cmos_set_reg(0x22, DEV_cmos_get_reg(0x1c)); + // AMI BIOS: 1st hard disk sectors/track + DEV_cmos_set_reg(0x23, bx_options.atadevice[0][0].Ospt->get ()); + } + + //set up cmos for second hard drive + if (BX_DRIVE_IS_HD(0,1)) { + BX_DEBUG(("1: I will put 0xf into the second hard disk field")); + // fill in lower 4 bits of 0x12 for second HD + DEV_cmos_set_reg(0x12, (DEV_cmos_get_reg(0x12) & 0xf0) | 0x0f); + DEV_cmos_set_reg(0x1a, 47); // user definable type + // AMI BIOS: 2nd hard disk #cyl low byte + DEV_cmos_set_reg(0x24, (bx_options.atadevice[0][1].Ocylinders->get () & 0x00ff)); + // AMI BIOS: 2nd hard disk #cyl high byte + DEV_cmos_set_reg(0x25, (bx_options.atadevice[0][1].Ocylinders->get () & 0xff00) >> 8); + // AMI BIOS: 2nd hard disk #heads + DEV_cmos_set_reg(0x26, (bx_options.atadevice[0][1].Oheads->get ())); + // AMI BIOS: 2nd hard disk write precompensation cylinder, low byte + DEV_cmos_set_reg(0x27, 0xff); // -1 + // AMI BIOS: 2nd hard disk write precompensation cylinder, high byte + DEV_cmos_set_reg(0x28, 0xff); // -1 + // AMI BIOS: 2nd hard disk, 0x80 if heads>8 + DEV_cmos_set_reg(0x29, (bx_options.atadevice[0][1].Oheads->get () > 8) ? 0x80 : 0x00); + // AMI BIOS: 2nd hard disk landing zone, low byte + DEV_cmos_set_reg(0x2a, DEV_cmos_get_reg(0x24)); + // AMI BIOS: 2nd hard disk landing zone, high byte + DEV_cmos_set_reg(0x2b, DEV_cmos_get_reg(0x25)); + // AMI BIOS: 2nd hard disk sectors/track + DEV_cmos_set_reg(0x2c, bx_options.atadevice[0][1].Ospt->get ()); + } + + DEV_cmos_set_reg(0x39, 0); + DEV_cmos_set_reg(0x3a, 0); + for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) { + for (Bit8u device=0; device<2; device ++) { + if (bx_options.atadevice[channel][device].Opresent->get()) { + if (BX_DRIVE_IS_HD(channel,device)) { + Bit16u cylinders = bx_options.atadevice[channel][device].Ocylinders->get(); + Bit16u heads = bx_options.atadevice[channel][device].Oheads->get(); + Bit16u spt = bx_options.atadevice[channel][device].Ospt->get(); + Bit8u translation = bx_options.atadevice[channel][device].Otranslation->get(); + + Bit8u reg = 0x39 + channel/2; + Bit8u bitshift = 2 * (device+(2 * (channel%2))); + + // Find the right translation if autodetect + if (translation == BX_ATA_TRANSLATION_AUTO) { + if((cylinders <= 1024) && (heads <= 16) && (spt <= 63)) { + translation = BX_ATA_TRANSLATION_NONE; + } + else if (((Bit32u)cylinders * (Bit32u)heads) <= 131072) { + translation = BX_ATA_TRANSLATION_LARGE; + } + else translation = BX_ATA_TRANSLATION_LBA; + + BX_INFO(("translation on ata%d-%d set to '%s'",channel, device, + translation==BX_ATA_TRANSLATION_NONE?"none": + translation==BX_ATA_TRANSLATION_LARGE?"large": + "lba")); + } + + // FIXME we should test and warn + // - if LBA and spt != 63 + // - if RECHS and heads != 16 + // - if NONE and size > 1024*16*SPT blocks + // - if LARGE and size > 8192*16*SPT blocks + // - if RECHS and size > 1024*240*SPT blocks + // - if LBA and size > 1024*255*63, not that we can do much about it + + switch(translation) { + case BX_ATA_TRANSLATION_NONE: + DEV_cmos_set_reg(reg, DEV_cmos_get_reg(reg) | (0 << bitshift)); + break; + case BX_ATA_TRANSLATION_LBA: + DEV_cmos_set_reg(reg, DEV_cmos_get_reg(reg) | (1 << bitshift)); + break; + case BX_ATA_TRANSLATION_LARGE: + DEV_cmos_set_reg(reg, DEV_cmos_get_reg(reg) | (2 << bitshift)); + break; + case BX_ATA_TRANSLATION_RECHS: + DEV_cmos_set_reg(reg, DEV_cmos_get_reg(reg) | (3 << bitshift)); + break; + } + } + } + } + } + + // Set the "non-extended" boot device. This will default to DISKC if cdrom + if ( bx_options.Obootdrive->get () != BX_BOOT_FLOPPYA) { + // system boot sequence C:, A: + DEV_cmos_set_reg(0x2d, DEV_cmos_get_reg(0x2d) & 0xdf); + } + else { // 'a' + // system boot sequence A:, C: + DEV_cmos_set_reg(0x2d, DEV_cmos_get_reg(0x2d) | 0x20); + } + + // Set the "extended" boot device, byte 0x3D (needed for cdrom booting) + if ( bx_options.Obootdrive->get () == BX_BOOT_FLOPPYA) { + // system boot sequence A: + DEV_cmos_set_reg(0x3d, 0x01); + BX_INFO(("Boot device will be 'a'")); + } + else if ( bx_options.Obootdrive->get () == BX_BOOT_DISKC) { + // system boot sequence C: + DEV_cmos_set_reg(0x3d, 0x02); + BX_INFO(("Boot device will be 'c'")); + } + else if ( bx_options.Obootdrive->get () == BX_BOOT_CDROM) { + // system boot sequence cdrom + DEV_cmos_set_reg(0x3d, 0x03); + BX_INFO(("Boot device will be 'cdrom'")); + } + + // Set the signature check flag in cmos, inverted for compatibility + DEV_cmos_set_reg(0x38, bx_options.OfloppySigCheck->get()); + BX_INFO(("Floppy boot signature check is %sabled", bx_options.OfloppySigCheck->get() ? "dis" : "en")); + } + +} + + void +bx_hard_drive_c::reset(unsigned type) +{ + for (unsigned channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) { + if (BX_HD_THIS channels[channel].irq) + DEV_pic_lower_irq(BX_HD_THIS channels[channel].irq); + } +} + + +#define GOTO_RETURN_VALUE if(io_len==4){\ + goto return_value32;\ + }\ + else if(io_len==2){\ + value16=(Bit16u)value32;\ + goto return_value16;\ + }\ + else{\ + value8=(Bit8u)value32;\ + goto return_value8;\ + } + + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_hard_drive_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_HD_SMF + bx_hard_drive_c *class_ptr = (bx_hard_drive_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + Bit32u +bx_hard_drive_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_HD_SMF + Bit8u value8; + Bit16u value16; + Bit32u value32; + + Bit8u channel = BX_MAX_ATA_CHANNEL; + Bit32u port = 0xff; // undefined + + for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) { + if ((address & 0xfff8) == BX_HD_THIS channels[channel].ioaddr1) { + port = address - BX_HD_THIS channels[channel].ioaddr1; + break; + } + else if ((address & 0xfff8) == BX_HD_THIS channels[channel].ioaddr2) { + port = address - BX_HD_THIS channels[channel].ioaddr2 + 0x10; + break; + } + } + + if (channel == BX_MAX_ATA_CHANNEL) { + if ((address < 0x03f6) || (address > 0x03f7)) { + BX_PANIC(("read: unable to find ATA channel, ioport=0x%04x", address)); + } else { + channel = 0; + port = address - 0x03e0; + } + } + +#if BX_PDC20230C_VLBIDE_SUPPORT +// pdc20230c is only available for first ata channel +if (channel == 0) { + + // Detect the switch to programming mode + if (!BX_HD_THIS pdc20230c.prog_mode) { + switch (port) { + case 0x02: + if ((BX_HD_THIS pdc20230c.prog_count == 0) || (BX_HD_THIS pdc20230c.prog_count > 2)) { + BX_HD_THIS pdc20230c.prog_count++; + } + else { + BX_HD_THIS pdc20230c.prog_count=0; + } + break; + case 0x16: + if ((BX_HD_THIS pdc20230c.prog_count == 1) || (BX_HD_THIS pdc20230c.prog_count == 2)) { + BX_HD_THIS pdc20230c.prog_count++; + } + else { + BX_HD_THIS pdc20230c.prog_count=0; + } + break; + default: + BX_HD_THIS pdc20230c.prog_count=0; + } + + if (BX_HD_THIS pdc20230c.prog_count == 5) { + BX_HD_THIS pdc20230c.prog_mode = 1; + BX_SELECTED_CONTROLLER(channel).sector_count &= 0x7f; + BX_INFO(("Promise VLB-IDE DC2300: Switching to Programming mode")); + } + } + + // Returns value when in programming mode + if (BX_HD_THIS pdc20230c.prog_mode) { + switch (port) { + case 0x05: + // Leave programming mode + BX_HD_THIS pdc20230c.prog_mode = 0; + BX_INFO(("Promise VLB-IDE DC2300: Leaving Programming mode")); + // Value will be sent be normal code + break; + case 0x03: + // Special programming register + value32 = BX_HD_THIS pdc20230c.p1f3_value; + GOTO_RETURN_VALUE ; + break; + case 0x04: + // Special programming register + value32 = BX_HD_THIS pdc20230c.p1f4_value; + GOTO_RETURN_VALUE ; + break; + } + } +} +#endif + + switch (port) { + case 0x00: // hard disk data (16bit) 0x1f0 + if (BX_SELECTED_CONTROLLER(channel).status.drq == 0) { + BX_ERROR(("IO read(0x%04x) with drq == 0: last command was %02xh", + address, (unsigned) BX_SELECTED_CONTROLLER(channel).current_command)); + return(0); + } + BX_DEBUG(("IO read(0x%04x): current command is %02xh", + address, (unsigned) BX_SELECTED_CONTROLLER(channel).current_command)); + switch (BX_SELECTED_CONTROLLER(channel).current_command) { + case 0x20: // READ SECTORS, with retries + case 0x21: // READ SECTORS, without retries + if (BX_SELECTED_CONTROLLER(channel).buffer_index >= 512) + BX_PANIC(("IO read(0x%04x): buffer_index >= 512", address)); + +#if BX_SupportRepeatSpeedups + if (DEV_bulk_io_quantum_requested()) { + unsigned transferLen, quantumsMax; + + quantumsMax = + (512 - BX_SELECTED_CONTROLLER(channel).buffer_index) / io_len; + if ( quantumsMax == 0) + BX_PANIC(("IO read(0x%04x): not enough space for read", address)); + DEV_bulk_io_quantum_transferred() = + DEV_bulk_io_quantum_requested(); + if (quantumsMax < DEV_bulk_io_quantum_transferred()) + DEV_bulk_io_quantum_transferred() = quantumsMax; + transferLen = io_len * DEV_bulk_io_quantum_transferred(); + memcpy((Bit8u*) DEV_bulk_io_host_addr(), + &BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index], + transferLen); + DEV_bulk_io_host_addr() += transferLen; + BX_SELECTED_CONTROLLER(channel).buffer_index += transferLen; + value32 = 0; // Value returned not important; + } + else +#endif + { + value32 = 0L; + switch(io_len){ + case 4: + value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index+3] << 24); + value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index+2] << 16); + case 2: + value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index+1] << 8); + value32 |= BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index]; + } + BX_SELECTED_CONTROLLER(channel).buffer_index += io_len; + } + + // if buffer completely read + if (BX_SELECTED_CONTROLLER(channel).buffer_index >= 512) { + // update sector count, sector number, cylinder, + // drive, head, status + // if there are more sectors, read next one in... + // + BX_SELECTED_CONTROLLER(channel).buffer_index = 0; + + increment_address(channel); + + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.write_fault = 0; + if (bx_options.OnewHardDriveSupport->get ()) + BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1; + else + BX_SELECTED_CONTROLLER(channel).status.seek_complete = 0; + BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + + if (BX_SELECTED_CONTROLLER(channel).sector_count==0) { + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + } + else { /* read next one into controller buffer */ + off_t logical_sector; + off_t ret; + + BX_SELECTED_CONTROLLER(channel).status.drq = 1; + BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1; + +#if TEST_READ_BEYOND_END==1 + BX_SELECTED_CONTROLLER(channel).cylinder_no += 100000; +#endif + if (!calculate_logical_address(channel, &logical_sector)) { + BX_ERROR(("multi-sector read reached invalid sector %lu, aborting", (unsigned long)logical_sector)); + command_aborted (channel, BX_SELECTED_CONTROLLER(channel).current_command); + GOTO_RETURN_VALUE ; + } + ret = BX_SELECTED_DRIVE(channel).hard_drive->lseek(logical_sector * 512, SEEK_SET); + if (ret < 0) { + BX_ERROR(("could not lseek() hard drive image file")); + command_aborted (channel, BX_SELECTED_CONTROLLER(channel).current_command); + GOTO_RETURN_VALUE ; + } + ret = BX_SELECTED_DRIVE(channel).hard_drive->read((bx_ptr_t) BX_SELECTED_CONTROLLER(channel).buffer, 512); + if (ret < 512) { + BX_ERROR(("logical sector was %lu", (unsigned long)logical_sector)); + BX_ERROR(("could not read() hard drive image file at byte %lu", (unsigned long)logical_sector*512)); + command_aborted (channel, BX_SELECTED_CONTROLLER(channel).current_command); + GOTO_RETURN_VALUE ; + } + + BX_SELECTED_CONTROLLER(channel).buffer_index = 0; + raise_interrupt(channel); + } + } + GOTO_RETURN_VALUE ; + break; + + case 0xec: // IDENTIFY DEVICE + case 0xa1: + if (bx_options.OnewHardDriveSupport->get ()) { + unsigned index; + + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.write_fault = 0; + BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1; + BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + + index = BX_SELECTED_CONTROLLER(channel).buffer_index; + value32 = BX_SELECTED_CONTROLLER(channel).buffer[index]; + index++; + if (io_len >= 2) { + value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[index] << 8); + index++; + } + if (io_len == 4) { + value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[index] << 16); + value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[index+1] << 24); + index += 2; + } + BX_SELECTED_CONTROLLER(channel).buffer_index = index; + + if (BX_SELECTED_CONTROLLER(channel).buffer_index >= 512) { + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom)) + BX_INFO(("Read all drive ID Bytes ...")); + } + GOTO_RETURN_VALUE; + } + else + BX_PANIC(("IO read(0x%04x): current command is %02xh", address, + (unsigned) BX_SELECTED_CONTROLLER(channel).current_command)); + + case 0xa0: { + unsigned index = BX_SELECTED_CONTROLLER(channel).buffer_index; + unsigned increment = 0; + + // Load block if necessary + if (index >= 2048) { + if (index > 2048) + BX_PANIC(("index > 2048 : 0x%x",index)); + switch (BX_SELECTED_DRIVE(channel).atapi.command) { + case 0x28: // read (10) + case 0xa8: // read (12) +#ifdef LOWLEVEL_CDROM + if (!BX_SELECTED_DRIVE(channel).cdrom.ready) { + BX_PANIC(("Read with CDROM not ready")); + } + BX_SELECTED_DRIVE(channel).cdrom.cd->read_block(BX_SELECTED_CONTROLLER(channel).buffer, + BX_SELECTED_DRIVE(channel).cdrom.next_lba); + BX_SELECTED_DRIVE(channel).cdrom.next_lba++; + BX_SELECTED_DRIVE(channel).cdrom.remaining_blocks--; + + if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom)) + if (!BX_SELECTED_DRIVE(channel).cdrom.remaining_blocks) + BX_INFO(("Last READ block loaded {CDROM}")); + else + BX_INFO(("READ block loaded (%d remaining) {CDROM}", + BX_SELECTED_DRIVE(channel).cdrom.remaining_blocks)); + + // one block transfered, start at beginning + index = 0; +#else + BX_PANIC(("Read with no LOWLEVEL_CDROM")); +#endif + break; + + default: // no need to load a new block + break; + } + } + + value32 = BX_SELECTED_CONTROLLER(channel).buffer[index+increment]; + increment++; + if (io_len >= 2) { + value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[index+increment] << 8); + increment++; + } + if (io_len == 4) { + value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[index+increment] << 16); + value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[index+increment+1] << 24); + increment += 2; + } + BX_SELECTED_CONTROLLER(channel).buffer_index = index + increment; + BX_SELECTED_CONTROLLER(channel).drq_index += increment; + + if (BX_SELECTED_CONTROLLER(channel).drq_index >= (unsigned)BX_SELECTED_DRIVE(channel).atapi.drq_bytes) { + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).drq_index = 0; + + BX_SELECTED_DRIVE(channel).atapi.total_bytes_remaining -= BX_SELECTED_DRIVE(channel).atapi.drq_bytes; + + if (BX_SELECTED_DRIVE(channel).atapi.total_bytes_remaining > 0) { + // one or more blocks remaining (works only for single block commands) + if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom)) + BX_INFO(("PACKET drq bytes read")); + BX_SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1; + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drq = 1; + BX_SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 0; + + // set new byte count if last block + if (BX_SELECTED_DRIVE(channel).atapi.total_bytes_remaining < BX_SELECTED_CONTROLLER(channel).byte_count) { + BX_SELECTED_CONTROLLER(channel).byte_count = BX_SELECTED_DRIVE(channel).atapi.total_bytes_remaining; + } + BX_SELECTED_DRIVE(channel).atapi.drq_bytes = BX_SELECTED_CONTROLLER(channel).byte_count; + + raise_interrupt(channel); + } else { + // all bytes read + if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom)) + BX_INFO(("PACKET all bytes read")); + BX_SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1; + BX_SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 1; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).interrupt_reason.rel = 0; + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + + raise_interrupt(channel); + } + } + GOTO_RETURN_VALUE; + break; + } + + // List all the read operations that are defined in the ATA/ATAPI spec + // that we don't support. Commands that are listed here will cause a + // BX_ERROR, which is non-fatal, and the command will be aborted. + case 0x08: BX_ERROR(("read cmd 0x08 (DEVICE RESET) not supported")); command_aborted(channel, 0x08); break; + case 0x10: BX_ERROR(("read cmd 0x10 (RECALIBRATE) not supported")); command_aborted(channel, 0x10); break; + case 0x22: BX_ERROR(("read cmd 0x22 (READ LONG) not supported")); command_aborted(channel, 0x22); break; + case 0x23: BX_ERROR(("read cmd 0x23 (READ LONG NO RETRY) not supported")); command_aborted(channel, 0x23); break; + case 0x24: BX_ERROR(("read cmd 0x24 (READ SECTORS EXT) not supported")); command_aborted(channel, 0x24); break; + case 0x25: BX_ERROR(("read cmd 0x25 (READ DMA EXT) not supported")); command_aborted(channel, 0x25); break; + case 0x26: BX_ERROR(("read cmd 0x26 (READ DMA QUEUED EXT) not supported")); command_aborted(channel, 0x26); break; + case 0x27: BX_ERROR(("read cmd 0x27 (READ NATIVE MAX ADDRESS EXT) not supported")); command_aborted(channel, 0x27); break; + case 0x29: BX_ERROR(("read cmd 0x29 (READ MULTIPLE EXT) not supported")); command_aborted(channel, 0x29); break; + case 0x2A: BX_ERROR(("read cmd 0x2A (READ STREAM DMA) not supported")); command_aborted(channel, 0x2A); break; + case 0x2B: BX_ERROR(("read cmd 0x2B (READ STREAM PIO) not supported")); command_aborted(channel, 0x2B); break; + case 0x2F: BX_ERROR(("read cmd 0x2F (READ LOG EXT) not supported")); command_aborted(channel, 0x2F); break; + case 0x30: BX_ERROR(("read cmd 0x30 (WRITE SECTORS) not supported")); command_aborted(channel, 0x30); break; + case 0x31: BX_ERROR(("read cmd 0x31 (WRITE SECTORS NO RETRY) not supported")); command_aborted(channel, 0x31); break; + case 0x32: BX_ERROR(("read cmd 0x32 (WRITE LONG) not supported")); command_aborted(channel, 0x32); break; + case 0x33: BX_ERROR(("read cmd 0x33 (WRITE LONG NO RETRY) not supported")); command_aborted(channel, 0x33); break; + case 0x34: BX_ERROR(("read cmd 0x34 (WRITE SECTORS EXT) not supported")); command_aborted(channel, 0x34); break; + case 0x35: BX_ERROR(("read cmd 0x35 (WRITE DMA EXT) not supported")); command_aborted(channel, 0x35); break; + case 0x36: BX_ERROR(("read cmd 0x36 (WRITE DMA QUEUED EXT) not supported")); command_aborted(channel, 0x36); break; + case 0x37: BX_ERROR(("read cmd 0x37 (SET MAX ADDRESS EXT) not supported")); command_aborted(channel, 0x37); break; + case 0x38: BX_ERROR(("read cmd 0x38 (CFA WRITE SECTORS W/OUT ERASE) not supported")); command_aborted(channel, 0x38); break; + case 0x39: BX_ERROR(("read cmd 0x39 (WRITE MULTIPLE EXT) not supported")); command_aborted(channel, 0x39); break; + case 0x3A: BX_ERROR(("read cmd 0x3A (WRITE STREAM DMA) not supported")); command_aborted(channel, 0x3A); break; + case 0x3B: BX_ERROR(("read cmd 0x3B (WRITE STREAM PIO) not supported")); command_aborted(channel, 0x3B); break; + case 0x3F: BX_ERROR(("read cmd 0x3F (WRITE LOG EXT) not supported")); command_aborted(channel, 0x3F); break; + case 0x40: BX_ERROR(("read cmd 0x40 (READ VERIFY SECTORS) not supported")); command_aborted(channel, 0x40); break; + case 0x41: BX_ERROR(("read cmd 0x41 (READ VERIFY SECTORS NO RETRY) not supported")); command_aborted(channel, 0x41); break; + case 0x42: BX_ERROR(("read cmd 0x42 (READ VERIFY SECTORS EXT) not supported")); command_aborted(channel, 0x42); break; + case 0x50: BX_ERROR(("read cmd 0x50 (FORMAT TRACK) not supported")); command_aborted(channel, 0x50); break; + case 0x51: BX_ERROR(("read cmd 0x51 (CONFIGURE STREAM) not supported")); command_aborted(channel, 0x51); break; + case 0x70: BX_ERROR(("read cmd 0x70 (SEEK) not supported")); command_aborted(channel, 0x70); break; + case 0x87: BX_ERROR(("read cmd 0x87 (CFA TRANSLATE SECTOR) not supported")); command_aborted(channel, 0x87); break; + case 0x90: BX_ERROR(("read cmd 0x90 (EXECUTE DEVICE DIAGNOSTIC) not supported")); command_aborted(channel, 0x90); break; + case 0x91: BX_ERROR(("read cmd 0x91 (INITIALIZE DEVICE PARAMETERS) not supported")); command_aborted(channel, 0x91); break; + case 0x92: BX_ERROR(("read cmd 0x92 (DOWNLOAD MICROCODE) not supported")); command_aborted(channel, 0x92); break; + case 0x94: BX_ERROR(("read cmd 0x94 (STANDBY IMMEDIATE) not supported")); command_aborted(channel, 0x94); break; + case 0x95: BX_ERROR(("read cmd 0x95 (IDLE IMMEDIATE) not supported")); command_aborted(channel, 0x95); break; + case 0x96: BX_ERROR(("read cmd 0x96 (STANDBY) not supported")); command_aborted(channel, 0x96); break; + case 0x97: BX_ERROR(("read cmd 0x97 (IDLE) not supported")); command_aborted(channel, 0x97); break; + case 0x98: BX_ERROR(("read cmd 0x98 (CHECK POWER MODE) not supported")); command_aborted(channel, 0x98); break; + case 0x99: BX_ERROR(("read cmd 0x99 (SLEEP) not supported")); command_aborted(channel, 0x99); break; + case 0xA2: BX_ERROR(("read cmd 0xA2 (SERVICE) not supported")); command_aborted(channel, 0xA2); break; + case 0xB0: BX_ERROR(("read cmd 0xB0 (SMART DISABLE OPERATIONS) not supported")); command_aborted(channel, 0xB0); break; + case 0xB1: BX_ERROR(("read cmd 0xB1 (DEVICE CONFIGURATION FREEZE LOCK) not supported")); command_aborted(channel, 0xB1); break; + case 0xC0: BX_ERROR(("read cmd 0xC0 (CFA ERASE SECTORS) not supported")); command_aborted(channel, 0xC0); break; + case 0xC4: BX_ERROR(("read cmd 0xC4 (READ MULTIPLE) not supported")); command_aborted(channel, 0xC4); break; + case 0xC5: BX_ERROR(("read cmd 0xC5 (WRITE MULTIPLE) not supported")); command_aborted(channel, 0xC5); break; + case 0xC6: BX_ERROR(("read cmd 0xC6 (SET MULTIPLE MODE) not supported")); command_aborted(channel, 0xC6); break; + case 0xC7: BX_ERROR(("read cmd 0xC7 (READ DMA QUEUED) not supported")); command_aborted(channel, 0xC7); break; + case 0xC8: BX_ERROR(("read cmd 0xC8 (READ DMA) not supported")); command_aborted(channel, 0xC8); break; + case 0xC9: BX_ERROR(("read cmd 0xC9 (READ DMA NO RETRY) not supported")); command_aborted(channel, 0xC9); break; + case 0xCA: BX_ERROR(("read cmd 0xCA (WRITE DMA) not supported")); command_aborted(channel, 0xCA); break; + case 0xCC: BX_ERROR(("read cmd 0xCC (WRITE DMA QUEUED) not supported")); command_aborted(channel, 0xCC); break; + case 0xCD: BX_ERROR(("read cmd 0xCD (CFA WRITE MULTIPLE W/OUT ERASE) not supported")); command_aborted(channel, 0xCD); break; + case 0xD1: BX_ERROR(("read cmd 0xD1 (CHECK MEDIA CARD TYPE) not supported")); command_aborted(channel, 0xD1); break; + case 0xDA: BX_ERROR(("read cmd 0xDA (GET MEDIA STATUS) not supported")); command_aborted(channel, 0xDA); break; + case 0xDE: BX_ERROR(("read cmd 0xDE (MEDIA LOCK) not supported")); command_aborted(channel, 0xDE); break; + case 0xDF: BX_ERROR(("read cmd 0xDF (MEDIA UNLOCK) not supported")); command_aborted(channel, 0xDF); break; + case 0xE0: BX_ERROR(("read cmd 0xE0 (STANDBY IMMEDIATE) not supported")); command_aborted(channel, 0xE0); break; + case 0xE1: BX_ERROR(("read cmd 0xE1 (IDLE IMMEDIATE) not supported")); command_aborted(channel, 0xE1); break; + case 0xE2: BX_ERROR(("read cmd 0xE2 (STANDBY) not supported")); command_aborted(channel, 0xE2); break; + case 0xE3: BX_ERROR(("read cmd 0xE3 (IDLE) not supported")); command_aborted(channel, 0xE3); break; + case 0xE4: BX_ERROR(("read cmd 0xE4 (READ BUFFER) not supported")); command_aborted(channel, 0xE4); break; + case 0xE5: BX_ERROR(("read cmd 0xE5 (CHECK POWER MODE) not supported")); command_aborted(channel, 0xE5); break; + case 0xE6: BX_ERROR(("read cmd 0xE6 (SLEEP) not supported")); command_aborted(channel, 0xE6); break; + case 0xE7: BX_ERROR(("read cmd 0xE7 (FLUSH CACHE) not supported")); command_aborted(channel, 0xE7); break; + case 0xE8: BX_ERROR(("read cmd 0xE8 (WRITE BUFFER) not supported")); command_aborted(channel, 0xE8); break; + case 0xEA: BX_ERROR(("read cmd 0xEA (FLUSH CACHE EXT) not supported")); command_aborted(channel, 0xEA); break; + case 0xED: BX_ERROR(("read cmd 0xED (MEDIA EJECT) not supported")); command_aborted(channel, 0xED); break; + case 0xEF: BX_ERROR(("read cmd 0xEF (SET FEATURES) not supported")); command_aborted(channel, 0xEF); break; + case 0xF1: BX_ERROR(("read cmd 0xF1 (SECURITY SET PASSWORD) not supported")); command_aborted(channel, 0xF1); break; + case 0xF2: BX_ERROR(("read cmd 0xF2 (SECURITY UNLOCK) not supported")); command_aborted(channel, 0xF2); break; + case 0xF3: BX_ERROR(("read cmd 0xF3 (SECURITY ERASE PREPARE) not supported")); command_aborted(channel, 0xF3); break; + case 0xF4: BX_ERROR(("read cmd 0xF4 (SECURITY ERASE UNIT) not supported")); command_aborted(channel, 0xF4); break; + case 0xF5: BX_ERROR(("read cmd 0xF5 (SECURITY FREEZE LOCK) not supported")); command_aborted(channel, 0xF5); break; + case 0xF6: BX_ERROR(("read cmd 0xF6 (SECURITY DISABLE PASSWORD) not supported")); command_aborted(channel, 0xF6); break; + case 0xF8: BX_ERROR(("read cmd 0xF8 (READ NATIVE MAX ADDRESS) not supported")); command_aborted(channel, 0xF8); break; + case 0xF9: BX_ERROR(("read cmd 0xF9 (SET MAX ADDRESS) not supported")); command_aborted(channel, 0xF9); break; + + default: + BX_PANIC(("IO read(0x%04x): current command is %02xh", address, + (unsigned) BX_SELECTED_CONTROLLER(channel).current_command)); + } + break; + + case 0x01: // hard disk error register 0x1f1 + BX_SELECTED_CONTROLLER(channel).status.err = 0; + value8 = (!BX_SELECTED_IS_PRESENT(channel)) ? 0 : BX_SELECTED_CONTROLLER(channel).error_register; + goto return_value8; + break; + case 0x02: // hard disk sector count / interrupt reason 0x1f2 + value8 = (!BX_SELECTED_IS_PRESENT(channel)) ? 0 : BX_SELECTED_CONTROLLER(channel).sector_count; + goto return_value8; + break; + case 0x03: // sector number 0x1f3 + value8 = (!BX_SELECTED_IS_PRESENT(channel)) ? 0 : BX_SELECTED_CONTROLLER(channel).sector_no; + goto return_value8; + case 0x04: // cylinder low 0x1f4 + // -- WARNING : On real hardware the controller registers are shared between drives. + // So we must respond even if the select device is not present. Some OS uses this fact + // to detect the disks.... minix2 for example + value8 = (!BX_ANY_IS_PRESENT(channel)) ? 0 : (BX_SELECTED_CONTROLLER(channel).cylinder_no & 0x00ff); + goto return_value8; + case 0x05: // cylinder high 0x1f5 + // -- WARNING : On real hardware the controller registers are shared between drives. + // So we must respond even if the select device is not present. Some OS uses this fact + // to detect the disks.... minix2 for example + value8 = (!BX_ANY_IS_PRESENT(channel)) ? 0 : BX_SELECTED_CONTROLLER(channel).cylinder_no >> 8; + goto return_value8; + + case 0x06: // hard disk drive and head register 0x1f6 + // b7 Extended data field for ECC + // b6/b5: Used to be sector size. 00=256,01=512,10=1024,11=128 + // Since 512 was always used, bit 6 was taken to mean LBA mode: + // b6 1=LBA mode, 0=CHS mode + // b5 1 + // b4: DRV + // b3..0 HD3..HD0 + value8 = (1 << 7) | + ((BX_SELECTED_CONTROLLER(channel).lba_mode>0) << 6) | + (1 << 5) | // 01b = 512 sector size + (BX_HD_THIS channels[channel].drive_select << 4) | + (BX_SELECTED_CONTROLLER(channel).head_no << 0); + goto return_value8; + break; +//BX_CONTROLLER(channel,0).lba_mode + + case 0x07: // Hard Disk Status 0x1f7 + case 0x16: // Hard Disk Alternate Status 0x3f6 + if (!BX_ANY_IS_PRESENT(channel)) { + // (mch) Just return zero for these registers + value8 = 0; + } else { + value8 = ( + (BX_SELECTED_CONTROLLER(channel).status.busy << 7) | + (BX_SELECTED_CONTROLLER(channel).status.drive_ready << 6) | + (BX_SELECTED_CONTROLLER(channel).status.write_fault << 5) | + (BX_SELECTED_CONTROLLER(channel).status.seek_complete << 4) | + (BX_SELECTED_CONTROLLER(channel).status.drq << 3) | + (BX_SELECTED_CONTROLLER(channel).status.corrected_data << 2) | + (BX_SELECTED_CONTROLLER(channel).status.index_pulse << 1) | + (BX_SELECTED_CONTROLLER(channel).status.err) ); + BX_SELECTED_CONTROLLER(channel).status.index_pulse_count++; + BX_SELECTED_CONTROLLER(channel).status.index_pulse = 0; + if (BX_SELECTED_CONTROLLER(channel).status.index_pulse_count >= INDEX_PULSE_CYCLE) { + BX_SELECTED_CONTROLLER(channel).status.index_pulse = 1; + BX_SELECTED_CONTROLLER(channel).status.index_pulse_count = 0; + } + } + if (port == 0x07) { + DEV_pic_lower_irq(BX_HD_THIS channels[channel].irq); + } + goto return_value8; + break; + + case 0x17: // Hard Disk Address Register 0x3f7 + // Obsolete and unsupported register. Not driven by hard + // disk controller. Report all 1's. If floppy controller + // is handling this address, it will call this function + // set/clear D7 (the only bit it handles), then return + // the combined value + value8 = 0xff; + goto return_value8; + break; + + default: + BX_PANIC(("hard drive: io read to address %x unsupported", + (unsigned) address)); + } + + BX_PANIC(("hard drive: shouldnt get here!")); + return(0); + + return_value32: + BX_DEBUG(("32-bit read from %04x = %08x {%s}", + (unsigned) address, value32, BX_SELECTED_TYPE_STRING(channel))); + return value32; + + return_value16: + BX_DEBUG(("16-bit read from %04x = %04x {%s}", + (unsigned) address, value16, BX_SELECTED_TYPE_STRING(channel))); + return value16; + + return_value8: + BX_DEBUG(("8-bit read from %04x = %02x {%s}", + (unsigned) address, value8, BX_SELECTED_TYPE_STRING(channel))); + return value8; +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_hard_drive_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_HD_SMF + bx_hard_drive_c *class_ptr = (bx_hard_drive_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + void +bx_hard_drive_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_HD_SMF + off_t logical_sector; + off_t ret; + bx_bool prev_control_reset; + + Bit8u channel = BX_MAX_ATA_CHANNEL; + Bit32u port = 0xff; // undefined + + for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) { + if ((address & 0xfff8) == BX_HD_THIS channels[channel].ioaddr1) { + port = address - BX_HD_THIS channels[channel].ioaddr1; + break; + } + else if ((address & 0xfff8) == BX_HD_THIS channels[channel].ioaddr2) { + port = address - BX_HD_THIS channels[channel].ioaddr2 + 0x10; + break; + } + } + + if (channel == BX_MAX_ATA_CHANNEL) { + if (address != 0x03f6) { + BX_PANIC(("write: unable to find ATA channel, ioport=0x%04x", address)); + } else { + channel = 0; + port = address - 0x03e0; + } + } + +#if BX_PDC20230C_VLBIDE_SUPPORT +// pdc20230c is only available for first ata channel +if (channel == 0) { + BX_HD_THIS pdc20230c.prog_count = 0; + + if (BX_HD_THIS pdc20230c.prog_mode != 0) { + switch (port) { + case 0x03: + BX_HD_THIS pdc20230c.p1f3_value = value; + return; + break; + case 0x04: + BX_HD_THIS pdc20230c.p1f4_value = value; + return; + break; + } + } +} +#endif + + if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom)) { + switch (io_len) { + case 1: + BX_INFO(("8-bit write to %04x = %02x {%s}", + (unsigned) address, (unsigned) value, BX_SELECTED_TYPE_STRING(channel))); + break; + + case 2: + BX_INFO(("16-bit write to %04x = %04x {%s}", + (unsigned) address, (unsigned) value, BX_SELECTED_TYPE_STRING(channel))); + break; + + case 4: + BX_INFO(("32-bit write to %04x = %08x {%s}", + (unsigned) address, (unsigned) value, BX_SELECTED_TYPE_STRING(channel))); + break; + + default: + BX_INFO(("unknown-size write to %04x = %08x {%s}", + (unsigned) address, (unsigned) value, BX_SELECTED_TYPE_STRING(channel))); + break; + } + } + + BX_DEBUG(("IO write to %04x = %02x", (unsigned) address, (unsigned) value)); + + switch (port) { + case 0x00: // 0x1f0 + switch (BX_SELECTED_CONTROLLER(channel).current_command) { + case 0x30: // WRITE SECTORS + if (BX_SELECTED_CONTROLLER(channel).buffer_index >= 512) + BX_PANIC(("IO write(0x%04x): buffer_index >= 512", address)); + +#if BX_SupportRepeatSpeedups + if (DEV_bulk_io_quantum_requested()) { + unsigned transferLen, quantumsMax; + + quantumsMax = + (512 - BX_SELECTED_CONTROLLER(channel).buffer_index) / io_len; + if ( quantumsMax == 0) + BX_PANIC(("IO write(0x%04x): not enough space for write", address)); + DEV_bulk_io_quantum_transferred() = + DEV_bulk_io_quantum_requested(); + if (quantumsMax < DEV_bulk_io_quantum_transferred()) + DEV_bulk_io_quantum_transferred() = quantumsMax; + transferLen = io_len * DEV_bulk_io_quantum_transferred(); + memcpy( + &BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index], + (Bit8u*) DEV_bulk_io_host_addr(), + transferLen); + DEV_bulk_io_host_addr() += transferLen; + BX_SELECTED_CONTROLLER(channel).buffer_index += transferLen; + } + else +#endif + { + switch(io_len){ + case 4: + BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index+3] = (Bit8u)(value >> 24); + BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index+2] = (Bit8u)(value >> 16); + case 2: + BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index+1] = (Bit8u)(value >> 8); + BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index] = (Bit8u) value; + } + BX_SELECTED_CONTROLLER(channel).buffer_index += io_len; + } + + /* if buffer completely writtten */ + if (BX_SELECTED_CONTROLLER(channel).buffer_index >= 512) { + off_t logical_sector; + off_t ret; + +#if TEST_WRITE_BEYOND_END==1 + BX_SELECTED_CONTROLLER(channel).cylinder_no += 100000; +#endif + if (!calculate_logical_address(channel, &logical_sector)) { + BX_ERROR(("write reached invalid sector %lu, aborting", (unsigned long)logical_sector)); + command_aborted (channel, BX_SELECTED_CONTROLLER(channel).current_command); + return; + } +#if TEST_WRITE_BEYOND_END==2 + logical_sector += 100000; +#endif + ret = BX_SELECTED_DRIVE(channel).hard_drive->lseek(logical_sector * 512, SEEK_SET); + if (ret < 0) { + BX_ERROR(("could not lseek() hard drive image file at byte %lu", (unsigned long)logical_sector * 512)); + command_aborted (channel, BX_SELECTED_CONTROLLER(channel).current_command); + return; + } + ret = BX_SELECTED_DRIVE(channel).hard_drive->write((bx_ptr_t) BX_SELECTED_CONTROLLER(channel).buffer, 512); + if (ret < 512) { + BX_ERROR(("could not write() hard drive image file at byte %lu", (unsigned long)logical_sector*512)); + command_aborted (channel, BX_SELECTED_CONTROLLER(channel).current_command); + return; + } + + BX_SELECTED_CONTROLLER(channel).buffer_index = 0; + + /* update sector count, sector number, cylinder, + * drive, head, status + * if there are more sectors, read next one in... + */ + + increment_address(channel); + + /* When the write is complete, controller clears the DRQ bit and + * sets the BSY bit. + * If at least one more sector is to be written, controller sets DRQ bit, + * clears BSY bit, and issues IRQ + */ + + if (BX_SELECTED_CONTROLLER(channel).sector_count!=0) { + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.drq = 1; + BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + } + else { /* no more sectors to write */ + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0; + } + raise_interrupt(channel); + } + break; + + case 0xa0: // PACKET + if (BX_SELECTED_CONTROLLER(channel).buffer_index >= PACKET_SIZE) + BX_PANIC(("IO write(0x%04x): buffer_index >= PACKET_SIZE", address)); + BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index] = value; + BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index+1] = (value >> 8); + BX_SELECTED_CONTROLLER(channel).buffer_index += 2; + + /* if packet completely writtten */ + if (BX_SELECTED_CONTROLLER(channel).buffer_index >= PACKET_SIZE) { + // complete command received + Bit8u atapi_command = BX_SELECTED_CONTROLLER(channel).buffer[0]; + + if (bx_dbg.cdrom) + BX_INFO(("cdrom: ATAPI command 0x%x started", atapi_command)); + + switch (atapi_command) { + case 0x00: // test unit ready + if (BX_SELECTED_DRIVE(channel).cdrom.ready) { + atapi_cmd_nop(channel); + } else { + atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); + } + raise_interrupt(channel); + break; + + case 0x03: { // request sense + int alloc_length = BX_SELECTED_CONTROLLER(channel).buffer[4]; + init_send_atapi_command(channel, atapi_command, 18, alloc_length); + + // sense data + BX_SELECTED_CONTROLLER(channel).buffer[0] = 0x70 | (1 << 7); + BX_SELECTED_CONTROLLER(channel).buffer[1] = 0; + BX_SELECTED_CONTROLLER(channel).buffer[2] = BX_SELECTED_DRIVE(channel).sense.sense_key; + BX_SELECTED_CONTROLLER(channel).buffer[3] = BX_SELECTED_DRIVE(channel).sense.information.arr[0]; + BX_SELECTED_CONTROLLER(channel).buffer[4] = BX_SELECTED_DRIVE(channel).sense.information.arr[1]; + BX_SELECTED_CONTROLLER(channel).buffer[5] = BX_SELECTED_DRIVE(channel).sense.information.arr[2]; + BX_SELECTED_CONTROLLER(channel).buffer[6] = BX_SELECTED_DRIVE(channel).sense.information.arr[3]; + BX_SELECTED_CONTROLLER(channel).buffer[7] = 17-7; + BX_SELECTED_CONTROLLER(channel).buffer[8] = BX_SELECTED_DRIVE(channel).sense.specific_inf.arr[0]; + BX_SELECTED_CONTROLLER(channel).buffer[9] = BX_SELECTED_DRIVE(channel).sense.specific_inf.arr[1]; + BX_SELECTED_CONTROLLER(channel).buffer[10] = BX_SELECTED_DRIVE(channel).sense.specific_inf.arr[2]; + BX_SELECTED_CONTROLLER(channel).buffer[11] = BX_SELECTED_DRIVE(channel).sense.specific_inf.arr[3]; + BX_SELECTED_CONTROLLER(channel).buffer[12] = BX_SELECTED_DRIVE(channel).sense.asc; + BX_SELECTED_CONTROLLER(channel).buffer[13] = BX_SELECTED_DRIVE(channel).sense.ascq; + BX_SELECTED_CONTROLLER(channel).buffer[14] = BX_SELECTED_DRIVE(channel).sense.fruc; + BX_SELECTED_CONTROLLER(channel).buffer[15] = BX_SELECTED_DRIVE(channel).sense.key_spec.arr[0]; + BX_SELECTED_CONTROLLER(channel).buffer[16] = BX_SELECTED_DRIVE(channel).sense.key_spec.arr[1]; + BX_SELECTED_CONTROLLER(channel).buffer[17] = BX_SELECTED_DRIVE(channel).sense.key_spec.arr[2]; + + ready_to_send_atapi(channel); + } + break; + + case 0x1b: { // start stop unit + //bx_bool Immed = (BX_SELECTED_CONTROLLER(channel).buffer[1] >> 0) & 1; + bx_bool LoEj = (BX_SELECTED_CONTROLLER(channel).buffer[4] >> 1) & 1; + bx_bool Start = (BX_SELECTED_CONTROLLER(channel).buffer[4] >> 0) & 1; + + if (!LoEj && !Start) { // stop the disc + BX_ERROR(("FIXME: Stop disc not implemented")); + atapi_cmd_nop(channel); + raise_interrupt(channel); + } else if (!LoEj && Start) { // start (spin up) the disc +#ifdef LOWLEVEL_CDROM + BX_SELECTED_DRIVE(channel).cdrom.cd->start_cdrom(); +#endif + BX_ERROR(("FIXME: ATAPI start disc not reading TOC")); + atapi_cmd_nop(channel); + raise_interrupt(channel); + } else if (LoEj && !Start) { // Eject the disc + atapi_cmd_nop(channel); + + if (BX_SELECTED_DRIVE(channel).cdrom.ready) { +#ifdef LOWLEVEL_CDROM + BX_SELECTED_DRIVE(channel).cdrom.cd->eject_cdrom(); +#endif + BX_SELECTED_DRIVE(channel).cdrom.ready = 0; + bx_options.atadevice[channel][BX_SLAVE_SELECTED(channel)].Ostatus->set(BX_EJECTED); + bx_gui->update_drive_status_buttons(); + } + raise_interrupt(channel); + } else { // Load the disc + // My guess is that this command only closes the tray, that's a no-op for us + atapi_cmd_nop(channel); + raise_interrupt(channel); + } + } + break; + + case 0xbd: { // mechanism status + uint16 alloc_length = read_16bit(BX_SELECTED_CONTROLLER(channel).buffer + 8); + + if (alloc_length == 0) + BX_PANIC(("Zero allocation length to MECHANISM STATUS not impl.")); + + init_send_atapi_command(channel, atapi_command, 8, alloc_length); + + BX_SELECTED_CONTROLLER(channel).buffer[0] = 0; // reserved for non changers + BX_SELECTED_CONTROLLER(channel).buffer[1] = 0; // reserved for non changers + + BX_SELECTED_CONTROLLER(channel).buffer[2] = 0; // Current LBA (TODO!) + BX_SELECTED_CONTROLLER(channel).buffer[3] = 0; // Current LBA (TODO!) + BX_SELECTED_CONTROLLER(channel).buffer[4] = 0; // Current LBA (TODO!) + + BX_SELECTED_CONTROLLER(channel).buffer[5] = 1; // one slot + + BX_SELECTED_CONTROLLER(channel).buffer[6] = 0; // slot table length + BX_SELECTED_CONTROLLER(channel).buffer[7] = 0; // slot table length + + ready_to_send_atapi(channel); + } + break; + + case 0x5a: { // mode sense + uint16 alloc_length = read_16bit(BX_SELECTED_CONTROLLER(channel).buffer + 7); + + Bit8u PC = BX_SELECTED_CONTROLLER(channel).buffer[2] >> 6; + Bit8u PageCode = BX_SELECTED_CONTROLLER(channel).buffer[2] & 0x3f; + + switch (PC) { + case 0x0: // current values + switch (PageCode) { + case 0x01: // error recovery + init_send_atapi_command(channel, atapi_command, sizeof(error_recovery_t) + 8, alloc_length); + + init_mode_sense_single(channel, &BX_SELECTED_DRIVE(channel).cdrom.current.error_recovery, + sizeof(error_recovery_t)); + ready_to_send_atapi(channel); + break; + + case 0x2a: // CD-ROM capabilities & mech. status + init_send_atapi_command(channel, atapi_command, 28, alloc_length); + init_mode_sense_single(channel, &BX_SELECTED_CONTROLLER(channel).buffer[8], 28); + BX_SELECTED_CONTROLLER(channel).buffer[8] = 0x2a; + BX_SELECTED_CONTROLLER(channel).buffer[9] = 0x12; + BX_SELECTED_CONTROLLER(channel).buffer[10] = 0x00; + BX_SELECTED_CONTROLLER(channel).buffer[11] = 0x00; + // Multisession, Mode 2 Form 2, Mode 2 Form 1 + BX_SELECTED_CONTROLLER(channel).buffer[12] = 0x70; + BX_SELECTED_CONTROLLER(channel).buffer[13] = (3 << 5); + BX_SELECTED_CONTROLLER(channel).buffer[14] = (unsigned char) +(1 | + (BX_SELECTED_DRIVE(channel).cdrom.locked ? (1 << 1) : 0) | + (1 << 3) | + (1 << 5)); + BX_SELECTED_CONTROLLER(channel).buffer[15] = 0x00; + BX_SELECTED_CONTROLLER(channel).buffer[16] = (706 >> 8) & 0xff; + BX_SELECTED_CONTROLLER(channel).buffer[17] = 706 & 0xff; + BX_SELECTED_CONTROLLER(channel).buffer[18] = 0; + BX_SELECTED_CONTROLLER(channel).buffer[19] = 2; + BX_SELECTED_CONTROLLER(channel).buffer[20] = (512 >> 8) & 0xff; + BX_SELECTED_CONTROLLER(channel).buffer[21] = 512 & 0xff; + BX_SELECTED_CONTROLLER(channel).buffer[22] = (706 >> 8) & 0xff; + BX_SELECTED_CONTROLLER(channel).buffer[23] = 706 & 0xff; + BX_SELECTED_CONTROLLER(channel).buffer[24] = 0; + BX_SELECTED_CONTROLLER(channel).buffer[25] = 0; + BX_SELECTED_CONTROLLER(channel).buffer[26] = 0; + BX_SELECTED_CONTROLLER(channel).buffer[27] = 0; + ready_to_send_atapi(channel); + break; + + case 0x0d: // CD-ROM + case 0x0e: // CD-ROM audio control + case 0x3f: // all + BX_ERROR(("cdrom: MODE SENSE (curr), code=%x" + " not implemented yet", + PageCode)); + atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + raise_interrupt(channel); + break; + + default: + // not implemeted by this device + BX_INFO(("cdrom: MODE SENSE PC=%x, PageCode=%x," + " not implemented by device", + PC, PageCode)); + atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + raise_interrupt(channel); + break; + } + break; + + case 0x1: // changeable values + switch (PageCode) { + case 0x01: // error recovery + case 0x0d: // CD-ROM + case 0x0e: // CD-ROM audio control + case 0x2a: // CD-ROM capabilities & mech. status + case 0x3f: // all + BX_ERROR(("cdrom: MODE SENSE (chg), code=%x" + " not implemented yet", + PageCode)); + atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + raise_interrupt(channel); + break; + + default: + // not implemeted by this device + BX_INFO(("cdrom: MODE SENSE PC=%x, PageCode=%x," + " not implemented by device", + PC, PageCode)); + atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + raise_interrupt(channel); + break; + } + break; + + case 0x2: // default values + switch (PageCode) { + case 0x01: // error recovery + case 0x0d: // CD-ROM + case 0x0e: // CD-ROM audio control + case 0x2a: // CD-ROM capabilities & mech. status + case 0x3f: // all + BX_PANIC(("cdrom: MODE SENSE (dflt), code=%x", + PageCode)); + break; + + default: + // not implemeted by this device + BX_INFO(("cdrom: MODE SENSE PC=%x, PageCode=%x," + " not implemented by device", + PC, PageCode)); + atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + raise_interrupt(channel); + break; + } + break; + + case 0x3: // saved values not implemented + atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, ASC_SAVING_PARAMETERS_NOT_SUPPORTED); + raise_interrupt(channel); + break; + + default: + BX_PANIC(("Should not get here!")); + break; + } + } + break; + + case 0x12: { // inquiry + uint8 alloc_length = BX_SELECTED_CONTROLLER(channel).buffer[4]; + + init_send_atapi_command(channel, atapi_command, 36, alloc_length); + + BX_SELECTED_CONTROLLER(channel).buffer[0] = 0x05; // CD-ROM + BX_SELECTED_CONTROLLER(channel).buffer[1] = 0x80; // Removable + BX_SELECTED_CONTROLLER(channel).buffer[2] = 0x00; // ISO, ECMA, ANSI version + BX_SELECTED_CONTROLLER(channel).buffer[3] = 0x21; // ATAPI-2, as specified + BX_SELECTED_CONTROLLER(channel).buffer[4] = 31; // additional length (total 36) + BX_SELECTED_CONTROLLER(channel).buffer[5] = 0x00; // reserved + BX_SELECTED_CONTROLLER(channel).buffer[6] = 0x00; // reserved + BX_SELECTED_CONTROLLER(channel).buffer[7] = 0x00; // reserved + + // Vendor ID + const char* vendor_id = "VTAB "; + int i; + for (i = 0; i < 8; i++) + BX_SELECTED_CONTROLLER(channel).buffer[8+i] = vendor_id[i]; + + // Product ID + const char* product_id = "Turbo CD-ROM "; + for (i = 0; i < 16; i++) + BX_SELECTED_CONTROLLER(channel).buffer[16+i] = product_id[i]; + + // Product Revision level + const char* rev_level = "1.0 "; + for (i = 0; i < 4; i++) + BX_SELECTED_CONTROLLER(channel).buffer[32+i] = rev_level[i]; + + ready_to_send_atapi(channel); + } + break; + + case 0x25: { // read cd-rom capacity + // no allocation length??? + init_send_atapi_command(channel, atapi_command, 8, 8); + + if (BX_SELECTED_DRIVE(channel).cdrom.ready) { + uint32 capacity = BX_SELECTED_DRIVE(channel).cdrom.capacity; + BX_INFO(("Capacity is %d sectors (%d bytes)", capacity, capacity * 2048)); + BX_SELECTED_CONTROLLER(channel).buffer[0] = (capacity >> 24) & 0xff; + BX_SELECTED_CONTROLLER(channel).buffer[1] = (capacity >> 16) & 0xff; + BX_SELECTED_CONTROLLER(channel).buffer[2] = (capacity >> 8) & 0xff; + BX_SELECTED_CONTROLLER(channel).buffer[3] = (capacity >> 0) & 0xff; + BX_SELECTED_CONTROLLER(channel).buffer[4] = (2048 >> 24) & 0xff; + BX_SELECTED_CONTROLLER(channel).buffer[5] = (2048 >> 16) & 0xff; + BX_SELECTED_CONTROLLER(channel).buffer[6] = (2048 >> 8) & 0xff; + BX_SELECTED_CONTROLLER(channel).buffer[7] = (2048 >> 0) & 0xff; + ready_to_send_atapi(channel); + } else { + atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); + raise_interrupt(channel); + } + } + break; + + case 0xbe: { // read cd + if (BX_SELECTED_DRIVE(channel).cdrom.ready) { + BX_ERROR(("Read CD with CD present not implemented")); + atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); + raise_interrupt(channel); + } else { + atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); + raise_interrupt(channel); + } + } + break; + + case 0x43: { // read toc + if (BX_SELECTED_DRIVE(channel).cdrom.ready) { +#ifdef LOWLEVEL_CDROM + bool msf = (BX_SELECTED_CONTROLLER(channel).buffer[1] >> 1) & 1; + uint8 starting_track = BX_SELECTED_CONTROLLER(channel).buffer[6]; +#endif + uint16 alloc_length = read_16bit(BX_SELECTED_CONTROLLER(channel).buffer + 7); + + uint8 format = (BX_SELECTED_CONTROLLER(channel).buffer[9] >> 6); + int i; + switch (format) { + case 0: +#ifdef LOWLEVEL_CDROM + int toc_length; + if (!(BX_SELECTED_DRIVE(channel).cdrom.cd->read_toc(BX_SELECTED_CONTROLLER(channel).buffer, + &toc_length, msf, starting_track))) { + atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + raise_interrupt(channel); + } else { + init_send_atapi_command(channel, atapi_command, toc_length, alloc_length); + ready_to_send_atapi(channel); + } +#else + BX_PANIC(("LOWLEVEL_CDROM not defined")); +#endif + break; + + case 1: + // multi session stuff. we ignore this and emulate a single session only + init_send_atapi_command(channel, atapi_command, 12, alloc_length); + + BX_SELECTED_CONTROLLER(channel).buffer[0] = 0; + BX_SELECTED_CONTROLLER(channel).buffer[1] = 0x0a; + BX_SELECTED_CONTROLLER(channel).buffer[2] = 1; + BX_SELECTED_CONTROLLER(channel).buffer[3] = 1; + for (i = 0; i < 8; i++) + BX_SELECTED_CONTROLLER(channel).buffer[4+i] = 0; + + ready_to_send_atapi(channel); + break; + + case 2: + default: + BX_PANIC(("(READ TOC) Format %d not supported", format)); + break; + } + } else { + atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); + raise_interrupt(channel); + } + } + break; + + case 0x28: // read (10) + case 0xa8: // read (12) + { + + uint32 transfer_length; + if (atapi_command == 0x28) + transfer_length = read_16bit(BX_SELECTED_CONTROLLER(channel).buffer + 7); + else + transfer_length = read_32bit(BX_SELECTED_CONTROLLER(channel).buffer + 6); + + uint32 lba = read_32bit(BX_SELECTED_CONTROLLER(channel).buffer + 2); + + if (!BX_SELECTED_DRIVE(channel).cdrom.ready) { + atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); + raise_interrupt(channel); + break; + } + + if (transfer_length == 0) { + atapi_cmd_nop(channel); + raise_interrupt(channel); + BX_INFO(("READ(%d) with transfer length 0, ok", atapi_command==0x28?10:12)); + break; + } + + if (lba + transfer_length > BX_SELECTED_DRIVE(channel).cdrom.capacity) { + atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR); + raise_interrupt(channel); + break; + } + + BX_DEBUG(("cdrom: READ (%d) LBA=%d LEN=%d", atapi_command==0x28?10:12, lba, transfer_length)); + + // handle command + init_send_atapi_command(channel, atapi_command, transfer_length * 2048, + transfer_length * 2048, true); + BX_SELECTED_DRIVE(channel).cdrom.remaining_blocks = transfer_length; + BX_SELECTED_DRIVE(channel).cdrom.next_lba = lba; + ready_to_send_atapi(channel); + } + break; + + case 0x2b: { // seek + uint32 lba = read_32bit(BX_SELECTED_CONTROLLER(channel).buffer + 2); + if (!BX_SELECTED_DRIVE(channel).cdrom.ready) { + atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); + raise_interrupt(channel); + break; + } + + if (lba > BX_SELECTED_DRIVE(channel).cdrom.capacity) { + atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR); + raise_interrupt(channel); + break; + } + BX_INFO(("cdrom: SEEK (ignored)")); + atapi_cmd_nop(channel); + raise_interrupt(channel); + } + break; + + case 0x1e: { // prevent/allow medium removal + if (BX_SELECTED_DRIVE(channel).cdrom.ready) { + BX_SELECTED_DRIVE(channel).cdrom.locked = BX_SELECTED_CONTROLLER(channel).buffer[4] & 1; + atapi_cmd_nop(channel); + } else { + atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); + } + raise_interrupt(channel); + } + break; + + case 0x42: { // read sub-channel + bool msf = get_packet_field(channel,1, 1, 1); + bool sub_q = get_packet_field(channel,2, 6, 1); + uint8 data_format = get_packet_byte(channel,3); + uint8 track_number = get_packet_byte(channel,6); + uint16 alloc_length = get_packet_word(channel,7); + UNUSED(msf); + UNUSED(data_format); + UNUSED(track_number); + + if (!BX_SELECTED_DRIVE(channel).cdrom.ready) { + atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); + raise_interrupt(channel); + } else { + BX_SELECTED_CONTROLLER(channel).buffer[0] = 0; + BX_SELECTED_CONTROLLER(channel).buffer[1] = 0; // audio not supported + BX_SELECTED_CONTROLLER(channel).buffer[2] = 0; + BX_SELECTED_CONTROLLER(channel).buffer[3] = 0; + + int ret_len = 4; // header size + + if (sub_q) { // !sub_q == header only + BX_ERROR(("Read sub-channel with SubQ not implemented")); + atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + raise_interrupt(channel); + } + + init_send_atapi_command(channel, atapi_command, ret_len, alloc_length); + ready_to_send_atapi(channel); + } + } + break; + + case 0x51: { // read disc info + // no-op to keep the Linux CD-ROM driver happy + atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); + raise_interrupt(channel); + } + break; + + case 0x55: // mode select + case 0xa6: // load/unload cd + case 0x4b: // pause/resume + case 0x45: // play audio + case 0x47: // play audio msf + case 0xbc: // play cd + case 0xb9: // read cd msf + case 0x44: // read header + case 0xba: // scan + case 0xbb: // set cd speed + case 0x4e: // stop play/scan + case 0x46: // ??? + case 0x4a: // ??? + BX_ERROR(("ATAPI command 0x%x not implemented yet", + atapi_command)); + atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); + raise_interrupt(channel); + break; + default: + BX_PANIC(("Unknown ATAPI command 0x%x (%d)", + atapi_command, atapi_command)); + // We'd better signal the error if the user chose to continue + atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); + raise_interrupt(channel); + break; + } + } + + break; + + default: + BX_PANIC(("IO write(0x%04x): current command is %02xh", address, + (unsigned) BX_SELECTED_CONTROLLER(channel).current_command)); + } + break; + + case 0x01: // hard disk write precompensation 0x1f1 + WRITE_FEATURES(channel,value); + if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom)) { + if (value == 0xff) + BX_INFO(("no precompensation {%s}", BX_SELECTED_TYPE_STRING(channel))); + else + BX_INFO(("precompensation value %02x {%s}", (unsigned) value, BX_SELECTED_TYPE_STRING(channel))); + } + break; + + case 0x02: // hard disk sector count 0x1f2 + WRITE_SECTOR_COUNT(channel,value); + if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom)) + BX_INFO(("sector count = %u {%s}", (unsigned) value, BX_SELECTED_TYPE_STRING(channel))); + break; + + case 0x03: // hard disk sector number 0x1f3 + WRITE_SECTOR_NUMBER(channel,value); + if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom)) + BX_INFO(("sector number = %u {%s}", (unsigned) value, BX_SELECTED_TYPE_STRING(channel))); + break; + + case 0x04: // hard disk cylinder low 0x1f4 + WRITE_CYLINDER_LOW(channel,value); + if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom)) + BX_INFO(("cylinder low = %02xh {%s}", (unsigned) value, BX_SELECTED_TYPE_STRING(channel))); + break; + + case 0x05: // hard disk cylinder high 0x1f5 + WRITE_CYLINDER_HIGH(channel,value); + if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom)) + BX_INFO(("cylinder high = %02xh {%s}", (unsigned) value, BX_SELECTED_TYPE_STRING(channel))); + break; + + case 0x06: // hard disk drive and head register 0x1f6 + // b7 Extended data field for ECC + // b6/b5: Used to be sector size. 00=256,01=512,10=1024,11=128 + // Since 512 was always used, bit 6 was taken to mean LBA mode: + // b6 1=LBA mode, 0=CHS mode + // b5 1 + // b4: DRV + // b3..0 HD3..HD0 + { + if ( (value & 0xa0) != 0xa0 ) // 1x1xxxxx + BX_INFO(("IO write 0x%04x (%02x): not 1x1xxxxxb", address, (unsigned) value)); + Bit32u drvsel = BX_HD_THIS channels[channel].drive_select = (value >> 4) & 0x01; + WRITE_HEAD_NO(channel,value & 0xf); + if (BX_SELECTED_CONTROLLER(channel).lba_mode == 0 && ((value >> 6) & 1) == 1) + BX_DEBUG(("enabling LBA mode")); + WRITE_LBA_MODE(channel,(value >> 6) & 1); + if (!BX_SELECTED_IS_PRESENT(channel)) { + BX_ERROR (("device set to %d which does not exist",drvsel)); + BX_SELECTED_CONTROLLER(channel).error_register = 0x04; // aborted + BX_SELECTED_CONTROLLER(channel).status.err = 1; + } + break; + } + + case 0x07: // hard disk command 0x1f7 + // (mch) Writes to the command register with drive_select != 0 + // are ignored if no secondary device is present + if ((BX_SLAVE_SELECTED(channel)) && (!BX_SLAVE_IS_PRESENT(channel))) + break; + // Writes to the command register clear the IRQ + DEV_pic_lower_irq(BX_HD_THIS channels[channel].irq); + + if (BX_SELECTED_CONTROLLER(channel).status.busy) + BX_PANIC(("hard disk: command sent, controller BUSY")); + if ( (value & 0xf0) == 0x10 ) + value = 0x10; + switch (value) { + + case 0x10: // CALIBRATE DRIVE + if (!BX_SELECTED_IS_HD(channel)) + BX_PANIC(("calibrate drive issued to non-disk")); + if (!BX_SELECTED_IS_PRESENT(channel)) { + BX_SELECTED_CONTROLLER(channel).error_register = 0x02; // Track 0 not found + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.seek_complete = 0; + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 1; + raise_interrupt(channel); + BX_INFO(("calibrate drive: disk ata%d-%d not present", channel, BX_SLAVE_SELECTED(channel))); + break; + } + + /* move head to cylinder 0, issue IRQ */ + BX_SELECTED_CONTROLLER(channel).error_register = 0; + BX_SELECTED_CONTROLLER(channel).cylinder_no = 0; + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1; + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + raise_interrupt(channel); + break; + + case 0x20: // READ MULTIPLE SECTORS, with retries + case 0x21: // READ MULTIPLE SECTORS, without retries + /* update sector_no, always points to current sector + * after each sector is read to buffer, DRQ bit set and issue IRQ + * if interrupt handler transfers all data words into main memory, + * and more sectors to read, then set BSY bit again, clear DRQ and + * read next sector into buffer + * sector count of 0 means 256 sectors + */ + + if (!BX_SELECTED_IS_HD(channel)) { + BX_ERROR(("read multiple issued to non-disk")); + command_aborted(channel, value); + break; + } + + BX_SELECTED_CONTROLLER(channel).current_command = value; + + // Lose98 accesses 0/0/0 in CHS mode + if (!BX_SELECTED_CONTROLLER(channel).lba_mode && + !BX_SELECTED_CONTROLLER(channel).head_no && + !BX_SELECTED_CONTROLLER(channel).cylinder_no && + !BX_SELECTED_CONTROLLER(channel).sector_no) { + BX_INFO(("Read from 0/0/0, aborting command")); + command_aborted(channel, value); + break; + } + +#if TEST_READ_BEYOND_END==2 + BX_SELECTED_CONTROLLER(channel).cylinder_no += 100000; +#endif + if (!calculate_logical_address(channel, &logical_sector)) { + BX_ERROR(("initial read from sector %lu out of bounds, aborting", (unsigned long)logical_sector)); + command_aborted(channel, value); + break; + } +#if TEST_READ_BEYOND_END==3 + logical_sector += 100000; +#endif + ret=BX_SELECTED_DRIVE(channel).hard_drive->lseek(logical_sector * 512, SEEK_SET); + if (ret < 0) { + BX_ERROR (("could not lseek() hard drive image file, aborting")); + command_aborted(channel, value); + break; + } + ret = BX_SELECTED_DRIVE(channel).hard_drive->read((bx_ptr_t) BX_SELECTED_CONTROLLER(channel).buffer, 512); + if (ret < 512) { + BX_ERROR(("logical sector was %lu", (unsigned long)logical_sector)); + BX_ERROR(("could not read() hard drive image file at byte %lu", (unsigned long)logical_sector*512)); + command_aborted(channel, value); + break; + } + + BX_SELECTED_CONTROLLER(channel).error_register = 0; + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1; + BX_SELECTED_CONTROLLER(channel).status.drq = 1; + BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + BX_SELECTED_CONTROLLER(channel).buffer_index = 0; + raise_interrupt(channel); + break; + + case 0x30: /* WRITE SECTORS, with retries */ + /* update sector_no, always points to current sector + * after each sector is read to buffer, DRQ bit set and issue IRQ + * if interrupt handler transfers all data words into main memory, + * and more sectors to read, then set BSY bit again, clear DRQ and + * read next sector into buffer + * sector count of 0 means 256 sectors + */ + + if (!BX_SELECTED_IS_HD(channel)) + BX_PANIC(("write multiple issued to non-disk")); + + if (BX_SELECTED_CONTROLLER(channel).status.busy) { + BX_PANIC(("write command: BSY bit set")); + } + BX_SELECTED_CONTROLLER(channel).current_command = value; + + // implicit seek done :^) + BX_SELECTED_CONTROLLER(channel).error_register = 0; + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + // BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1; + BX_SELECTED_CONTROLLER(channel).status.drq = 1; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + BX_SELECTED_CONTROLLER(channel).buffer_index = 0; + break; + + case 0x90: // EXECUTE DEVICE DIAGNOSTIC + if (BX_SELECTED_CONTROLLER(channel).status.busy) { + BX_PANIC(("diagnostic command: BSY bit set")); + } + if (!BX_SELECTED_IS_HD(channel)) + BX_PANIC(("drive diagnostics issued to non-disk")); + BX_SELECTED_CONTROLLER(channel).error_register = 0x81; // Drive 1 failed, no error on drive 0 + // BX_SELECTED_CONTROLLER(channel).status.busy = 0; // not needed + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + break; + + case 0x91: // INITIALIZE DRIVE PARAMETERS + if (BX_SELECTED_CONTROLLER(channel).status.busy) { + BX_PANIC(("init drive parameters command: BSY bit set")); + } + if (!BX_SELECTED_IS_HD(channel)) + BX_PANIC(("initialize drive parameters issued to non-disk")); + // sets logical geometry of specified drive + BX_DEBUG(("init drive params: sec=%u, drive sel=%u, head=%u", + (unsigned) BX_SELECTED_CONTROLLER(channel).sector_count, + (unsigned) BX_HD_THIS channels[channel].drive_select, + (unsigned) BX_SELECTED_CONTROLLER(channel).head_no)); + if (!BX_SELECTED_IS_PRESENT(channel)) { + BX_PANIC(("init drive params: disk ata%d-%d not present", channel, BX_SLAVE_SELECTED(channel))); + //BX_SELECTED_CONTROLLER(channel).error_register = 0x12; + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + raise_interrupt(channel); + break; + } + if (BX_SELECTED_CONTROLLER(channel).sector_count != BX_SELECTED_DRIVE(channel).hard_drive->sectors) + BX_PANIC(("init drive params: sector count doesnt match %d!=%d", BX_SELECTED_CONTROLLER(channel).sector_count, BX_SELECTED_DRIVE(channel).hard_drive->sectors)); + if ( BX_SELECTED_CONTROLLER(channel).head_no != (BX_SELECTED_DRIVE(channel).hard_drive->heads-1) ) + BX_PANIC(("init drive params: head number doesn't match %d != %d",BX_SELECTED_CONTROLLER(channel).head_no, BX_SELECTED_DRIVE(channel).hard_drive->heads-1)); + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + raise_interrupt(channel); + break; + + case 0xec: // IDENTIFY DEVICE + if (bx_options.OnewHardDriveSupport->get ()) { + if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom)) + BX_INFO(("Drive ID Command issued : 0xec ")); + + if (!BX_SELECTED_IS_PRESENT(channel)) { + BX_INFO(("disk ata%d-%d not present, aborting",channel,BX_SLAVE_SELECTED(channel))); + command_aborted(channel, value); + break; + } + if (BX_SELECTED_IS_CD(channel)) { + BX_SELECTED_CONTROLLER(channel).head_no = 0; + BX_SELECTED_CONTROLLER(channel).sector_count = 1; + BX_SELECTED_CONTROLLER(channel).sector_no = 1; + BX_SELECTED_CONTROLLER(channel).cylinder_no = 0xeb14; + command_aborted(channel, 0xec); + } else { + BX_SELECTED_CONTROLLER(channel).current_command = value; + BX_SELECTED_CONTROLLER(channel).error_register = 0; + + // See ATA/ATAPI-4, 8.12 + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.write_fault = 0; + BX_SELECTED_CONTROLLER(channel).status.drq = 1; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + + BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1; + BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0; + + BX_SELECTED_CONTROLLER(channel).buffer_index = 0; + raise_interrupt(channel); + identify_drive(channel); + } + } + else { + BX_INFO(("sent IDENTIFY DEVICE (0xec) to old hard drive")); + command_aborted(channel, value); + } + break; + + case 0xef: // SET FEATURES + switch(BX_SELECTED_CONTROLLER(channel).features) { + case 0x02: // Enable and + case 0x82: // Disable write cache. + case 0xAA: // Enable and + case 0x55: // Disable look-ahead cache. + case 0xCC: // Enable and + case 0x66: // Disable reverting to power-on default + BX_INFO(("SET FEATURES subcommand 0x%02x not supported by disk.", (unsigned) BX_SELECTED_CONTROLLER(channel).features)); + command_aborted(channel, value); + break; + + default: + BX_PANIC(("SET FEATURES with unknown subcommand: 0x%02x", (unsigned) BX_SELECTED_CONTROLLER(channel).features )); + // We'd better signal the error if the user chose to continue + command_aborted(channel, value); + } + break; + + case 0x40: // READ VERIFY SECTORS + if (bx_options.OnewHardDriveSupport->get ()) { + if (!BX_SELECTED_IS_HD(channel)) + BX_PANIC(("read verify issued to non-disk")); + BX_INFO(("Verify Command : 0x40 ! ")); + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + raise_interrupt(channel); + } + else { + BX_INFO(("sent READ VERIFY SECTORS (0x40) to old hard drive")); + command_aborted(channel, value); + } + break; + + case 0xc6: // SET MULTIPLE MODE (mch) + if (BX_SELECTED_CONTROLLER(channel).sector_count != 128 && + BX_SELECTED_CONTROLLER(channel).sector_count != 64 && + BX_SELECTED_CONTROLLER(channel).sector_count != 32 && + BX_SELECTED_CONTROLLER(channel).sector_count != 16 && + BX_SELECTED_CONTROLLER(channel).sector_count != 8 && + BX_SELECTED_CONTROLLER(channel).sector_count != 4 && + BX_SELECTED_CONTROLLER(channel).sector_count != 2) + command_aborted(channel, value); + + if (!BX_SELECTED_IS_HD(channel)) + BX_PANIC(("set multiple mode issued to non-disk")); + + BX_SELECTED_CONTROLLER(channel).sectors_per_block = BX_SELECTED_CONTROLLER(channel).sector_count; + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.write_fault = 0; + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + break; + + // ATAPI commands + case 0xa1: // IDENTIFY PACKET DEVICE + if (BX_SELECTED_IS_CD(channel)) { + BX_SELECTED_CONTROLLER(channel).current_command = value; + BX_SELECTED_CONTROLLER(channel).error_register = 0; + + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.write_fault = 0; + BX_SELECTED_CONTROLLER(channel).status.drq = 1; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + + BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1; + BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0; + + BX_SELECTED_CONTROLLER(channel).buffer_index = 0; + raise_interrupt(channel); + identify_ATAPI_drive(channel); + } else { + command_aborted(channel, 0xa1); + } + break; + + case 0x08: // DEVICE RESET (atapi) + if (BX_SELECTED_IS_CD(channel)) { + BX_SELECTED_CONTROLLER(channel).status.busy = 1; + BX_SELECTED_CONTROLLER(channel).error_register &= ~(1 << 7); + + // device signature + BX_SELECTED_CONTROLLER(channel).head_no = 0; + BX_SELECTED_CONTROLLER(channel).sector_count = 1; + BX_SELECTED_CONTROLLER(channel).sector_no = 1; + BX_SELECTED_CONTROLLER(channel).cylinder_no = 0xeb14; + + BX_SELECTED_CONTROLLER(channel).status.write_fault = 0; + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + + } else { + BX_DEBUG(("ATAPI Device Reset on non-cd device")); + command_aborted(channel, 0x08); + } + break; + + case 0xa0: // SEND PACKET (atapi) + if (BX_SELECTED_IS_CD(channel)) { + // PACKET + if (BX_SELECTED_CONTROLLER(channel).features & (1 << 0)) + BX_PANIC(("PACKET-DMA not supported")); + if (BX_SELECTED_CONTROLLER(channel).features & (1 << 1)) + BX_PANIC(("PACKET-overlapped not supported")); + + // We're already ready! + BX_SELECTED_CONTROLLER(channel).sector_count = 1; + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.write_fault = 0; + // serv bit?? + BX_SELECTED_CONTROLLER(channel).status.drq = 1; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + + // NOTE: no interrupt here + BX_SELECTED_CONTROLLER(channel).current_command = value; + BX_SELECTED_CONTROLLER(channel).buffer_index = 0; + } else { + command_aborted (channel, 0xa0); + } + break; + + case 0xa2: // SERVICE (atapi), optional + if (BX_SELECTED_IS_CD(channel)) { + BX_PANIC(("ATAPI SERVICE not implemented")); + } else { + command_aborted (channel, 0xa2); + } + break; + + // power management + case 0xe5: // CHECK POWER MODE + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.write_fault = 0; + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + BX_SELECTED_CONTROLLER(channel).sector_count = 0xff; // Active or Idle mode + raise_interrupt(channel); + break; + + case 0x70: // SEEK (cgs) + if (BX_SELECTED_IS_HD(channel)) { + BX_DEBUG(("write cmd 0x70 (SEEK) executing")); + if (!calculate_logical_address(channel, &logical_sector)) { + BX_ERROR(("initial seek to sector %lu out of bounds, aborting", (unsigned long)logical_sector)); + command_aborted(channel, value); + break; + } + BX_SELECTED_CONTROLLER(channel).error_register = 0; + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1; + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + BX_SELECTED_CONTROLLER(channel).buffer_index = 0; + BX_DEBUG(("s[0].controller.control.disable_irq = %02x", (BX_HD_THIS channels[channel].drives[0]).controller.control.disable_irq)); + BX_DEBUG(("s[1].controller.control.disable_irq = %02x", (BX_HD_THIS channels[channel].drives[1]).controller.control.disable_irq)); + BX_DEBUG(("SEEK completed. error_register = %02x", BX_SELECTED_CONTROLLER(channel).error_register)); + raise_interrupt(channel); + BX_DEBUG(("SEEK interrupt completed")); + } else { + BX_ERROR(("write cmd 0x70 (SEEK) not supported for non-disk")); + command_aborted(channel, 0x70); + } + break; + + + + // List all the write operations that are defined in the ATA/ATAPI spec + // that we don't support. Commands that are listed here will cause a + // BX_ERROR, which is non-fatal, and the command will be aborted. + case 0x22: BX_ERROR(("write cmd 0x22 (READ LONG) not supported")); command_aborted(channel, 0x22); break; + case 0x23: BX_ERROR(("write cmd 0x23 (READ LONG NO RETRY) not supported")); command_aborted(channel, 0x23); break; + case 0x24: BX_ERROR(("write cmd 0x24 (READ SECTORS EXT) not supported"));command_aborted(channel, 0x24); break; + case 0x25: BX_ERROR(("write cmd 0x25 (READ DMA EXT) not supported"));command_aborted(channel, 0x25); break; + case 0x26: BX_ERROR(("write cmd 0x26 (READ DMA QUEUED EXT) not supported"));command_aborted(channel, 0x26); break; + case 0x27: BX_ERROR(("write cmd 0x27 (READ NATIVE MAX ADDRESS EXT) not supported"));command_aborted(channel, 0x27); break; + case 0x29: BX_ERROR(("write cmd 0x29 (READ MULTIPLE EXT) not supported"));command_aborted(channel, 0x29); break; + case 0x2A: BX_ERROR(("write cmd 0x2A (READ STREAM DMA) not supported"));command_aborted(channel, 0x2A); break; + case 0x2B: BX_ERROR(("write cmd 0x2B (READ STREAM PIO) not supported"));command_aborted(channel, 0x2B); break; + case 0x2F: BX_ERROR(("write cmd 0x2F (READ LOG EXT) not supported"));command_aborted(channel, 0x2F); break; + case 0x31: BX_ERROR(("write cmd 0x31 (WRITE SECTORS NO RETRY) not supported")); command_aborted(channel, 0x31); break; + case 0x32: BX_ERROR(("write cmd 0x32 (WRITE LONG) not supported")); command_aborted(channel, 0x32); break; + case 0x33: BX_ERROR(("write cmd 0x33 (WRITE LONG NO RETRY) not supported")); command_aborted(channel, 0x33); break; + case 0x34: BX_ERROR(("write cmd 0x34 (WRITE SECTORS EXT) not supported"));command_aborted(channel, 0x34); break; + case 0x35: BX_ERROR(("write cmd 0x35 (WRITE DMA EXT) not supported"));command_aborted(channel, 0x35); break; + case 0x36: BX_ERROR(("write cmd 0x36 (WRITE DMA QUEUED EXT) not supported"));command_aborted(channel, 0x36); break; + case 0x37: BX_ERROR(("write cmd 0x37 (SET MAX ADDRESS EXT) not supported"));command_aborted(channel, 0x37); break; + case 0x38: BX_ERROR(("write cmd 0x38 (CFA WRITE SECTORS W/OUT ERASE) not supported"));command_aborted(channel, 0x38); break; + case 0x39: BX_ERROR(("write cmd 0x39 (WRITE MULTIPLE EXT) not supported"));command_aborted(channel, 0x39); break; + case 0x3A: BX_ERROR(("write cmd 0x3A (WRITE STREAM DMA) not supported"));command_aborted(channel, 0x3A); break; + case 0x3B: BX_ERROR(("write cmd 0x3B (WRITE STREAM PIO) not supported"));command_aborted(channel, 0x3B); break; + case 0x3F: BX_ERROR(("write cmd 0x3F (WRITE LOG EXT) not supported"));command_aborted(channel, 0x3F); break; + case 0x41: BX_ERROR(("write cmd 0x41 (READ VERIFY SECTORS NO RETRY) not supported")); command_aborted(channel, 0x41); break; + case 0x42: BX_ERROR(("write cmd 0x42 (READ VERIFY SECTORS EXT) not supported"));command_aborted(channel, 0x42); break; + case 0x50: BX_ERROR(("write cmd 0x50 (FORMAT TRACK) not supported")); command_aborted(channel, 0x50); break; + case 0x51: BX_ERROR(("write cmd 0x51 (CONFIGURE STREAM) not supported"));command_aborted(channel, 0x51); break; + case 0x87: BX_ERROR(("write cmd 0x87 (CFA TRANSLATE SECTOR) not supported"));command_aborted(channel, 0x87); break; + case 0x92: BX_ERROR(("write cmd 0x92 (DOWNLOAD MICROCODE) not supported"));command_aborted(channel, 0x92); break; + case 0x94: BX_ERROR(("write cmd 0x94 (STANDBY IMMEDIATE) not supported")); command_aborted(channel, 0x94); break; + case 0x95: BX_ERROR(("write cmd 0x95 (IDLE IMMEDIATE) not supported")); command_aborted(channel, 0x95); break; + case 0x96: BX_ERROR(("write cmd 0x96 (STANDBY) not supported")); command_aborted(channel, 0x96); break; + case 0x97: BX_ERROR(("write cmd 0x97 (IDLE) not supported")); command_aborted(channel, 0x97); break; + case 0x98: BX_ERROR(("write cmd 0x98 (CHECK POWER MODE) not supported")); command_aborted(channel, 0x98); break; + case 0x99: BX_ERROR(("write cmd 0x99 (SLEEP) not supported")); command_aborted(channel, 0x99); break; + case 0xB0: BX_ERROR(("write cmd 0xB0 (SMART commands) not supported"));command_aborted(channel, 0xB0); break; + case 0xB1: BX_ERROR(("write cmd 0xB1 (DEVICE CONFIGURATION commands) not supported"));command_aborted(channel, 0xB1); break; + case 0xC0: BX_ERROR(("write cmd 0xC0 (CFA ERASE SECTORS) not supported"));command_aborted(channel, 0xC0); break; + case 0xC4: BX_ERROR(("write cmd 0xC4 (READ MULTIPLE) not supported"));command_aborted(channel, 0xC4); break; + case 0xC5: BX_ERROR(("write cmd 0xC5 (WRITE MULTIPLE) not supported"));command_aborted(channel, 0xC5); break; + case 0xC7: BX_ERROR(("write cmd 0xC7 (READ DMA QUEUED) not supported"));command_aborted(channel, 0xC7); break; + case 0xC8: BX_ERROR(("write cmd 0xC8 (READ DMA) not supported"));command_aborted(channel, 0xC8); break; + case 0xC9: BX_ERROR(("write cmd 0xC9 (READ DMA NO RETRY) not supported")); command_aborted(channel, 0xC9); break; + case 0xCA: BX_ERROR(("write cmd 0xCA (WRITE DMA) not supported"));command_aborted(channel, 0xCA); break; + case 0xCC: BX_ERROR(("write cmd 0xCC (WRITE DMA QUEUED) not supported"));command_aborted(channel, 0xCC); break; + case 0xCD: BX_ERROR(("write cmd 0xCD (CFA WRITE MULTIPLE W/OUT ERASE) not supported"));command_aborted(channel, 0xCD); break; + case 0xD1: BX_ERROR(("write cmd 0xD1 (CHECK MEDIA CARD TYPE) not supported"));command_aborted(channel, 0xD1); break; + case 0xDA: BX_ERROR(("write cmd 0xDA (GET MEDIA STATUS) not supported"));command_aborted(channel, 0xDA); break; + case 0xDE: BX_ERROR(("write cmd 0xDE (MEDIA LOCK) not supported"));command_aborted(channel, 0xDE); break; + case 0xDF: BX_ERROR(("write cmd 0xDF (MEDIA UNLOCK) not supported"));command_aborted(channel, 0xDF); break; + case 0xE0: BX_ERROR(("write cmd 0xE0 (STANDBY IMMEDIATE) not supported"));command_aborted(channel, 0xE0); break; + case 0xE1: BX_ERROR(("write cmd 0xE1 (IDLE IMMEDIATE) not supported"));command_aborted(channel, 0xE1); break; + case 0xE2: BX_ERROR(("write cmd 0xE2 (STANDBY) not supported"));command_aborted(channel, 0xE2); break; + case 0xE3: BX_ERROR(("write cmd 0xE3 (IDLE) not supported"));command_aborted(channel, 0xE3); break; + case 0xE4: BX_ERROR(("write cmd 0xE4 (READ BUFFER) not supported"));command_aborted(channel, 0xE4); break; + case 0xE6: BX_ERROR(("write cmd 0xE6 (SLEEP) not supported"));command_aborted(channel, 0xE6); break; + case 0xE7: BX_ERROR(("write cmd 0xE7 (FLUSH CACHE) not supported"));command_aborted(channel, 0xE7); break; + case 0xE8: BX_ERROR(("write cmd 0xE8 (WRITE BUFFER) not supported"));command_aborted(channel, 0xE8); break; + case 0xEA: BX_ERROR(("write cmd 0xEA (FLUSH CACHE EXT) not supported"));command_aborted(channel, 0xEA); break; + case 0xED: BX_ERROR(("write cmd 0xED (MEDIA EJECT) not supported"));command_aborted(channel, 0xED); break; + case 0xF1: BX_ERROR(("write cmd 0xF1 (SECURITY SET PASSWORD) not supported"));command_aborted(channel, 0xF1); break; + case 0xF2: BX_ERROR(("write cmd 0xF2 (SECURITY UNLOCK) not supported"));command_aborted(channel, 0xF2); break; + case 0xF3: BX_ERROR(("write cmd 0xF3 (SECURITY ERASE PREPARE) not supported"));command_aborted(channel, 0xF3); break; + case 0xF4: BX_ERROR(("write cmd 0xF4 (SECURITY ERASE UNIT) not supported"));command_aborted(channel, 0xF4); break; + case 0xF5: BX_ERROR(("write cmd 0xF5 (SECURITY FREEZE LOCK) not supported"));command_aborted(channel, 0xF5); break; + case 0xF6: BX_ERROR(("write cmd 0xF6 (SECURITY DISABLE PASSWORD) not supported"));command_aborted(channel, 0xF6); break; + case 0xF8: BX_ERROR(("write cmd 0xF8 (READ NATIVE MAX ADDRESS) not supported"));command_aborted(channel, 0xF8); break; + case 0xF9: BX_ERROR(("write cmd 0xF9 (SET MAX ADDRESS) not supported"));command_aborted(channel, 0xF9); break; + + default: + BX_PANIC(("IO write(0x%04x): command 0x%02x", address, (unsigned) value)); + // if user foolishly decides to continue, abort the command + // so that the software knows the drive didn't understand it. + command_aborted(channel, value); + } + break; + + case 0x16: // hard disk adapter control 0x3f6 + // (mch) Even if device 1 was selected, a write to this register + // goes to device 0 (if device 1 is absent) + + prev_control_reset = BX_SELECTED_CONTROLLER(channel).control.reset; + BX_HD_THIS channels[channel].drives[0].controller.control.reset = value & 0x04; + BX_HD_THIS channels[channel].drives[1].controller.control.reset = value & 0x04; + // CGS: was: BX_SELECTED_CONTROLLER(channel).control.disable_irq = value & 0x02; + BX_HD_THIS channels[channel].drives[0].controller.control.disable_irq = value & 0x02; + BX_HD_THIS channels[channel].drives[1].controller.control.disable_irq = value & 0x02; + + BX_DEBUG(( "adpater control reg: reset controller = %d", + (unsigned) (BX_SELECTED_CONTROLLER(channel).control.reset) ? 1 : 0 )); + BX_DEBUG(( "adpater control reg: disable_irq(X) = %d", + (unsigned) (BX_SELECTED_CONTROLLER(channel).control.disable_irq) ? 1 : 0 )); + + if (!prev_control_reset && BX_SELECTED_CONTROLLER(channel).control.reset) { + // transition from 0 to 1 causes all drives to reset + BX_DEBUG(("hard drive: RESET")); + + // (mch) Set BSY, drive not ready + for (int id = 0; id < 2; id++) { + BX_CONTROLLER(channel,id).status.busy = 1; + BX_CONTROLLER(channel,id).status.drive_ready = 0; + BX_CONTROLLER(channel,id).reset_in_progress = 1; + + BX_CONTROLLER(channel,id).status.write_fault = 0; + BX_CONTROLLER(channel,id).status.seek_complete = 1; + BX_CONTROLLER(channel,id).status.drq = 0; + BX_CONTROLLER(channel,id).status.corrected_data = 0; + BX_CONTROLLER(channel,id).status.err = 0; + + BX_CONTROLLER(channel,id).error_register = 0x01; // diagnostic code: no error + + BX_CONTROLLER(channel,id).current_command = 0x00; + BX_CONTROLLER(channel,id).buffer_index = 0; + + BX_CONTROLLER(channel,id).sectors_per_block = 0x80; + BX_CONTROLLER(channel,id).lba_mode = 0; + + BX_CONTROLLER(channel,id).control.disable_irq = 0; + DEV_pic_lower_irq(BX_HD_THIS channels[channel].irq); + } + } else if (BX_SELECTED_CONTROLLER(channel).reset_in_progress && + !BX_SELECTED_CONTROLLER(channel).control.reset) { + // Clear BSY and DRDY + BX_DEBUG(("Reset complete {%s}", BX_SELECTED_TYPE_STRING(channel))); + for (int id = 0; id < 2; id++) { + BX_CONTROLLER(channel,id).status.busy = 0; + BX_CONTROLLER(channel,id).status.drive_ready = 1; + BX_CONTROLLER(channel,id).reset_in_progress = 0; + + // Device signature + if (BX_DRIVE_IS_HD(channel,id)) { + BX_CONTROLLER(channel,id).head_no = 0; + BX_CONTROLLER(channel,id).sector_count = 1; + BX_CONTROLLER(channel,id).sector_no = 1; + BX_CONTROLLER(channel,id).cylinder_no = 0; + } else { + BX_CONTROLLER(channel,id).head_no = 0; + BX_CONTROLLER(channel,id).sector_count = 1; + BX_CONTROLLER(channel,id).sector_no = 1; + BX_CONTROLLER(channel,id).cylinder_no = 0xeb14; + } + } + } + BX_DEBUG(("s[0].controller.control.disable_irq = %02x", (BX_HD_THIS channels[channel].drives[0]).controller.control.disable_irq)); + BX_DEBUG(("s[1].controller.control.disable_irq = %02x", (BX_HD_THIS channels[channel].drives[1]).controller.control.disable_irq)); + break; + + default: + BX_PANIC(("hard drive: io write to address %x = %02x", + (unsigned) address, (unsigned) value)); + } +} + + void +bx_hard_drive_c::close_harddrive(void) +{ + for (Bit8u channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) { + if(BX_HD_THIS channels[channel].drives[0].hard_drive != NULL) + BX_HD_THIS channels[channel].drives[0].hard_drive->close(); + if(BX_HD_THIS channels[channel].drives[1].hard_drive != NULL) + BX_HD_THIS channels[channel].drives[1].hard_drive->close(); + } +} + + + bx_bool BX_CPP_AttrRegparmN(2) +bx_hard_drive_c::calculate_logical_address(Bit8u channel, off_t *sector) +{ + off_t logical_sector; + + if (BX_SELECTED_CONTROLLER(channel).lba_mode) { + //bx_printf ("disk: calculate: %d %d %d\n", ((Bit32u)BX_SELECTED_CONTROLLER(channel).head_no), ((Bit32u)BX_SELECTED_CONTROLLER(channel).cylinder_no), (Bit32u)BX_SELECTED_CONTROLLER(channel).sector_no); + logical_sector = ((Bit32u)BX_SELECTED_CONTROLLER(channel).head_no) << 24 | + ((Bit32u)BX_SELECTED_CONTROLLER(channel).cylinder_no) << 8 | + (Bit32u)BX_SELECTED_CONTROLLER(channel).sector_no; + //bx_printf ("disk: result: %u\n", logical_sector); + } else + logical_sector = (BX_SELECTED_CONTROLLER(channel).cylinder_no * BX_SELECTED_DRIVE(channel).hard_drive->heads * + BX_SELECTED_DRIVE(channel).hard_drive->sectors) + + (BX_SELECTED_CONTROLLER(channel).head_no * BX_SELECTED_DRIVE(channel).hard_drive->sectors) + + (BX_SELECTED_CONTROLLER(channel).sector_no - 1); + + Bit32u sector_count= + (Bit32u)BX_SELECTED_DRIVE(channel).hard_drive->cylinders * + (Bit32u)BX_SELECTED_DRIVE(channel).hard_drive->heads * + (Bit32u)BX_SELECTED_DRIVE(channel).hard_drive->sectors; + + if (logical_sector >= sector_count) { + BX_ERROR (("calc_log_addr: out of bounds (%d/%d)", (Bit32u)logical_sector, sector_count)); + return false; + } + *sector = logical_sector; + return true; +} + + void BX_CPP_AttrRegparmN(1) +bx_hard_drive_c::increment_address(Bit8u channel) +{ + BX_SELECTED_CONTROLLER(channel).sector_count--; + + if (BX_SELECTED_CONTROLLER(channel).lba_mode) { + off_t current_address; + calculate_logical_address(channel, ¤t_address); + current_address++; + BX_SELECTED_CONTROLLER(channel).head_no = (Bit8u)((current_address >> 24) & 0xf); + BX_SELECTED_CONTROLLER(channel).cylinder_no = (Bit16u)((current_address >> 8) & 0xffff); + BX_SELECTED_CONTROLLER(channel).sector_no = (Bit8u)((current_address) & 0xff); + } else { + BX_SELECTED_CONTROLLER(channel).sector_no++; + if (BX_SELECTED_CONTROLLER(channel).sector_no > BX_SELECTED_DRIVE(channel).hard_drive->sectors) { + BX_SELECTED_CONTROLLER(channel).sector_no = 1; + BX_SELECTED_CONTROLLER(channel).head_no++; + if (BX_SELECTED_CONTROLLER(channel).head_no >= BX_SELECTED_DRIVE(channel).hard_drive->heads) { + BX_SELECTED_CONTROLLER(channel).head_no = 0; + BX_SELECTED_CONTROLLER(channel).cylinder_no++; + if (BX_SELECTED_CONTROLLER(channel).cylinder_no >= BX_SELECTED_DRIVE(channel).hard_drive->cylinders) + BX_SELECTED_CONTROLLER(channel).cylinder_no = BX_SELECTED_DRIVE(channel).hard_drive->cylinders - 1; + } + } + } +} + + void +bx_hard_drive_c::identify_ATAPI_drive(Bit8u channel) +{ + unsigned i; + + BX_SELECTED_DRIVE(channel).id_drive[0] = (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0); // Removable CDROM, 50us response, 12 byte packets + + for (i = 1; i <= 9; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + const char* serial_number = " VT00001\0\0\0\0\0\0\0\0\0\0\0\0"; + for (i = 0; i < 10; i++) { + BX_SELECTED_DRIVE(channel).id_drive[10+i] = (serial_number[i*2] << 8) | + serial_number[i*2 + 1]; + } + + for (i = 20; i <= 22; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + const char* firmware = "ALPHA1 "; + for (i = 0; i < strlen(firmware)/2; i++) { + BX_SELECTED_DRIVE(channel).id_drive[23+i] = (firmware[i*2] << 8) | + firmware[i*2 + 1]; + } + BX_ASSERT((23+i) == 27); + + for (i = 0; i < strlen((char *) BX_SELECTED_MODEL(channel))/2; i++) { + BX_SELECTED_DRIVE(channel).id_drive[27+i] = (BX_SELECTED_MODEL(channel)[i*2] << 8) | + BX_SELECTED_MODEL(channel)[i*2 + 1]; + } + BX_ASSERT((27+i) == 47); + + BX_SELECTED_DRIVE(channel).id_drive[47] = 0; + BX_SELECTED_DRIVE(channel).id_drive[48] = 1; // 32 bits access + + BX_SELECTED_DRIVE(channel).id_drive[49] = (1 << 9); // LBA supported + + BX_SELECTED_DRIVE(channel).id_drive[50] = 0; + BX_SELECTED_DRIVE(channel).id_drive[51] = 0; + BX_SELECTED_DRIVE(channel).id_drive[52] = 0; + + BX_SELECTED_DRIVE(channel).id_drive[53] = 3; // words 64-70, 54-58 valid + + for (i = 54; i <= 62; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + // copied from CFA540A + BX_SELECTED_DRIVE(channel).id_drive[63] = 0x0103; // variable (DMA stuff) + BX_SELECTED_DRIVE(channel).id_drive[64] = 0x0001; // PIO + BX_SELECTED_DRIVE(channel).id_drive[65] = 0x00b4; + BX_SELECTED_DRIVE(channel).id_drive[66] = 0x00b4; + BX_SELECTED_DRIVE(channel).id_drive[67] = 0x012c; + BX_SELECTED_DRIVE(channel).id_drive[68] = 0x00b4; + + BX_SELECTED_DRIVE(channel).id_drive[69] = 0; + BX_SELECTED_DRIVE(channel).id_drive[70] = 0; + BX_SELECTED_DRIVE(channel).id_drive[71] = 30; // faked + BX_SELECTED_DRIVE(channel).id_drive[72] = 30; // faked + BX_SELECTED_DRIVE(channel).id_drive[73] = 0; + BX_SELECTED_DRIVE(channel).id_drive[74] = 0; + + BX_SELECTED_DRIVE(channel).id_drive[75] = 0; + + for (i = 76; i <= 79; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + BX_SELECTED_DRIVE(channel).id_drive[80] = 0x1e; // supports up to ATA/ATAPI-4 + BX_SELECTED_DRIVE(channel).id_drive[81] = 0; + BX_SELECTED_DRIVE(channel).id_drive[82] = 0; + BX_SELECTED_DRIVE(channel).id_drive[83] = 0; + BX_SELECTED_DRIVE(channel).id_drive[84] = 0; + BX_SELECTED_DRIVE(channel).id_drive[85] = 0; + BX_SELECTED_DRIVE(channel).id_drive[86] = 0; + BX_SELECTED_DRIVE(channel).id_drive[87] = 0; + BX_SELECTED_DRIVE(channel).id_drive[88] = 0; + + for (i = 89; i <= 126; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + BX_SELECTED_DRIVE(channel).id_drive[127] = 0; + BX_SELECTED_DRIVE(channel).id_drive[128] = 0; + + for (i = 129; i <= 159; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + for (i = 160; i <= 255; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + // now convert the id_drive array (native 256 word format) to + // the controller buffer (512 bytes) + Bit16u temp16; + for (i = 0; i <= 255; i++) { + temp16 = BX_SELECTED_DRIVE(channel).id_drive[i]; + BX_SELECTED_CONTROLLER(channel).buffer[i*2] = temp16 & 0x00ff; + BX_SELECTED_CONTROLLER(channel).buffer[i*2+1] = temp16 >> 8; + } +} + + void +bx_hard_drive_c::identify_drive(Bit8u channel) +{ + unsigned i; + Bit32u temp32; + Bit16u temp16; + +#if defined(CONNER_CFA540A) + BX_SELECTED_DRIVE(channel).id_drive[0] = 0x0c5a; + BX_SELECTED_DRIVE(channel).id_drive[1] = 0x0418; + BX_SELECTED_DRIVE(channel).id_drive[2] = 0; + BX_SELECTED_DRIVE(channel).id_drive[3] = BX_SELECTED_DRIVE(channel).hard_drive->heads; + BX_SELECTED_DRIVE(channel).id_drive[4] = 0x9fb7; + BX_SELECTED_DRIVE(channel).id_drive[5] = 0x0289; + BX_SELECTED_DRIVE(channel).id_drive[6] = BX_SELECTED_DRIVE(channel).hard_drive->sectors; + BX_SELECTED_DRIVE(channel).id_drive[7] = 0x0030; + BX_SELECTED_DRIVE(channel).id_drive[8] = 0x000a; + BX_SELECTED_DRIVE(channel).id_drive[9] = 0x0000; + + char* serial_number = " CA00GSQ\0\0\0\0\0\0\0\0\0\0\0\0"; + for (i = 0; i < 10; i++) { + BX_SELECTED_DRIVE(channel).id_drive[10+i] = (serial_number[i*2] << 8) | + serial_number[i*2 + 1]; + } + + BX_SELECTED_DRIVE(channel).id_drive[20] = 3; + BX_SELECTED_DRIVE(channel).id_drive[21] = 512; // 512 Sectors = 256kB cache + BX_SELECTED_DRIVE(channel).id_drive[22] = 4; + + char* firmware = "8FT054 "; + for (i = 0; i < strlen(firmware)/2; i++) { + BX_SELECTED_DRIVE(channel).id_drive[23+i] = (firmware[i*2] << 8) | + firmware[i*2 + 1]; + } + BX_ASSERT((23+i) == 27); + + char* model = "Conner Peripherals 540MB - CFA540A "; + for (i = 0; i < strlen(model)/2; i++) { + BX_SELECTED_DRIVE(channel).id_drive[27+i] = (model[i*2] << 8) | + model[i*2 + 1]; + } + BX_ASSERT((27+i) == 47); + + BX_SELECTED_DRIVE(channel).id_drive[47] = 0x8080; // multiple mode identification + BX_SELECTED_DRIVE(channel).id_drive[48] = 0; + BX_SELECTED_DRIVE(channel).id_drive[49] = 0x0f01; + + BX_SELECTED_DRIVE(channel).id_drive[50] = 0; + + BX_SELECTED_DRIVE(channel).id_drive[51] = 0; + BX_SELECTED_DRIVE(channel).id_drive[52] = 0x0002; + BX_SELECTED_DRIVE(channel).id_drive[53] = 0x0003; + BX_SELECTED_DRIVE(channel).id_drive[54] = 0x0418; + + BX_SELECTED_DRIVE(channel).id_drive[55] = BX_SELECTED_DRIVE(channel).hard_drive->heads; + BX_SELECTED_DRIVE(channel).id_drive[56] = BX_SELECTED_DRIVE(channel).hard_drive->sectors; + + BX_SELECTED_DRIVE(channel).id_drive[57] = 0x1e80; + BX_SELECTED_DRIVE(channel).id_drive[58] = 0x0010; + BX_SELECTED_DRIVE(channel).id_drive[59] = 0x0100 | BX_SELECTED_CONTROLLER(channel).sectors_per_block; + BX_SELECTED_DRIVE(channel).id_drive[60] = 0x20e0; + BX_SELECTED_DRIVE(channel).id_drive[61] = 0x0010; + + BX_SELECTED_DRIVE(channel).id_drive[62] = 0; + + BX_SELECTED_DRIVE(channel).id_drive[63] = 0x0103; // variable (DMA stuff) + BX_SELECTED_DRIVE(channel).id_drive[64] = 0x0001; // PIO + BX_SELECTED_DRIVE(channel).id_drive[65] = 0x00b4; + BX_SELECTED_DRIVE(channel).id_drive[66] = 0x00b4; + BX_SELECTED_DRIVE(channel).id_drive[67] = 0x012c; + BX_SELECTED_DRIVE(channel).id_drive[68] = 0x00b4; + + for (i = 69; i <= 79; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + BX_SELECTED_DRIVE(channel).id_drive[80] = 0; + + BX_SELECTED_DRIVE(channel).id_drive[81] = 0; + + BX_SELECTED_DRIVE(channel).id_drive[82] = 0; + BX_SELECTED_DRIVE(channel).id_drive[83] = 0; + BX_SELECTED_DRIVE(channel).id_drive[84] = 0; + BX_SELECTED_DRIVE(channel).id_drive[85] = 0; + BX_SELECTED_DRIVE(channel).id_drive[86] = 0; + BX_SELECTED_DRIVE(channel).id_drive[87] = 0; + + for (i = 88; i <= 127; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + BX_SELECTED_DRIVE(channel).id_drive[128] = 0x0418; + BX_SELECTED_DRIVE(channel).id_drive[129] = 0x103f; + BX_SELECTED_DRIVE(channel).id_drive[130] = 0x0418; + BX_SELECTED_DRIVE(channel).id_drive[131] = 0x103f; + BX_SELECTED_DRIVE(channel).id_drive[132] = 0x0004; + BX_SELECTED_DRIVE(channel).id_drive[133] = 0xffff; + BX_SELECTED_DRIVE(channel).id_drive[134] = 0; + BX_SELECTED_DRIVE(channel).id_drive[135] = 0x5050; + + for (i = 136; i <= 144; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + BX_SELECTED_DRIVE(channel).id_drive[145] = 0x302e; + BX_SELECTED_DRIVE(channel).id_drive[146] = 0x3245; + BX_SELECTED_DRIVE(channel).id_drive[147] = 0x2020; + BX_SELECTED_DRIVE(channel).id_drive[148] = 0x2020; + + for (i = 149; i <= 255; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + +#else + + // Identify Drive command return values definition + // + // This code is rehashed from some that was donated. + // I'm using ANSI X3.221-1994, AT Attachment Interface for Disk Drives + // and X3T10 2008D Working Draft for ATA-3 + + + // Word 0: general config bit-significant info + // Note: bits 1-5 and 8-14 are now "Vendor specific (obsolete)" + // bit 15: 0=ATA device + // 1=ATAPI device + // bit 14: 1=format speed tolerance gap required + // bit 13: 1=track offset option available + // bit 12: 1=data strobe offset option available + // bit 11: 1=rotational speed tolerance is > 0,5% (typo?) + // bit 10: 1=disk transfer rate > 10Mbs + // bit 9: 1=disk transfer rate > 5Mbs but <= 10Mbs + // bit 8: 1=disk transfer rate <= 5Mbs + // bit 7: 1=removable cartridge drive + // bit 6: 1=fixed drive + // bit 5: 1=spindle motor control option implemented + // bit 4: 1=head switch time > 15 usec + // bit 3: 1=not MFM encoded + // bit 2: 1=soft sectored + // bit 1: 1=hard sectored + // bit 0: 0=reserved + BX_SELECTED_DRIVE(channel).id_drive[0] = 0x0040; + + // Word 1: number of user-addressable cylinders in + // default translation mode. If the value in words 60-61 + // exceed 16,515,072, this word shall contain 16,383. + BX_SELECTED_DRIVE(channel).id_drive[1] = BX_SELECTED_DRIVE(channel).hard_drive->cylinders; + + // Word 2: reserved + BX_SELECTED_DRIVE(channel).id_drive[2] = 0; + + // Word 3: number of user-addressable heads in default + // translation mode + BX_SELECTED_DRIVE(channel).id_drive[3] = BX_SELECTED_DRIVE(channel).hard_drive->heads; + + // Word 4: # unformatted bytes per translated track in default xlate mode + // Word 5: # unformatted bytes per sector in default xlated mode + // Word 6: # user-addressable sectors per track in default xlate mode + // Note: words 4,5 are now "Vendor specific (obsolete)" + BX_SELECTED_DRIVE(channel).id_drive[4] = (512 * BX_SELECTED_DRIVE(channel).hard_drive->sectors); + BX_SELECTED_DRIVE(channel).id_drive[5] = 512; + BX_SELECTED_DRIVE(channel).id_drive[6] = BX_SELECTED_DRIVE(channel).hard_drive->sectors; + + // Word 7-9: Vendor specific + for (i=7; i<=9; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + // Word 10-19: Serial number (20 ASCII characters, 0000h=not specified) + // This field is right justified and padded with spaces (20h). + for (i=10; i<=19; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + // Word 20: buffer type + // 0000h = not specified + // 0001h = single ported single sector buffer which is + // not capable of simulataneous data xfers to/from + // the host and the disk. + // 0002h = dual ported multi-sector buffer capable of + // simulatenous data xfers to/from the host and disk. + // 0003h = dual ported mutli-sector buffer capable of + // simulatenous data xfers with a read caching + // capability. + // 0004h-ffffh = reserved + BX_SELECTED_DRIVE(channel).id_drive[20] = 3; + + // Word 21: buffer size in 512 byte increments, 0000h = not specified + BX_SELECTED_DRIVE(channel).id_drive[21] = 512; // 512 Sectors = 256kB cache + + // Word 22: # of ECC bytes available on read/write long cmds + // 0000h = not specified + BX_SELECTED_DRIVE(channel).id_drive[22] = 4; + + // Word 23..26: Firmware revision (8 ascii chars, 0000h=not specified) + // This field is left justified and padded with spaces (20h) + for (i=23; i<=26; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + // Word 27..46: Model number (40 ascii chars, 0000h=not specified) + // This field is left justified and padded with spaces (20h) +// for (i=27; i<=46; i++) +// BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + for (i=0; i<20; i++) { + BX_SELECTED_DRIVE(channel).id_drive[27+i] = (BX_SELECTED_MODEL(channel)[i*2] << 8) | + BX_SELECTED_MODEL(channel)[i*2 + 1]; + } + + // Word 47: 15-8 Vendor unique + // 7-0 00h= read/write multiple commands not implemented + // xxh= maximum # of sectors that can be transferred + // per interrupt on read and write multiple commands + BX_SELECTED_DRIVE(channel).id_drive[47] = max_multiple_sectors; + + // Word 48: 0000h = cannot perform dword IO + // 0001h = can perform dword IO + BX_SELECTED_DRIVE(channel).id_drive[48] = 1; + + // Word 49: Capabilities + // 15-10: 0 = reserved + // 9: 1 = LBA supported + // 8: 1 = DMA supported + // 7-0: Vendor unique + BX_SELECTED_DRIVE(channel).id_drive[49] = 1<<9; + + // Word 50: Reserved + BX_SELECTED_DRIVE(channel).id_drive[50] = 0; + + // Word 51: 15-8 PIO data transfer cycle timing mode + // 7-0 Vendor unique + BX_SELECTED_DRIVE(channel).id_drive[51] = 0x200; + + // Word 52: 15-8 DMA data transfer cycle timing mode + // 7-0 Vendor unique + BX_SELECTED_DRIVE(channel).id_drive[52] = 0x200; + + // Word 53: 15-1 Reserved + // 0 1=the fields reported in words 54-58 are valid + // 0=the fields reported in words 54-58 may be valid + BX_SELECTED_DRIVE(channel).id_drive[53] = 0; + + // Word 54: # of user-addressable cylinders in curr xlate mode + // Word 55: # of user-addressable heads in curr xlate mode + // Word 56: # of user-addressable sectors/track in curr xlate mode + BX_SELECTED_DRIVE(channel).id_drive[54] = BX_SELECTED_DRIVE(channel).hard_drive->cylinders; + BX_SELECTED_DRIVE(channel).id_drive[55] = BX_SELECTED_DRIVE(channel).hard_drive->heads; + BX_SELECTED_DRIVE(channel).id_drive[56] = BX_SELECTED_DRIVE(channel).hard_drive->sectors; + + // Word 57-58: Current capacity in sectors + // Excludes all sectors used for device specific purposes. + temp32 = + BX_SELECTED_DRIVE(channel).hard_drive->cylinders * + BX_SELECTED_DRIVE(channel).hard_drive->heads * + BX_SELECTED_DRIVE(channel).hard_drive->sectors; + BX_SELECTED_DRIVE(channel).id_drive[57] = (temp32 & 0xffff); // LSW + BX_SELECTED_DRIVE(channel).id_drive[58] = (temp32 >> 16); // MSW + + // Word 59: 15-9 Reserved + // 8 1=multiple sector setting is valid + // 7-0 current setting for number of sectors that can be + // transferred per interrupt on R/W multiple commands + BX_SELECTED_DRIVE(channel).id_drive[59] = 0x0000 | curr_multiple_sectors; + + // Word 60-61: + // If drive supports LBA Mode, these words reflect total # of user + // addressable sectors. This value does not depend on the current + // drive geometry. If the drive does not support LBA mode, these + // words shall be set to 0. + Bit32u num_sects = BX_SELECTED_DRIVE(channel).hard_drive->cylinders * BX_SELECTED_DRIVE(channel).hard_drive->heads * BX_SELECTED_DRIVE(channel).hard_drive->sectors; + BX_SELECTED_DRIVE(channel).id_drive[60] = num_sects & 0xffff; // LSW + BX_SELECTED_DRIVE(channel).id_drive[61] = num_sects >> 16; // MSW + + // Word 62: 15-8 single word DMA transfer mode active + // 7-0 single word DMA transfer modes supported + // The low order byte identifies by bit, all the Modes which are + // supported e.g., if Mode 0 is supported bit 0 is set. + // The high order byte contains a single bit set to indiciate + // which mode is active. + BX_SELECTED_DRIVE(channel).id_drive[62] = 0x0; + + // Word 63: 15-8 multiword DMA transfer mode active + // 7-0 multiword DMA transfer modes supported + // The low order byte identifies by bit, all the Modes which are + // supported e.g., if Mode 0 is supported bit 0 is set. + // The high order byte contains a single bit set to indiciate + // which mode is active. + BX_SELECTED_DRIVE(channel).id_drive[63] = 0x0; + + // Word 64-79 Reserved + for (i=64; i<=79; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + // Word 80: 15-5 reserved + // 4 supports ATA/ATAPI-4 + // 3 supports ATA-3 + // 2 supports ATA-2 + // 1 supports ATA-1 + // 0 reserved + BX_SELECTED_DRIVE(channel).id_drive[80] = (1 << 2) | (1 << 1); + + // Word 81: Minor version number + BX_SELECTED_DRIVE(channel).id_drive[81] = 0; + + // Word 82: 15 obsolete + // 14 NOP command supported + // 13 READ BUFFER command supported + // 12 WRITE BUFFER command supported + // 11 obsolete + // 10 Host protected area feature set supported + // 9 DEVICE RESET command supported + // 8 SERVICE interrupt supported + // 7 release interrupt supported + // 6 look-ahead supported + // 5 write cache supported + // 4 supports PACKET command feature set + // 3 supports power management feature set + // 2 supports removable media feature set + // 1 supports securite mode feature set + // 0 support SMART feature set + BX_SELECTED_DRIVE(channel).id_drive[82] = 1 << 14; + BX_SELECTED_DRIVE(channel).id_drive[83] = 1 << 14; + BX_SELECTED_DRIVE(channel).id_drive[84] = 1 << 14; + BX_SELECTED_DRIVE(channel).id_drive[85] = 1 << 14; + BX_SELECTED_DRIVE(channel).id_drive[86] = 0; + BX_SELECTED_DRIVE(channel).id_drive[87] = 1 << 14; + + for (i=88; i<=127; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + // Word 128-159 Vendor unique + for (i=128; i<=159; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + + // Word 160-255 Reserved + for (i=160; i<=255; i++) + BX_SELECTED_DRIVE(channel).id_drive[i] = 0; + +#endif + + BX_DEBUG(("Drive ID Info. initialized : %04d {%s}", 512, BX_SELECTED_TYPE_STRING(channel))); + + // now convert the id_drive array (native 256 word format) to + // the controller buffer (512 bytes) + for (i=0; i<=255; i++) { + temp16 = BX_SELECTED_DRIVE(channel).id_drive[i]; + BX_SELECTED_CONTROLLER(channel).buffer[i*2] = temp16 & 0x00ff; + BX_SELECTED_CONTROLLER(channel).buffer[i*2+1] = temp16 >> 8; + } +} + + void BX_CPP_AttrRegparmN(3) +bx_hard_drive_c::init_send_atapi_command(Bit8u channel, Bit8u command, int req_length, int alloc_length, bool lazy) +{ + // BX_SELECTED_CONTROLLER(channel).byte_count is a union of BX_SELECTED_CONTROLLER(channel).cylinder_no; + // lazy is used to force a data read in the buffer at the next read. + + if (BX_SELECTED_CONTROLLER(channel).byte_count == 0xffff) + BX_SELECTED_CONTROLLER(channel).byte_count = 0xfffe; + + if ((BX_SELECTED_CONTROLLER(channel).byte_count & 1) + && !(alloc_length <= BX_SELECTED_CONTROLLER(channel).byte_count)) { + BX_INFO(("Odd byte count (0x%04x) to ATAPI command 0x%02x, using 0x%04x", + BX_SELECTED_CONTROLLER(channel).byte_count, command, BX_SELECTED_CONTROLLER(channel).byte_count - 1)); + BX_SELECTED_CONTROLLER(channel).byte_count -= 1; + } + + if (BX_SELECTED_CONTROLLER(channel).byte_count == 0) + BX_PANIC(("ATAPI command with zero byte count")); + + if (alloc_length < 0) + BX_PANIC(("Allocation length < 0")); + if (alloc_length == 0) + alloc_length = BX_SELECTED_CONTROLLER(channel).byte_count; + + BX_SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1; + BX_SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 0; + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drq = 1; + BX_SELECTED_CONTROLLER(channel).status.err = 0; + + // no bytes transfered yet + if (lazy) + BX_SELECTED_CONTROLLER(channel).buffer_index = 2048; + else + BX_SELECTED_CONTROLLER(channel).buffer_index = 0; + BX_SELECTED_CONTROLLER(channel).drq_index = 0; + + if (BX_SELECTED_CONTROLLER(channel).byte_count > req_length) + BX_SELECTED_CONTROLLER(channel).byte_count = req_length; + + if (BX_SELECTED_CONTROLLER(channel).byte_count > alloc_length) + BX_SELECTED_CONTROLLER(channel).byte_count = alloc_length; + + BX_SELECTED_DRIVE(channel).atapi.command = command; + BX_SELECTED_DRIVE(channel).atapi.drq_bytes = BX_SELECTED_CONTROLLER(channel).byte_count; + BX_SELECTED_DRIVE(channel).atapi.total_bytes_remaining = (req_length < alloc_length) ? req_length : alloc_length; + + // if (lazy) { + // // bias drq_bytes and total_bytes_remaining + // BX_SELECTED_DRIVE(channel).atapi.drq_bytes += 2048; + // BX_SELECTED_DRIVE(channel).atapi.total_bytes_remaining += 2048; + // } +} + +void +bx_hard_drive_c::atapi_cmd_error(Bit8u channel, sense_t sense_key, asc_t asc) +{ + BX_ERROR(("atapi_cmd_error channel=%02x key=%02x asc=%02x", channel, sense_key, asc)); + + BX_SELECTED_CONTROLLER(channel).error_register = sense_key << 4; + BX_SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1; + BX_SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 1; + BX_SELECTED_CONTROLLER(channel).interrupt_reason.rel = 0; + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.write_fault = 0; + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 1; + + BX_SELECTED_DRIVE(channel).sense.sense_key = sense_key; + BX_SELECTED_DRIVE(channel).sense.asc = asc; + BX_SELECTED_DRIVE(channel).sense.ascq = 0; +} + +void BX_CPP_AttrRegparmN(1) +bx_hard_drive_c::atapi_cmd_nop(Bit8u channel) +{ + BX_SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1; + BX_SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 1; + BX_SELECTED_CONTROLLER(channel).interrupt_reason.rel = 0; + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.err = 0; +} + +void +bx_hard_drive_c::init_mode_sense_single(Bit8u channel, const void* src, int size) +{ + // Header + BX_SELECTED_CONTROLLER(channel).buffer[0] = (size+6) >> 8; + BX_SELECTED_CONTROLLER(channel).buffer[1] = (size+6) & 0xff; + BX_SELECTED_CONTROLLER(channel).buffer[2] = 0x70; // no media present + BX_SELECTED_CONTROLLER(channel).buffer[3] = 0; // reserved + BX_SELECTED_CONTROLLER(channel).buffer[4] = 0; // reserved + BX_SELECTED_CONTROLLER(channel).buffer[5] = 0; // reserved + BX_SELECTED_CONTROLLER(channel).buffer[6] = 0; // reserved + BX_SELECTED_CONTROLLER(channel).buffer[7] = 0; // reserved + + // Data + memcpy(BX_SELECTED_CONTROLLER(channel).buffer + 8, src, size); +} + + void BX_CPP_AttrRegparmN(1) +bx_hard_drive_c::ready_to_send_atapi(Bit8u channel) +{ + raise_interrupt(channel); +} + +void BX_CPP_AttrRegparmN(1) +bx_hard_drive_c::raise_interrupt(Bit8u channel) +{ + BX_DEBUG(("raise_interrupt called, disable_irq = %02x", BX_SELECTED_CONTROLLER(channel).control.disable_irq)); + if (!BX_SELECTED_CONTROLLER(channel).control.disable_irq) { BX_DEBUG(("raising interrupt")); } else { BX_DEBUG(("Not raising interrupt")); } + if (!BX_SELECTED_CONTROLLER(channel).control.disable_irq) { + Bit32u irq = BX_HD_THIS channels[channel].irq; + BX_DEBUG(("Raising interrupt %d {%s}", irq, BX_SELECTED_TYPE_STRING(channel))); + DEV_pic_raise_irq(irq); + } else { + if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom)) + BX_INFO(("Interrupt masked {%s}", BX_SELECTED_TYPE_STRING(channel))); + } +} + + void +bx_hard_drive_c::command_aborted(Bit8u channel, unsigned value) +{ + BX_DEBUG(("aborting on command 0x%02x {%s}", value, BX_SELECTED_TYPE_STRING(channel))); + BX_SELECTED_CONTROLLER(channel).current_command = 0; + BX_SELECTED_CONTROLLER(channel).status.busy = 0; + BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1; + BX_SELECTED_CONTROLLER(channel).status.err = 1; + BX_SELECTED_CONTROLLER(channel).error_register = 0x04; // command ABORTED + BX_SELECTED_CONTROLLER(channel).status.drq = 0; + BX_SELECTED_CONTROLLER(channel).status.seek_complete = 0; + BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0; + BX_SELECTED_CONTROLLER(channel).buffer_index = 0; + raise_interrupt(channel); +} + + Bit32u +bx_hard_drive_c::get_device_handle(Bit8u channel, Bit8u device) +{ + BX_DEBUG(("get_device_handle %d %d",channel, device)); + if ((channel < BX_MAX_ATA_CHANNEL) && (device < 2)) { + return ((channel*2) + device); + } + + return BX_MAX_ATA_CHANNEL*2; +} + + Bit32u +bx_hard_drive_c::get_first_cd_handle(void) +{ + for (Bit8u channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) { + if (BX_DRIVE_IS_CD(channel,0)) return (channel*2); + if (BX_DRIVE_IS_CD(channel,1)) return ((channel*2) + 1); + } + return BX_MAX_ATA_CHANNEL*2; +} + + unsigned +bx_hard_drive_c::get_cd_media_status(Bit32u handle) +{ + if ( handle >= BX_MAX_ATA_CHANNEL*2 ) return 0; + + Bit8u channel = handle / 2; + Bit8u device = handle % 2; + return( BX_HD_THIS channels[channel].drives[device].cdrom.ready ); +} + + unsigned +bx_hard_drive_c::set_cd_media_status(Bit32u handle, unsigned status) +{ + BX_DEBUG (("set_cd_media_status handle=%d status=%d", handle, status)); + if ( handle >= BX_MAX_ATA_CHANNEL*2 ) return 0; + + Bit8u channel = handle / 2; + Bit8u device = handle % 2; + + // if setting to the current value, nothing to do + if (status == BX_HD_THIS channels[channel].drives[device].cdrom.ready) + return(status); + // return 0 if no cdromd is present + if (!BX_DRIVE_IS_CD(channel,device)) + return(0); + + if (status == 0) { + // eject cdrom if not locked by guest OS + if (BX_HD_THIS channels[channel].drives[device].cdrom.locked) return(1); + else { +#ifdef LOWLEVEL_CDROM + BX_HD_THIS channels[channel].drives[device].cdrom.cd->eject_cdrom(); +#endif + BX_HD_THIS channels[channel].drives[device].cdrom.ready = 0; + bx_options.atadevice[channel][device].Ostatus->set(BX_EJECTED); + } + } + else { + // insert cdrom +#ifdef LOWLEVEL_CDROM + if (BX_HD_THIS channels[channel].drives[device].cdrom.cd->insert_cdrom(bx_options.atadevice[channel][device].Opath->getptr())) { + BX_INFO(( "Media present in CD-ROM drive")); + BX_HD_THIS channels[channel].drives[device].cdrom.ready = 1; + BX_HD_THIS channels[channel].drives[device].cdrom.capacity = BX_HD_THIS channels[channel].drives[device].cdrom.cd->capacity(); + bx_options.atadevice[channel][device].Ostatus->set(BX_INSERTED); + BX_SELECTED_DRIVE(channel).sense.sense_key = SENSE_UNIT_ATTENTION; + BX_SELECTED_DRIVE(channel).sense.asc = 0; + BX_SELECTED_DRIVE(channel).sense.ascq = 0; + raise_interrupt(channel); + } + else { +#endif + BX_INFO(( "Could not locate CD-ROM, continuing with media not present")); + BX_HD_THIS channels[channel].drives[device].cdrom.ready = 0; + bx_options.atadevice[channel][device].Ostatus->set(BX_EJECTED); +#ifdef LOWLEVEL_CDROM + } +#endif + } + return( BX_HD_THIS channels[channel].drives[device].cdrom.ready ); +} + + +/*** default_image_t function definitions ***/ + +int default_image_t::open (const char* pathname) +{ + return open(pathname, O_RDWR); +} + +int default_image_t::open (const char* pathname, int flags) +{ + fd = ::open(pathname, flags +#ifdef O_BINARY + | O_BINARY +#endif + ); + + if (fd < 0) { + return fd; + } + + /* look at size of image file to calculate disk geometry */ + struct stat stat_buf; + int ret = fstat(fd, &stat_buf); + if (ret) { + BX_PANIC(("fstat() returns error!")); + } + + return fd; +} + +void default_image_t::close () +{ + if (fd > -1) { + ::close(fd); + } +} + +off_t default_image_t::lseek (off_t offset, int whence) +{ + return ::lseek(fd, offset, whence); +} + +ssize_t default_image_t::read (void* buf, size_t count) +{ + return ::read(fd, (char*) buf, count); +} + +ssize_t default_image_t::write (const void* buf, size_t count) +{ + return ::write(fd, (char*) buf, count); +} + +char increment_string (char *str, int diff) +{ + // find the last character of the string, and increment it. + char *p = str; + while (*p != 0) p++; + BX_ASSERT (p>str); // choke on zero length strings + p--; // point to last character of the string + (*p) += diff; // increment to next/previous ascii code. + BX_DEBUG(("increment string returning '%s'", str)); + return (*p); +} + +/*** concat_image_t function definitions ***/ + +concat_image_t::concat_image_t () +{ + fd = -1; +} + +void concat_image_t::increment_string (char *str) +{ + ::increment_string(str, +1); +} + +int concat_image_t::open (const char* pathname0) +{ + char *pathname = strdup (pathname0); + BX_DEBUG(("concat_image_t.open")); + off_t start_offset = 0; + for (int i=0; i<BX_CONCAT_MAX_IMAGES; i++) { + fd_table[i] = ::open(pathname, O_RDWR +#ifdef O_BINARY + | O_BINARY +#endif + ); + if (fd_table[i] < 0) { + // open failed. + // if no FD was opened successfully, return -1 (fail). + if (i==0) return -1; + // otherwise, it only means that all images in the series have + // been opened. Record the number of fds opened successfully. + maxfd = i; + break; + } + BX_DEBUG(("concat_image: open image %s, fd[%d] = %d", pathname, i, fd_table[i])); + /* look at size of image file to calculate disk geometry */ + struct stat stat_buf; + int ret = fstat(fd_table[i], &stat_buf); + if (ret) { + BX_PANIC(("fstat() returns error!")); + } +#ifdef S_ISBLK + if (S_ISBLK(stat_buf.st_mode)) { + BX_PANIC(("block devices should REALLY NOT be used with --enable-split-hd. " + "Please reconfigure with --disable-split-hd")); + } +#endif + if ((stat_buf.st_size % 512) != 0) { + BX_PANIC(("size of disk image must be multiple of 512 bytes")); + } + length_table[i] = stat_buf.st_size; + start_offset_table[i] = start_offset; + start_offset += stat_buf.st_size; + increment_string (pathname); + } + // start up with first image selected + index = 0; + fd = fd_table[0]; + thismin = 0; + thismax = length_table[0]-1; + seek_was_last_op = 0; + return 0; // success. +} + +void concat_image_t::close () +{ + BX_DEBUG(("concat_image_t.close")); + if (fd > -1) { + ::close(fd); + } +} + +off_t concat_image_t::lseek (off_t offset, int whence) +{ + if ((offset % 512) != 0) + BX_PANIC( ("lseek HD with offset not multiple of 512")); + BX_DEBUG(("concat_image_t.lseek(%d)", whence)); + // is this offset in this disk image? + if (offset < thismin) { + // no, look at previous images + for (int i=index-1; i>=0; i--) { + if (offset >= start_offset_table[i]) { + index = i; + fd = fd_table[i]; + thismin = start_offset_table[i]; + thismax = thismin + length_table[i] - 1; + BX_DEBUG(("concat_image_t.lseek to earlier image, index=%d", index)); + break; + } + } + } else if (offset > thismax) { + // no, look at later images + for (int i=index+1; i<maxfd; i++) { + if (offset < start_offset_table[i] + length_table[i]) { + index = i; + fd = fd_table[i]; + thismin = start_offset_table[i]; + thismax = thismin + length_table[i] - 1; + BX_DEBUG(("concat_image_t.lseek to earlier image, index=%d", index)); + break; + } + } + } + // now offset should be within the current image. + offset -= start_offset_table[index]; + if (offset < 0 || offset >= length_table[index]) { + BX_PANIC(("concat_image_t.lseek to byte %ld failed", (long)offset)); + return -1; + } + + seek_was_last_op = 1; + return ::lseek(fd, offset, whence); +} + +ssize_t concat_image_t::read (void* buf, size_t count) +{ + if (bx_dbg.disk) + BX_DEBUG(("concat_image_t.read %ld bytes", (long)count)); + // notice if anyone does sequential read or write without seek in between. + // This can be supported pretty easily, but needs additional checks for + // end of a partial image. + if (!seek_was_last_op) + BX_PANIC( ("no seek before read")); + return ::read(fd, (char*) buf, count); +} + +ssize_t concat_image_t::write (const void* buf, size_t count) +{ + BX_DEBUG(("concat_image_t.write %ld bytes", (long)count)); + // notice if anyone does sequential read or write without seek in between. + // This can be supported pretty easily, but needs additional checks for + // end of a partial image. + if (!seek_was_last_op) + BX_PANIC( ("no seek before write")); + return ::write(fd, (char*) buf, count); +} + +/*** sparse_image_t function definitions ***/ +sparse_image_t::sparse_image_t () +{ + fd = -1; + pathname = NULL; +#ifdef _POSIX_MAPPED_FILES + mmap_header = NULL; +#endif + pagetable = NULL; +} + + +/* +void showpagetable(uint32 * pagetable, size_t numpages) +{ + printf("Non null pages: "); + for (int i = 0; i < numpages; i++) + { + if (pagetable[i] != 0xffffffff) + { + printf("%d ", i); + } + } + printf("\n"); +} +*/ + + +void sparse_image_t::read_header() +{ + BX_ASSERT(sizeof(header) == SPARSE_HEADER_SIZE); + + int ret = ::read(fd, &header, sizeof(header)); + + if (-1 == ret) + { + panic(strerror(errno)); + } + + if (sizeof(header) != ret) + { + panic("could not read entire header"); + } + + if (dtoh32(header.magic) != SPARSE_HEADER_MAGIC) + { + panic("failed header magic check"); + } + + if (dtoh32(header.version) != 1) + { + panic("unknown version in header"); + } + + pagesize = dtoh32(header.pagesize); + uint32 numpages = dtoh32(header.numpages); + + total_size = pagesize; + total_size *= numpages; + + pagesize_shift = 0; + while ((pagesize >> pagesize_shift) > 1) pagesize_shift++; + + if ((uint32)(1 << pagesize_shift) != pagesize) + { + panic("failed block size header check"); + } + + pagesize_mask = pagesize - 1; + + size_t preamble_size = (sizeof(uint32) * numpages) + sizeof(header); + data_start = 0; + while (data_start < preamble_size) data_start += pagesize; + + bool did_mmap = false; + +#ifdef _POSIX_MAPPED_FILES +// Try to memory map from the beginning of the file (0 is trivially a page multiple) + void * mmap_header = mmap(NULL, preamble_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (mmap_header == MAP_FAILED) + { + BX_INFO(("failed to mmap sparse disk file - using conventional file access")); + mmap_header = NULL; + } + else + { + mmap_length = preamble_size; + did_mmap = true; + pagetable = ((uint32 *) (((uint8 *) mmap_header) + sizeof(header))); + +// system_pagesize = getpagesize(); + system_pagesize_mask = getpagesize() - 1; + } +#endif + + if (!did_mmap) + { + pagetable = new uint32[numpages]; + + if (pagetable == NULL) + { + panic("could not allocate memory for sparse disk block table"); + } + + ret = ::read(fd, pagetable, sizeof(uint32) * numpages); + + if (-1 == ret) + { + panic(strerror(errno)); + } + + if ((int)(sizeof(uint32) * numpages) != ret) + { + panic("could not read entire block table"); + } + } +} + +int sparse_image_t::open (const char* pathname0) +{ + pathname = strdup(pathname0); + BX_DEBUG(("sparse_image_t.open")); + + fd = ::open(pathname, O_RDWR +#ifdef O_BINARY + | O_BINARY +#endif + ); + + if (fd < 0) + { + // open failed. + return -1; + } + BX_DEBUG(("sparse_image: open image %s", pathname)); + + read_header(); + + struct stat stat_buf; + if (0 != fstat(fd, &stat_buf)) panic(("fstat() returns error!")); + + underlying_filesize = stat_buf.st_size; + + if ((underlying_filesize % pagesize) != 0) + panic("size of sparse disk image is not multiple of page size"); + + underlying_current_filepos = 0; + if (-1 == ::lseek(fd, 0, SEEK_SET)) + panic("error while seeking to start of file"); + + lseek(0, SEEK_SET); + + //showpagetable(pagetable, header.numpages); + + char * parentpathname = strdup(pathname); + char lastchar = ::increment_string(parentpathname, -1); + + if ((lastchar >= '0') && (lastchar <= '9')) + { + struct stat stat_buf; + if (0 == stat(parentpathname, &stat_buf)) + { + parent_image = new sparse_image_t(); + int ret = parent_image->open(parentpathname); + if (ret != 0) return ret; + if ( (parent_image->pagesize != pagesize) + || (parent_image->total_size != total_size)) + { + panic("child drive image does not have same page count/page size configuration"); + } + } + } + + if (parentpathname != NULL) free(parentpathname); + + return 0; // success. +} + +void sparse_image_t::close () +{ + BX_DEBUG(("concat_image_t.close")); + if (pathname != NULL) + { + free(pathname); + } +#ifdef _POSIX_MAPPED_FILES + if (mmap_header != NULL) + { + int ret = munmap(mmap_header, mmap_length); + if (ret != 0) + BX_INFO(("failed to un-memory map sparse disk file")); + } + pagetable = NULL; // We didn't malloc it +#endif + if (fd > -1) { + ::close(fd); + } + if (pagetable != NULL) + { + delete [] pagetable; + } + if (parent_image != NULL) + { + delete parent_image; + } +} + +off_t sparse_image_t::lseek (off_t offset, int whence) +{ + //showpagetable(pagetable, header.numpages); + + if ((offset % 512) != 0) + BX_PANIC( ("lseek HD with offset not multiple of 512")); + if (whence != SEEK_SET) + BX_PANIC( ("lseek HD with whence not SEEK_SET")); + + BX_DEBUG(("sparse_image_t.lseek(%d)", whence)); + + if (offset > total_size) + { + BX_PANIC(("sparse_image_t.lseek to byte %ld failed", (long)offset)); + return -1; + } + + //printf("Seeking to position %ld\n", (long) offset); + + set_virtual_page(offset >> pagesize_shift); + position_page_offset = offset & pagesize_mask; + + return 0; +} + +inline off_t sparse_image_t::get_physical_offset() +{ + off_t physical_offset = data_start; + physical_offset += (position_physical_page << pagesize_shift); + physical_offset += position_page_offset; + + return physical_offset; +} + +inline void sparse_image_t::set_virtual_page(uint32 new_virtual_page) +{ + position_virtual_page = new_virtual_page; + + position_physical_page = dtoh32(pagetable[position_virtual_page]); +} + +ssize_t sparse_image_t::read_page_fragment(uint32 read_virtual_page, uint32 read_page_offset, size_t read_size, void * buf) +{ + if (read_virtual_page != position_virtual_page) + { + set_virtual_page(read_virtual_page); + } + + position_page_offset = read_page_offset; + + if (position_physical_page == SPARSE_PAGE_NOT_ALLOCATED) + { + if (parent_image != NULL) + { + return parent_image->read_page_fragment(read_virtual_page, read_page_offset, read_size, buf); + } + else + { + memset(buf, read_size, 0); + } + } + else + { + off_t physical_offset = get_physical_offset(); + + if (physical_offset != underlying_current_filepos) + { + int ret = ::lseek(fd, physical_offset, SEEK_SET); + // underlying_current_filepos update deferred + if (ret == -1) + panic(strerror(errno)); + } + + //printf("Reading %s at position %ld size %d\n", pathname, (long) physical_offset, (long) read_size); + ssize_t readret = ::read(fd, buf, read_size); + + if (readret == -1) + { + panic(strerror(errno)); + } + + if ((size_t)readret != read_size) + { + panic("could not read block contents from file"); + } + + underlying_current_filepos = physical_offset + read_size; + } + + return read_size; +} + +ssize_t sparse_image_t::read(void* buf, size_t count) +{ + //showpagetable(pagetable, header.numpages); + ssize_t total_read = 0; + + if (bx_dbg.disk) + BX_DEBUG(("sparse_image_t.read %ld bytes", (long)count)); + + while (count != 0) + { + size_t can_read = pagesize - position_page_offset; + if (count < can_read) can_read = count; + + BX_ASSERT (can_read != 0); + + size_t was_read = read_page_fragment(position_virtual_page, position_page_offset, can_read, buf); + + BX_ASSERT(was_read == can_read); + + total_read += can_read; + + position_page_offset += can_read; + if (position_page_offset == pagesize) + { + position_page_offset = 0; + set_virtual_page(position_virtual_page + 1); + } + + BX_ASSERT(position_page_offset < pagesize); + + buf = (((uint8 *) buf) + can_read); + count -= can_read; + } + + return total_read; +} + +void sparse_image_t::panic(const char * message) +{ + char buffer[1024]; + if (message == NULL) + { + snprintf(buffer, sizeof(buffer), "error with sparse disk image %s", pathname); + } + else + { + snprintf(buffer, sizeof(buffer), "error with sparse disk image %s - %s", pathname, message); + } + BX_PANIC((buffer)); +} + +ssize_t sparse_image_t::write (const void* buf, size_t count) +{ + //showpagetable(pagetable, header.numpages); + + ssize_t total_written = 0; + + uint32 update_pagetable_start = position_virtual_page; + uint32 update_pagetable_count = 0; + + if (bx_dbg.disk) + BX_DEBUG(("sparse_image_t.write %ld bytes", (long)count)); + + while (count != 0) + { + size_t can_write = pagesize - position_page_offset; + if (count < can_write) can_write = count; + + BX_ASSERT (can_write != 0); + + if (position_physical_page == SPARSE_PAGE_NOT_ALLOCATED) + { + // We just add on another page at the end of the file + // Reclamation, compaction etc should currently be done off-line + + size_t data_size = underlying_filesize - data_start; + BX_ASSERT((data_size % pagesize) == 0); + + + uint32 data_size_pages = data_size / pagesize; + uint32 next_data_page = data_size_pages; + + pagetable[position_virtual_page] = htod32(next_data_page); + position_physical_page = next_data_page; + + off_t page_file_start = data_start + (position_physical_page << pagesize_shift); + + if (parent_image != NULL) + { + // If we have a parent, we must merge our portion with the parent + void * writebuffer = NULL; + + if (can_write == pagesize) + { + writebuffer = (void *) buf; + } + else + { + writebuffer = malloc(pagesize); + if (writebuffer == NULL) + panic("Cannot allocate sufficient memory for page-merge in write"); + + // Read entire page - could optimize, but simple for now + parent_image->read_page_fragment(position_virtual_page, 0, pagesize, writebuffer); + + void * dest_start = ((uint8 *) writebuffer) + position_page_offset; + memcpy(dest_start, buf, can_write); + } + + int ret; + ret = ::lseek(fd, page_file_start, SEEK_SET); + // underlying_current_filepos update deferred + if (-1 == ret) panic(strerror(errno)); + + ret = ::write(fd, writebuffer, pagesize); + + if (-1 == ret) panic(strerror(errno)); + + if (pagesize != (uint32)ret) panic("failed to write entire merged page to disk"); + + if (can_write != pagesize) + { + free(writebuffer); + } + } + else + { + // We need to write a zero page because read has been returning zeroes + // We seek as close to the page end as possible, and then write a little + // This produces a sparse file which has blanks + // Also very quick, even when pagesize is massive + int ret; + ret = ::lseek(fd, page_file_start + pagesize - 4, SEEK_SET); + // underlying_current_filepos update deferred + if (-1 == ret) panic(strerror(errno)); + + uint32 zero = 0; + ret = ::write(fd, &zero, 4); + + if (-1 == ret) panic(strerror(errno)); + + if (4 != ret) panic("failed to write entire blank page to disk"); + } + + update_pagetable_count = (position_virtual_page - update_pagetable_start) + 1; + underlying_filesize = underlying_current_filepos = page_file_start + pagesize; + } + + BX_ASSERT(position_physical_page != SPARSE_PAGE_NOT_ALLOCATED); + + off_t physical_offset = get_physical_offset(); + + if (physical_offset != underlying_current_filepos) + { + int ret = ::lseek(fd, physical_offset, SEEK_SET); + // underlying_current_filepos update deferred + if (ret == -1) + panic(strerror(errno)); + } + + //printf("Writing at position %ld size %d\n", (long) physical_offset, can_write); + ssize_t writeret = ::write(fd, buf, can_write); + + if (writeret == -1) + { + panic(strerror(errno)); + } + + if ((size_t)writeret != can_write) + { + panic("could not write block contents to file"); + } + + underlying_current_filepos = physical_offset + can_write; + + total_written += can_write; + + position_page_offset += can_write; + if (position_page_offset == pagesize) + { + position_page_offset = 0; + set_virtual_page(position_virtual_page + 1); + } + + BX_ASSERT(position_page_offset < pagesize); + + buf = (((uint8 *) buf) + can_write); + count -= can_write; + } + + if (update_pagetable_count != 0) + { + bool done = false; + off_t pagetable_write_from = sizeof(header) + (sizeof(uint32) * update_pagetable_start); + size_t write_bytecount = update_pagetable_count * sizeof(uint32); + +#ifdef _POSIX_MAPPED_FILES + if (mmap_header != NULL) + { + // Sync from the beginning of the page + size_t system_page_offset = pagetable_write_from & system_pagesize_mask; + void * start = ((uint8 *) mmap_header + pagetable_write_from - system_page_offset); + + int ret = msync(start, system_page_offset + write_bytecount, MS_ASYNC); + + if (ret != 0) + panic(strerror(errno)); + + done = true; + } +#endif + + if (!done) + { + int ret = ::lseek(fd, pagetable_write_from, SEEK_SET); + // underlying_current_filepos update deferred + if (ret == -1) panic(strerror(errno)); + + //printf("Writing header at position %ld size %ld\n", (long) pagetable_write_from, (long) write_bytecount); + ret = ::write(fd, &pagetable[update_pagetable_start], write_bytecount); + if (ret == -1) panic(strerror(errno)); + if ((size_t)ret != write_bytecount) panic("could not write entire updated block header"); + + underlying_current_filepos = pagetable_write_from + write_bytecount; + } + } + + return total_written; +} + +#if DLL_HD_SUPPORT +/*** dll_image_t function definitions ***/ + +/* +function vdisk_open(path:PChar;numclusters,clustersize:integer):integer; +procedure vdisk_read(vunit:integer;blk:integer;var buf:TBlock); +procedure vdisk_write(vunit:integer;blk:integer;var buf:TBlock); +procedure vdisk_close(vunit:integer); +*/ + +HINSTANCE hlib_vdisk = 0; + +int (*vdisk_open) (const char *path,int numclusters,int clustersize); +void (*vdisk_read) (int vunit,int blk,void *buf); +void (*vdisk_write) (int vunit,int blk,const void *buf); +void (*vdisk_close) (int vunit); + +int dll_image_t::open (const char* pathname) +{ + if (hlib_vdisk == 0) { + hlib_vdisk = LoadLibrary("vdisk.dll"); + if (hlib_vdisk != 0) { + vdisk_read = (void (*)(int,int,void*)) GetProcAddress(hlib_vdisk,"vdisk_read"); + vdisk_write = (void (*)(int,int,const void*)) GetProcAddress(hlib_vdisk,"vdisk_write"); + vdisk_open = (int (*)(const char *,int,int)) GetProcAddress(hlib_vdisk,"vdisk_open"); + vdisk_close = (void (*)(int)) GetProcAddress(hlib_vdisk,"vdisk_close"); + } + } + if (hlib_vdisk != 0) { + vunit = vdisk_open(pathname,0x10000,64); + vblk = 0; + } else { + vunit = -2; + } + return vunit; +} + +void dll_image_t::close () +{ + if (vunit >= 0 && hlib_vdisk != 0) { + vdisk_close(vunit); + } +} + +off_t dll_image_t::lseek (off_t offset, int whence) +{ + vblk = offset >> 9; + return 0; +} + +ssize_t dll_image_t::read (void* buf, size_t count) +{ + if (vunit >= 0 && hlib_vdisk != 0) { + vdisk_read(vunit,vblk,buf); + return count; + } else { + return -1; + } +} + +ssize_t dll_image_t::write (const void* buf, size_t count) +{ + if (vunit >= 0 && hlib_vdisk != 0) { + vdisk_write(vunit,vblk,buf); + return count; + } else { + return -1; + } +} +#endif // DLL_HD_SUPPORT + +error_recovery_t::error_recovery_t () +{ + if (sizeof(error_recovery_t) != 8) { + BX_PANIC(("error_recovery_t has size != 8")); + } + + data[0] = 0x01; + data[1] = 0x06; + data[2] = 0x00; + data[3] = 0x05; // Try to recover 5 times + data[4] = 0x00; + data[5] = 0x00; + data[6] = 0x00; + data[7] = 0x00; +} + +uint16 BX_CPP_AttrRegparmN(1) +read_16bit(const uint8* buf) +{ + return (buf[0] << 8) | buf[1]; +} + +uint32 BX_CPP_AttrRegparmN(1) +read_32bit(const uint8* buf) +{ + return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; +} + +// redolog implementation +redolog_t::redolog_t () +{ + fd = -1; + catalog = NULL; + bitmap = NULL; + extent_index = (Bit32u)0; + extent_offset = (Bit32u)0; + extent_next = (Bit32u)0; +} + +void +redolog_t::print_header() +{ + BX_INFO(("redolog : Standard Header : magic='%s', type='%s', subtype='%s', version = %d.%d", + header.standard.magic, header.standard.type, header.standard.subtype, + dtoh32(header.standard.version)/0x10000, + dtoh32(header.standard.version)%0x10000)); + BX_INFO(("redolog : Specific Header : #entries=%d, bitmap size=%d, exent size = %d disk size = %lld", + dtoh32(header.specific.catalog), + dtoh32(header.specific.bitmap), + dtoh32(header.specific.extent), + dtoh64(header.specific.disk))); +} + +int +redolog_t::make_header (const char* type, Bit64u size) +{ + Bit32u entries, extent_size, bitmap_size; + Bit64u maxsize; + Bit32u flip=0; + + // Set standard header values + strcpy((char*)header.standard.magic, STANDARD_HEADER_MAGIC); + strcpy((char*)header.standard.type, REDOLOG_TYPE); + strcpy((char*)header.standard.subtype, type); + header.standard.version = htod32(STANDARD_HEADER_VERSION); + header.standard.header = htod32(STANDARD_HEADER_SIZE); + + entries = 512; + bitmap_size = 1; + + // Compute #entries and extent size values + do { + extent_size = 8 * bitmap_size * 512; + + header.specific.catalog = htod32(entries); + header.specific.bitmap = htod32(bitmap_size); + header.specific.extent = htod32(extent_size); + + maxsize = (Bit64u)entries * (Bit64u)extent_size; + + flip++; + + if(flip&0x01) bitmap_size *= 2; + else entries *= 2; + } while (maxsize < size); + + header.specific.disk = htod64(size); + + print_header(); + + catalog = (Bit32u*)malloc(dtoh32(header.specific.catalog) * sizeof(Bit32u)); + bitmap = (Bit8u*)malloc(dtoh32(header.specific.bitmap)); + + if ((catalog == NULL) || (bitmap==NULL)) + BX_PANIC(("redolog : could not malloc catalog or bitmap")); + + for (Bit32u i=0; i<dtoh32(header.specific.catalog); i++) + catalog[i] = htod32(REDOLOG_PAGE_NOT_ALLOCATED); + + bitmap_blocs = 1 + (dtoh32(header.specific.bitmap) - 1) / 512; + extent_blocs = 1 + (dtoh32(header.specific.extent) - 1) / 512; + + BX_DEBUG(("redolog : each bitmap is %d blocs", bitmap_blocs)); + BX_DEBUG(("redolog : each extent is %d blocs", extent_blocs)); + + return 0; +} + +int +redolog_t::create (const char* filename, const char* type, Bit64u size) +{ + int filedes; + + BX_INFO(("redolog : creating redolog %s", filename)); + + filedes = ::open(filename, O_RDWR | O_CREAT | O_TRUNC +#ifdef O_BINARY + | O_BINARY +#endif + , S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP); + + return create(filedes, type, size); +} + +int +redolog_t::create (int filedes, const char* type, Bit64u size) +{ + fd = filedes; + + if (fd < 0) + { + // open failed. + return -1; + } + + if (make_header(type, size) < 0) + { + return -1; + } + + // Write header + ::write(fd, &header, dtoh32(header.standard.header)); + + // Write catalog + // FIXME could mmap + ::write(fd, catalog, dtoh32(header.specific.catalog) * sizeof (Bit32u)); + + return 0; +} + +int +redolog_t::open (const char* filename, const char *type, Bit64u size) +{ + int res; + + fd = ::open(filename, O_RDWR +#ifdef O_BINARY + | O_BINARY +#endif + ); + if (fd < 0) + { + BX_INFO(("redolog : could not open image %s", filename)); + // open failed. + return -1; + } + BX_INFO(("redolog : open image %s", filename)); + + res = ::read(fd, &header, sizeof(header)); + if (res != STANDARD_HEADER_SIZE) + { + BX_PANIC(("redolog : could not read header")); + return -1; + } + + print_header(); + + if (strcmp((char*)header.standard.magic, STANDARD_HEADER_MAGIC) != 0) + { + BX_PANIC(("redolog : Bad header magic")); + return -1; + } + + if (strcmp((char*)header.standard.type, REDOLOG_TYPE) != 0) + { + BX_PANIC(("redolog : Bad header type")); + return -1; + } + if (strcmp((char*)header.standard.subtype, type) != 0) + { + BX_PANIC(("redolog : Bad header subtype")); + return -1; + } + + if (dtoh32(header.standard.version) != STANDARD_HEADER_VERSION) + { + BX_PANIC(("redolog : Bad header version")); + return -1; + } + + catalog = (Bit32u*)malloc(dtoh32(header.specific.catalog) * sizeof(Bit32u)); + + // FIXME could mmap + ::lseek(fd,dtoh32(header.standard.header),SEEK_SET); + res = ::read(fd, catalog, dtoh32(header.specific.catalog) * sizeof(Bit32u)) ; + + if (res != (ssize_t)(dtoh32(header.specific.catalog) * sizeof(Bit32u))) + { + BX_PANIC(("redolog : could not read catalog %d=%d",res, dtoh32(header.specific.catalog))); + return -1; + } + + // check last used extent + extent_next = 0; + for (Bit32u i=0; i < dtoh32(header.specific.catalog); i++) + { + if (dtoh32(catalog[i]) != REDOLOG_PAGE_NOT_ALLOCATED) + { + if (dtoh32(catalog[i]) >= extent_next) + extent_next = dtoh32(catalog[i]) + 1; + } + } + BX_INFO(("redolog : next extent will be at index %d",extent_next)); + + // memory used for storing bitmaps + bitmap = (Bit8u *)malloc(dtoh32(header.specific.bitmap)); + + bitmap_blocs = 1 + (dtoh32(header.specific.bitmap) - 1) / 512; + extent_blocs = 1 + (dtoh32(header.specific.extent) - 1) / 512; + + BX_DEBUG(("redolog : each bitmap is %d blocs", bitmap_blocs)); + BX_DEBUG(("redolog : each extent is %d blocs", extent_blocs)); + + return 0; +} + +void +redolog_t::close () +{ + if (fd >= 0) + ::close(fd); + + if (catalog != NULL) + free(catalog); + + if (bitmap != NULL) + free(bitmap); +} + +off_t +redolog_t::lseek (off_t offset, int whence) +{ + if ((offset % 512) != 0) { + BX_PANIC( ("redolog : lseek HD with offset not multiple of 512")); + return -1; + } + if (whence != SEEK_SET) { + BX_PANIC( ("redolog : lseek HD with whence not SEEK_SET")); + return -1; + } + if (offset > (off_t)dtoh64(header.specific.disk)) + { + BX_PANIC(("redolog : lseek to byte %ld failed", (long)offset)); + return -1; + } + + extent_index = offset / dtoh32(header.specific.extent); + extent_offset = (offset % dtoh32(header.specific.extent)) / 512; + + BX_DEBUG(("redolog : lseeking extent index %d, offset %d",extent_index, extent_offset)); + + return offset; +} + +ssize_t +redolog_t::read (void* buf, size_t count) +{ + off_t bloc_offset, bitmap_offset; + + if (count != 512) + BX_PANIC( ("redolog : read HD with count not 512")); + + BX_DEBUG(("redolog : reading index %d, mapping to %d", extent_index, dtoh32(catalog[extent_index]))); + + if (dtoh32(catalog[extent_index]) == REDOLOG_PAGE_NOT_ALLOCATED) + { + // page not allocated + return 0; + } + + bitmap_offset = (off_t)STANDARD_HEADER_SIZE + (dtoh32(header.specific.catalog) * sizeof(Bit32u)); + bitmap_offset += (off_t)512 * dtoh32(catalog[extent_index]) * (extent_blocs + bitmap_blocs); + bloc_offset = bitmap_offset + ((off_t)512 * (bitmap_blocs + extent_offset)); + + BX_DEBUG(("redolog : bitmap offset is %x", (Bit32u)bitmap_offset)); + BX_DEBUG(("redolog : bloc offset is %x", (Bit32u)bloc_offset)); + + + // FIXME if same extent_index as before we can skip bitmap read + + ::lseek(fd, bitmap_offset, SEEK_SET); + + if (::read(fd, bitmap, dtoh32(header.specific.bitmap)) != (ssize_t)dtoh32(header.specific.bitmap)) + { + BX_PANIC(("redolog : failed to read bitmap for extent %d", extent_index)); + return 0; + } + + if ( ((bitmap[extent_offset/8] >> (extent_offset%8)) & 0x01) == 0x00 ) + { + BX_DEBUG(("read not in redolog")); + + // bitmap says bloc not in reloglog + return 0; + } + + ::lseek(fd, bloc_offset, SEEK_SET); + + return (::read(fd, buf, count)); +} + +ssize_t +redolog_t::write (const void* buf, size_t count) +{ + Bit32u i; + off_t bloc_offset, bitmap_offset, catalog_offset; + ssize_t written; + bx_bool update_catalog = 0; + + if (count != 512) + BX_PANIC( ("redolog : write HD with count not 512")); + + BX_DEBUG(("redolog : writing index %d, mapping to %d", extent_index, dtoh32(catalog[extent_index]))); + if (dtoh32(catalog[extent_index]) == REDOLOG_PAGE_NOT_ALLOCATED) + { + if(extent_next >= dtoh32(header.specific.catalog)) + { + BX_PANIC(("redolog : can't allocate new extent... catalog is full")); + return 0; + } + + BX_DEBUG(("redolog : allocating new extent at %d", extent_next)); + + // Extent not allocated, allocate new + catalog[extent_index] = htod32(extent_next); + + extent_next += 1; + + char *zerobuffer = (char*)malloc(512); + memset(zerobuffer, 0, 512); + + // Write bitmap + bitmap_offset = (off_t)STANDARD_HEADER_SIZE + (dtoh32(header.specific.catalog) * sizeof(Bit32u)); + bitmap_offset += (off_t)512 * dtoh32(catalog[extent_index]) * (extent_blocs + bitmap_blocs); + ::lseek(fd, bitmap_offset, SEEK_SET); + for(i=0; i<bitmap_blocs; i++) + { + ::write(fd, zerobuffer, 512); + } + // Write extent + for(i=0; i<extent_blocs; i++) + { + ::write(fd, zerobuffer, 512); + } + + free(zerobuffer); + + update_catalog = 1; + } + + bitmap_offset = (off_t)STANDARD_HEADER_SIZE + (dtoh32(header.specific.catalog) * sizeof(Bit32u)); + bitmap_offset += (off_t)512 * dtoh32(catalog[extent_index]) * (extent_blocs + bitmap_blocs); + bloc_offset = bitmap_offset + ((off_t)512 * (bitmap_blocs + extent_offset)); + + BX_DEBUG(("redolog : bitmap offset is %x", (Bit32u)bitmap_offset)); + BX_DEBUG(("redolog : bloc offset is %x", (Bit32u)bloc_offset)); + + // Write bloc + ::lseek(fd, bloc_offset, SEEK_SET); + written = ::write(fd, buf, count); + + // Write bitmap + // FIXME if same extent_index as before we can skip bitmap read + ::lseek(fd, bitmap_offset, SEEK_SET); + if (::read(fd, bitmap, dtoh32(header.specific.bitmap)) != (ssize_t)dtoh32(header.specific.bitmap)) + { + BX_PANIC(("redolog : failed to read bitmap for extent %d", extent_index)); + return 0; + } + + // If bloc does not belong to extent yet + if ( ((bitmap[extent_offset/8] >> (extent_offset%8)) & 0x01) == 0x00 ) + { + bitmap[extent_offset/8] |= 1 << (extent_offset%8); + ::lseek(fd, bitmap_offset, SEEK_SET); + ::write(fd, bitmap, dtoh32(header.specific.bitmap)); + } + + // Write catalog + if (update_catalog) + { + // FIXME if mmap + catalog_offset = (off_t)STANDARD_HEADER_SIZE + (extent_index * sizeof(Bit32u)); + + BX_DEBUG(("redolog : writing catalog at offset %x", (Bit32u)catalog_offset)); + + ::lseek(fd, catalog_offset, SEEK_SET); + ::write(fd, &catalog[extent_index], sizeof(Bit32u)); + } + + return written; +} + + +/*** growing_image_t function definitions ***/ + +growing_image_t::growing_image_t(Bit64u _size) +{ + redolog = new redolog_t(); + size = _size; +} + +int growing_image_t::open (const char* pathname) +{ + int filedes = redolog->open(pathname,REDOLOG_SUBTYPE_GROWING,size); + BX_INFO(("'growing' disk opened, growing file is '%s'", pathname)); + return filedes; +} + +void growing_image_t::close () +{ + redolog->close(); +} + +off_t growing_image_t::lseek (off_t offset, int whence) +{ + return redolog->lseek(offset, whence); +} + +ssize_t growing_image_t::read (void* buf, size_t count) +{ + memset(buf, 0, count); + redolog->read((char*) buf, count); + return count; +} + +ssize_t growing_image_t::write (const void* buf, size_t count) +{ + return redolog->write((char*) buf, count); +} + + +/*** undoable_image_t function definitions ***/ + +undoable_image_t::undoable_image_t(Bit64u _size, const char* _redolog_name) +{ + redolog = new redolog_t(); + ro_disk = new default_image_t(); + size = _size; + redolog_name = NULL; + if (_redolog_name != NULL) { + if (strcmp(_redolog_name,"") != 0) { + redolog_name = strdup(_redolog_name); + } + } +} + +int undoable_image_t::open (const char* pathname) +{ + char *logname=NULL; + + if (ro_disk->open(pathname, O_RDONLY)<0) + return -1; + + // if redolog name was set + if ( redolog_name != NULL) { + if ( strcmp(redolog_name, "") != 0 ) { + logname = (char*)malloc(strlen(redolog_name) + 1); + strcpy (logname, redolog_name); + } + } + + // Otherwise we make up the redolog filename from the pathname + if ( logname == NULL) { + logname = (char*)malloc(strlen(pathname) + UNDOABLE_REDOLOG_EXTENSION_LENGTH + 1); + sprintf (logname, "%s%s", pathname, UNDOABLE_REDOLOG_EXTENSION); + } + + if (redolog->open(logname,REDOLOG_SUBTYPE_UNDOABLE,size) < 0) + { + if (redolog->create(logname, REDOLOG_SUBTYPE_UNDOABLE, size) < 0) + { + BX_PANIC(("Can't open or create redolog '%s'",logname)); + return -1; + } + } + + BX_INFO(("'undoable' disk opened: ro-file is '%s', redolog is '%s'", pathname, logname)); + free(logname); + + return 0; +} + +void undoable_image_t::close () +{ + redolog->close(); + ro_disk->close(); + + if (redolog_name!=NULL) + free(redolog_name); +} + +off_t undoable_image_t::lseek (off_t offset, int whence) +{ + redolog->lseek(offset, whence); + return ro_disk->lseek(offset, whence); +} + +ssize_t undoable_image_t::read (void* buf, size_t count) +{ + // This should be fixed if count != 512 + if ((size_t)redolog->read((char*) buf, count) != count) + return ro_disk->read((char*) buf, count); + else + return count; +} + +ssize_t undoable_image_t::write (const void* buf, size_t count) +{ + return redolog->write((char*) buf, count); +} + + +/*** volatile_image_t function definitions ***/ + +volatile_image_t::volatile_image_t(Bit64u _size, const char* _redolog_name) +{ + redolog = new redolog_t(); + ro_disk = new default_image_t(); + size = _size; + redolog_temp = NULL; + redolog_name = NULL; + if (_redolog_name != NULL) { + if (strcmp(_redolog_name,"") != 0) { + redolog_name = strdup(_redolog_name); + } + } +} + +int volatile_image_t::open (const char* pathname) +{ + int filedes; + const char *logname=NULL; + + if (ro_disk->open(pathname, O_RDONLY)<0) + return -1; + + // if redolog name was set + if ( redolog_name != NULL) { + if ( strcmp(redolog_name, "") != 0 ) { + logname = redolog_name; + } + } + + // otherwise use pathname as template + if (logname == NULL) { + logname = pathname; + } + + redolog_temp = (char*)malloc(strlen(logname) + VOLATILE_REDOLOG_EXTENSION_LENGTH + 1); + sprintf (redolog_temp, "%s%s", logname, VOLATILE_REDOLOG_EXTENSION); + + filedes = mkstemp (redolog_temp); + + if (filedes < 0) + { + BX_PANIC(("Can't create volatile redolog '%s'", redolog_temp)); + return -1; + } + if (redolog->create(filedes, REDOLOG_SUBTYPE_VOLATILE, size) < 0) + { + BX_PANIC(("Can't create volatile redolog '%s'", redolog_temp)); + return -1; + } + +#if (!defined(WIN32)) && !BX_WITH_MACOS + // on unix it is legal to delete an open file + unlink(redolog_temp); +#endif + + BX_INFO(("'volatile' disk opened: ro-file is '%s', redolog is '%s'", pathname, redolog_temp)); + + return 0; +} + +void volatile_image_t::close () +{ + redolog->close(); + ro_disk->close(); + +#if defined(WIN32) || BX_WITH_MACOS + // on non-unix we have to wait till the file is closed to delete it + unlink(redolog_temp); +#endif + if (redolog_temp!=NULL) + free(redolog_temp); + + if (redolog_name!=NULL) + free(redolog_name); +} + +off_t volatile_image_t::lseek (off_t offset, int whence) +{ + redolog->lseek(offset, whence); + return ro_disk->lseek(offset, whence); +} + +ssize_t volatile_image_t::read (void* buf, size_t count) +{ + // This should be fixed if count != 512 + if ((size_t)redolog->read((char*) buf, count) != count) + return ro_disk->read((char*) buf, count); + else + return count; +} + +ssize_t volatile_image_t::write (const void* buf, size_t count) +{ + return redolog->write((char*) buf, count); +} + +#if BX_COMPRESSED_HD_SUPPORT + +/*** z_ro_image_t function definitions ***/ + +z_ro_image_t::z_ro_image_t() +{ + offset = (off_t)0; +} + +int z_ro_image_t::open (const char* pathname) +{ + fd = ::open(pathname, O_RDONLY +#ifdef O_BINARY + | O_BINARY +#endif + ); + + if(fd < 0) + { + BX_PANIC(("Could not open '%s' file", pathname)); + return fd; + } + + gzfile = gzdopen(fd, "rb"); +} + +void z_ro_image_t::close () +{ + if (fd > -1) { + gzclose(gzfile); + // ::close(fd); + } +} + +off_t z_ro_image_t::lseek (off_t _offset, int whence) +{ + // Only SEEK_SET supported + if (whence != SEEK_SET) + { + BX_PANIC(("lseek on compressed images : only SEEK_SET supported")); + } + + // Seeking is expensive on compressed files, so we do it + // only when necessary, at the latest moment + offset = _offset; + + return offset; +} + +ssize_t z_ro_image_t::read (void* buf, size_t count) +{ + gzseek(gzfile, offset, SEEK_SET); + return gzread(gzfile, buf, count); +} + +ssize_t z_ro_image_t::write (const void* buf, size_t count) +{ + BX_PANIC(("z_ro_image: write not supported")); + return 0; +} + + +/*** z_undoable_image_t function definitions ***/ + +z_undoable_image_t::z_undoable_image_t(Bit64u _size, const char* _redolog_name) +{ + redolog = new redolog_t(); + ro_disk = new z_ro_image_t(); + size = _size; + + redolog_name = NULL; + if (_redolog_name != NULL) { + if (strcmp(_redolog_name,"") != 0) { + redolog_name = strdup(_redolog_name); + } + } +} + +int z_undoable_image_t::open (const char* pathname) +{ + char *logname=NULL; + + if (ro_disk->open(pathname)<0) + return -1; + + // If redolog name was set + if ( redolog_name != NULL) { + if ( strcmp(redolog_name, "") != 0) { + logname = (char*)malloc(strlen(redolog_name) + 1); + strcpy (logname, redolog_name); + } + } + + // Otherwise we make up the redolog filename from the pathname + if ( logname == NULL) { + logname = (char*)malloc(strlen(pathname) + UNDOABLE_REDOLOG_EXTENSION_LENGTH + 1); + sprintf (logname, "%s%s", pathname, UNDOABLE_REDOLOG_EXTENSION); + } + + if (redolog->open(logname,REDOLOG_SUBTYPE_UNDOABLE,size) < 0) + { + if (redolog->create(logname, REDOLOG_SUBTYPE_UNDOABLE, size) < 0) + { + BX_PANIC(("Can't open or create redolog '%s'",logname)); + return -1; + } + } + + BX_INFO(("'z-undoable' disk opened, z-ro-file is '%s', redolog is '%s'", pathname, logname)); + free(logname); + + return 0; +} + +void z_undoable_image_t::close () +{ + redolog->close(); + ro_disk->close(); + + if (redolog_name!=NULL) + free(redolog_name); +} + +off_t z_undoable_image_t::lseek (off_t offset, int whence) +{ + redolog->lseek(offset, whence); + return ro_disk->lseek(offset, whence); +} + +ssize_t z_undoable_image_t::read (void* buf, size_t count) +{ + // This should be fixed if count != 512 + if (redolog->read((char*) buf, count) != count) + return ro_disk->read((char*) buf, count); + else + return count; +} + +ssize_t z_undoable_image_t::write (const void* buf, size_t count) +{ + return redolog->write((char*) buf, count); +} + + +/*** z_volatile_image_t function definitions ***/ + +z_volatile_image_t::z_volatile_image_t(Bit64u _size, const char* _redolog_name) +{ + redolog = new redolog_t(); + ro_disk = new z_ro_image_t(); + size = _size; + + redolog_temp = NULL; + redolog_name = NULL; + if (_redolog_name != NULL) { + if (strcmp(_redolog_name,"") != 0) { + redolog_name = strdup(_redolog_name); + } + } +} + +int z_volatile_image_t::open (const char* pathname) +{ + int filedes; + const char *logname=NULL; + + if (ro_disk->open(pathname)<0) + return -1; + + // if redolog name was set + if ( redolog_name != NULL) { + if ( strcmp(redolog_name, "") !=0 ) { + logname = redolog_name; + } + } + + // otherwise use pathname as template + if (logname == NULL) { + logname = pathname; + } + + redolog_temp = (char*)malloc(strlen(logname) + VOLATILE_REDOLOG_EXTENSION_LENGTH + 1); + sprintf (redolog_temp, "%s%s", logname, VOLATILE_REDOLOG_EXTENSION); + + filedes = mkstemp (redolog_temp); + + if (filedes < 0) + { + BX_PANIC(("Can't create volatile redolog '%s'", redolog_temp)); + return -1; + } + if (redolog->create(filedes, REDOLOG_SUBTYPE_VOLATILE, size) < 0) + { + BX_PANIC(("Can't create volatile redolog '%s'", redolog_temp)); + return -1; + } + +#if (!defined(WIN32)) && !BX_WITH_MACOS + // on unix it is legal to delete an open file + unlink(redolog_temp); +#endif + + BX_INFO(("'z-volatile' disk opened: z-ro-file is '%s', redolog is '%s'", pathname, redolog_temp)); + + return 0; +} + +void z_volatile_image_t::close () +{ + redolog->close(); + ro_disk->close(); + +#if defined(WIN32) || BX_WITH_MACOS + // on non-unix we have to wait till the file is closed to delete it + unlink(redolog_temp); +#endif + + if (redolog_temp!=NULL) + free(redolog_temp); + + if (redolog_name!=NULL) + free(redolog_name); +} + +off_t z_volatile_image_t::lseek (off_t offset, int whence) +{ + redolog->lseek(offset, whence); + return ro_disk->lseek(offset, whence); +} + +ssize_t z_volatile_image_t::read (void* buf, size_t count) +{ + // This should be fixed if count != 512 + if (redolog->read((char*) buf, count) != count) + return ro_disk->read((char*) buf, count); + else + return count; +} + +ssize_t z_volatile_image_t::write (const void* buf, size_t count) +{ + return redolog->write((char*) buf, count); +} + + +#endif diff --git a/tools/ioemu/iodev/harddrv.h b/tools/ioemu/iodev/harddrv.h new file mode 100644 index 0000000000..9320e614d5 --- /dev/null +++ b/tools/ioemu/iodev/harddrv.h @@ -0,0 +1,765 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: harddrv.h,v 1.22.2.1 2004/02/06 22:14:36 danielg4 Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + +// SPARSE IMAGES HEADER +#define SPARSE_HEADER_MAGIC (0x02468ace) +#define SPARSE_HEADER_VERSION 1 +#define SPARSE_HEADER_SIZE (256) // Plenty of room for later +#define SPARSE_PAGE_NOT_ALLOCATED (0xffffffff) + + typedef struct + { + uint32 magic; + uint32 version; + uint32 pagesize; + uint32 numpages; + + uint32 padding[60]; + } sparse_header_t; + +#define STANDARD_HEADER_MAGIC "Bochs Virtual HD Image" +#define STANDARD_HEADER_VERSION (0x00010000) +#define STANDARD_HEADER_SIZE (512) + + + // WARNING : headers are kept in x86 (little) endianness + typedef struct + { + Bit8u magic[32]; + Bit8u type[16]; + Bit8u subtype[16]; + Bit32u version; + Bit32u header; + } standard_header_t; + +#define REDOLOG_TYPE "Redolog" +#define REDOLOG_SUBTYPE_UNDOABLE "Undoable" +#define REDOLOG_SUBTYPE_VOLATILE "Volatile" +#define REDOLOG_SUBTYPE_GROWING "Growing" +// #define REDOLOG_SUBTYPE_Z_UNDOABLE "z-Undoable" +// #define REDOLOG_SUBTYPE_Z_VOLATILE "z-Volatile" + +#define REDOLOG_PAGE_NOT_ALLOCATED (0xffffffff) + +#define UNDOABLE_REDOLOG_EXTENSION ".redolog" +#define UNDOABLE_REDOLOG_EXTENSION_LENGTH (strlen(UNDOABLE_REDOLOG_EXTENSION)) +#define VOLATILE_REDOLOG_EXTENSION ".XXXXXX" +#define VOLATILE_REDOLOG_EXTENSION_LENGTH (strlen(VOLATILE_REDOLOG_EXTENSION)) + + typedef struct + { + // the fields in the header are kept in little endian + Bit32u catalog; // #entries in the catalog + Bit32u bitmap; // bitmap size in bytes + Bit32u extent; // extent size in bytes + Bit64u disk; // disk size in bytes + } redolog_specific_header_t; + + typedef struct + { + standard_header_t standard; + redolog_specific_header_t specific; + + Bit8u padding[STANDARD_HEADER_SIZE - (sizeof (standard_header_t) + sizeof (redolog_specific_header_t))]; + } redolog_header_t; + +// htod : convert host to disk (little) endianness +// dtoh : convert disk (little) to host endianness +#if defined (BX_LITTLE_ENDIAN) +#define htod32(val) (val) +#define dtoh32(val) (val) +#define htod64(val) (val) +#define dtoh64(val) (val) +#else +#define htod32(val) ( (((val)&0xff000000)>>24) | (((val)&0xff0000)>>8) | (((val)&0xff00)<<8) | (((val)&0xff)<<24) ) +#define dtoh32(val) htod32(val) +#define htod64(val) ( (((val)&0xff00000000000000LL)>>56) | (((val)&0xff000000000000LL)>>40) | (((val)&0xff0000000000LL)>>24) | (((val)&0xff00000000LL)>>8) | (((val)&0xff000000LL)<<8) | (((val)&0xff0000LL)<<24) | (((val)&0xff00LL)<<40) | (((val)&0xffLL)<<56) ) +#define dtoh64(val) htod64(val) +#endif + +#ifndef INCLUDE_ONLY_HD_HEADERS + +typedef enum _sense { + SENSE_NONE = 0, SENSE_NOT_READY = 2, SENSE_ILLEGAL_REQUEST = 5, + SENSE_UNIT_ATTENTION = 6 +} sense_t; + +typedef enum _asc { + ASC_INV_FIELD_IN_CMD_PACKET = 0x24, + ASC_MEDIUM_NOT_PRESENT = 0x3a, + ASC_SAVING_PARAMETERS_NOT_SUPPORTED = 0x39, + ASC_LOGICAL_BLOCK_OOR = 0x21 +} asc_t; + +class LOWLEVEL_CDROM; + +class device_image_t +{ + public: + // Open a image. Returns non-negative if successful. + virtual int open (const char* pathname) = 0; + + // Close the image. + virtual void close () = 0; + + // Position ourselves. Return the resulting offset from the + // beginning of the file. + virtual off_t lseek (off_t offset, int whence) = 0; + + // Read count bytes to the buffer buf. Return the number of + // bytes read (count). + virtual ssize_t read (void* buf, size_t count) = 0; + + // Write count bytes from buf. Return the number of bytes + // written (count). + virtual ssize_t write (const void* buf, size_t count) = 0; + + unsigned cylinders; + unsigned heads; + unsigned sectors; +}; + +// FLAT MODE +class default_image_t : public device_image_t +{ + public: + // Open a image. Returns non-negative if successful. + int open (const char* pathname); + + // Open an image with specific flags. Returns non-negative if successful. + int open (const char* pathname, int flags); + + // Close the image. + void close (); + + // Position ourselves. Return the resulting offset from the + // beginning of the file. + off_t lseek (off_t offset, int whence); + + // Read count bytes to the buffer buf. Return the number of + // bytes read (count). + ssize_t read (void* buf, size_t count); + + // Write count bytes from buf. Return the number of bytes + // written (count). + ssize_t write (const void* buf, size_t count); + + private: + int fd; + +}; + +// CONCAT MODE +class concat_image_t : public device_image_t +{ + public: + // Default constructor + concat_image_t(); + + // Open a image. Returns non-negative if successful. + int open (const char* pathname); + + // Close the image. + void close (); + + // Position ourselves. Return the resulting offset from the + // beginning of the file. + off_t lseek (off_t offset, int whence); + + // Read count bytes to the buffer buf. Return the number of + // bytes read (count). + ssize_t read (void* buf, size_t count); + + // Write count bytes from buf. Return the number of bytes + // written (count). + ssize_t write (const void* buf, size_t count); + + private: +#define BX_CONCAT_MAX_IMAGES 8 + int fd_table[BX_CONCAT_MAX_IMAGES]; + off_t start_offset_table[BX_CONCAT_MAX_IMAGES]; + off_t length_table[BX_CONCAT_MAX_IMAGES]; + void increment_string (char *str); + int maxfd; // number of entries in tables that are valid + + // notice if anyone does sequential read or write without seek in between. + // This can be supported pretty easily, but needs additional checks. + // 0=something other than seek was last operation + // 1=seek was last operation + int seek_was_last_op; + + // the following variables tell which partial image file to use for + // the next read and write. + int index; // index into table + int fd; // fd to use for reads and writes + off_t thismin, thismax; // byte offset boundary of this image +}; + +// SPARSE MODE +class sparse_image_t : public device_image_t +{ + +// Format of a sparse file: +// 256 byte header, containing details such as page size and number of pages +// Page indirection table, mapping virtual pages to physical pages within file +// Physical pages till end of file + + public: + // Default constructor + sparse_image_t(); + + // Open a image. Returns non-negative if successful. + int open (const char* pathname); + + // Close the image. + void close (); + + // Position ourselves. Return the resulting offset from the + // beginning of the file. + off_t lseek (off_t offset, int whence); + + // Read count bytes to the buffer buf. Return the number of + // bytes read (count). + ssize_t read (void* buf, size_t count); + + // Write count bytes from buf. Return the number of bytes + // written (count). + ssize_t write (const void* buf, size_t count); + + private: + int fd; + +#ifdef _POSIX_MAPPED_FILES + void * mmap_header; + size_t mmap_length; + size_t system_pagesize_mask; +#endif + uint32 * pagetable; + + // Header is written to disk in little-endian (x86) format + // Thus needs to be converted on big-endian systems before read + // The pagetable is also kept little endian + + sparse_header_t header; + + uint32 pagesize; + int pagesize_shift; + uint32 pagesize_mask; + + off_t data_start; + off_t underlying_filesize; + + char * pathname; + + off_t position; + + uint32 position_virtual_page; + uint32 position_physical_page; + uint32 position_page_offset; + + off_t underlying_current_filepos; + + off_t total_size; + + void panic(const char * message); + off_t +#ifndef PARANOID + sparse_image_t:: +#endif + get_physical_offset(); + void +#ifndef PARANOID + sparse_image_t:: +#endif + set_virtual_page(uint32 new_virtual_page); + void read_header(); + ssize_t read_page_fragment(uint32 read_virtual_page, uint32 read_page_offset, size_t read_size, void * buf); + + sparse_image_t * parent_image; +}; + +#if EXTERNAL_DISK_SIMULATOR +#include "external-disk-simulator.h" +#endif + +#if DLL_HD_SUPPORT +class dll_image_t : public device_image_t +{ + public: + // Open a image. Returns non-negative if successful. + int open (const char* pathname); + + // Close the image. + void close (); + + // Position ourselves. Return the resulting offset from the + // beginning of the file. + off_t lseek (off_t offset, int whence); + + // Read count bytes to the buffer buf. Return the number of + // bytes read (count). + ssize_t read (void* buf, size_t count); + + // Write count bytes from buf. Return the number of bytes + // written (count). + ssize_t write (const void* buf, size_t count); + + private: + int vunit,vblk; + +}; +#endif + +// REDOLOG class +class redolog_t +{ + public: + redolog_t(); + int make_header (const char* type, Bit64u size); + int create (const char* filename, const char* type, Bit64u size); + int create (int filedes, const char* type, Bit64u size); + int open (const char* filename, const char* type, Bit64u size); + void close (); + + off_t lseek (off_t offset, int whence); + ssize_t read (void* buf, size_t count); + ssize_t write (const void* buf, size_t count); + + private: + void print_header(); + int fd; + redolog_header_t header; // Header is kept in x86 (little) endianness + Bit32u *catalog; + Bit8u *bitmap; + Bit32u extent_index; + Bit32u extent_offset; + Bit32u extent_next; + + Bit32u bitmap_blocs; + Bit32u extent_blocs; +}; + +// GROWING MODE +class growing_image_t : public device_image_t +{ + public: + // Contructor + growing_image_t(Bit64u size); + + // Open a image. Returns non-negative if successful. + int open (const char* pathname); + + // Close the image. + void close (); + + // Position ourselves. Return the resulting offset from the + // beginning of the file. + off_t lseek (off_t offset, int whence); + + // Read count bytes to the buffer buf. Return the number of + // bytes read (count). + ssize_t read (void* buf, size_t count); + + // Write count bytes from buf. Return the number of bytes + // written (count). + ssize_t write (const void* buf, size_t count); + + private: + redolog_t *redolog; + Bit64u size; +}; + +// UNDOABLE MODE +class undoable_image_t : public device_image_t +{ + public: + // Contructor + undoable_image_t(Bit64u size, const char* redolog_name); + + // Open a image. Returns non-negative if successful. + int open (const char* pathname); + + // Close the image. + void close (); + + // Position ourselves. Return the resulting offset from the + // beginning of the file. + off_t lseek (off_t offset, int whence); + + // Read count bytes to the buffer buf. Return the number of + // bytes read (count). + ssize_t read (void* buf, size_t count); + + // Write count bytes from buf. Return the number of bytes + // written (count). + ssize_t write (const void* buf, size_t count); + + private: + redolog_t *redolog; // Redolog instance + default_image_t *ro_disk; // Read-only flat disk instance + Bit64u size; + char *redolog_name; // Redolog name +}; + + +// VOLATILE MODE +class volatile_image_t : public device_image_t +{ + public: + // Contructor + volatile_image_t(Bit64u size, const char* redolog_name); + + // Open a image. Returns non-negative if successful. + int open (const char* pathname); + + // Close the image. + void close (); + + // Position ourselves. Return the resulting offset from the + // beginning of the file. + off_t lseek (off_t offset, int whence); + + // Read count bytes to the buffer buf. Return the number of + // bytes read (count). + ssize_t read (void* buf, size_t count); + + // Write count bytes from buf. Return the number of bytes + // written (count). + ssize_t write (const void* buf, size_t count); + + private: + redolog_t *redolog; // Redolog instance + default_image_t *ro_disk; // Read-only flat disk instance + Bit64u size; + char *redolog_name; // Redolog name + char *redolog_temp; // Redolog temporary file name +}; + + +#if BX_COMPRESSED_HD_SUPPORT + +#include <zlib.h> + + +// Default compressed READ-ONLY image class +class z_ro_image_t : public device_image_t +{ + public: + // Contructor + z_ro_image_t(); + + // Open a image. Returns non-negative if successful. + int open (const char* pathname); + + // Close the image. + void close (); + + // Position ourselves. Return the resulting offset from the + // beginning of the file. + off_t lseek (off_t offset, int whence); + + // Read count bytes to the buffer buf. Return the number of + // bytes read (count). + ssize_t read (void* buf, size_t count); + + // Write count bytes from buf. Return the number of bytes + // written (count). + ssize_t write (const void* buf, size_t count); + + private: + off_t offset; + int fd; + gzFile gzfile; + +}; + +// Z-UNDOABLE MODE +class z_undoable_image_t : public device_image_t +{ + public: + // Contructor + z_undoable_image_t(Bit64u size, const char* redolog_name); + + // Open a image. Returns non-negative if successful. + int open (const char* pathname); + + // Close the image. + void close (); + + // Position ourselves. Return the resulting offset from the + // beginning of the file. + off_t lseek (off_t offset, int whence); + + // Read count bytes to the buffer buf. Return the number of + // bytes read (count). + ssize_t read (void* buf, size_t count); + + // Write count bytes from buf. Return the number of bytes + // written (count). + ssize_t write (const void* buf, size_t count); + + private: + redolog_t *redolog; // Redolog instance + z_ro_image_t *ro_disk; // Read-only compressed flat disk instance + Bit64u size; + char *redolog_name; // Redolog name +}; + +// Z-VOLATILE MODE +class z_volatile_image_t : public device_image_t +{ + public: + // Contructor + z_volatile_image_t(Bit64u size, const char* redolog_name); + + // Open a image. Returns non-negative if successful. + int open (const char* pathname); + + // Close the image. + void close (); + + // Position ourselves. Return the resulting offset from the + // beginning of the file. + off_t lseek (off_t offset, int whence); + + // Read count bytes to the buffer buf. Return the number of + // bytes read (count). + ssize_t read (void* buf, size_t count); + + // Write count bytes from buf. Return the number of bytes + // written (count). + ssize_t write (const void* buf, size_t count); + + private: + redolog_t *redolog; // Redolog instance + z_ro_image_t *ro_disk; // Read-only compressed flat disk instance + Bit64u size; + char *redolog_name; // Redolog name + char *redolog_temp; // Redolog temporary file name +}; + +#endif + + +typedef struct { + struct { + bx_bool busy; + bx_bool drive_ready; + bx_bool write_fault; + bx_bool seek_complete; + bx_bool drq; + bx_bool corrected_data; + bx_bool index_pulse; + unsigned index_pulse_count; + bx_bool err; + } status; + Bit8u error_register; + Bit8u head_no; + union { + Bit8u sector_count; + struct { +#ifdef BX_LITTLE_ENDIAN + unsigned c_d : 1; + unsigned i_o : 1; + unsigned rel : 1; + unsigned tag : 5; +#else /* BX_BIG_ENDIAN */ + unsigned tag : 5; + unsigned rel : 1; + unsigned i_o : 1; + unsigned c_d : 1; +#endif + } interrupt_reason; + }; + Bit8u sector_no; + union { + Bit16u cylinder_no; + Bit16u byte_count; + }; + Bit8u buffer[2048]; + Bit32u buffer_index; + Bit32u drq_index; + Bit8u current_command; + Bit8u sectors_per_block; + Bit8u lba_mode; + struct { + bx_bool reset; // 0=normal, 1=reset controller + bx_bool disable_irq; // 0=allow irq, 1=disable irq + } control; + Bit8u reset_in_progress; + Bit8u features; + } controller_t; + +struct sense_info_t { + sense_t sense_key; + struct { + Bit8u arr[4]; + } information; + struct { + Bit8u arr[4]; + } specific_inf; + struct { + Bit8u arr[3]; + } key_spec; + Bit8u fruc; + Bit8u asc; + Bit8u ascq; +}; + +struct error_recovery_t { + unsigned char data[8]; + + error_recovery_t (); +}; + +uint16 read_16bit(const uint8* buf) BX_CPP_AttrRegparmN(1); +uint32 read_32bit(const uint8* buf) BX_CPP_AttrRegparmN(1); + + +#ifdef LOWLEVEL_CDROM +# include "cdrom.h" +#endif + + +struct cdrom_t +{ + bx_bool ready; + bx_bool locked; +#ifdef LOWLEVEL_CDROM + LOWLEVEL_CDROM* cd; +#endif + uint32 capacity; + int next_lba; + int remaining_blocks; + struct currentStruct { + error_recovery_t error_recovery; + } current; +}; + +struct atapi_t +{ + uint8 command; + int drq_bytes; + int total_bytes_remaining; +}; + +#if BX_USE_HD_SMF +# define BX_HD_SMF static +# define BX_HD_THIS theHardDrive-> +#else +# define BX_HD_SMF +# define BX_HD_THIS this-> +#endif + +typedef enum { + IDE_NONE, IDE_DISK, IDE_CDROM +} device_type_t; + +class bx_hard_drive_c : public bx_hard_drive_stub_c { +public: + + bx_hard_drive_c(void); + virtual ~bx_hard_drive_c(void); + virtual void close_harddrive(void); + virtual void init(); + virtual void reset(unsigned type); + virtual Bit32u get_device_handle(Bit8u channel, Bit8u device); + virtual Bit32u get_first_cd_handle(void); + virtual unsigned get_cd_media_status(Bit32u handle); + virtual unsigned set_cd_media_status(Bit32u handle, unsigned status); + + virtual Bit32u virt_read_handler(Bit32u address, unsigned io_len) { + return read_handler (this, address, io_len); + } + virtual void virt_write_handler(Bit32u address, + Bit32u value, unsigned io_len) + { + write_handler(this, address, value, io_len); + } +#if !BX_USE_HD_SMF + Bit32u read(Bit32u address, unsigned io_len); + void write(Bit32u address, Bit32u value, unsigned io_len); +#endif + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); + +private: + + BX_HD_SMF bx_bool calculate_logical_address(Bit8u channel, off_t *sector) BX_CPP_AttrRegparmN(2); + BX_HD_SMF void increment_address(Bit8u channel) BX_CPP_AttrRegparmN(1); + BX_HD_SMF void identify_drive(Bit8u channel); + BX_HD_SMF void identify_ATAPI_drive(Bit8u channel); + BX_HD_SMF void command_aborted(Bit8u channel, unsigned command); + + BX_HD_SMF void init_send_atapi_command(Bit8u channel, Bit8u command, int req_length, int alloc_length, bool lazy = false) BX_CPP_AttrRegparmN(3); + BX_HD_SMF void ready_to_send_atapi(Bit8u channel) BX_CPP_AttrRegparmN(1); + BX_HD_SMF void raise_interrupt(Bit8u channel) BX_CPP_AttrRegparmN(1); + BX_HD_SMF void atapi_cmd_error(Bit8u channel, sense_t sense_key, asc_t asc); + BX_HD_SMF void init_mode_sense_single(Bit8u channel, const void* src, int size); + BX_HD_SMF void atapi_cmd_nop(Bit8u channel) BX_CPP_AttrRegparmN(1); + + // FIXME: + // For each ATA channel we should have one controller struct + // and an array of two drive structs + struct channel_t { + struct drive_t { + device_image_t* hard_drive; + device_type_t device_type; + // 512 byte buffer for ID drive command + // These words are stored in native word endian format, as + // they are fetched and returned via a return(), so + // there's no need to keep them in x86 endian format. + Bit16u id_drive[256]; + + controller_t controller; + cdrom_t cdrom; + sense_info_t sense; + atapi_t atapi; + + Bit8u model_no[41]; + } drives[2]; + unsigned drive_select; + + Bit16u ioaddr1; + Bit16u ioaddr2; + Bit8u irq; + + } channels[BX_MAX_ATA_CHANNEL]; + +#if BX_PDC20230C_VLBIDE_SUPPORT +// pdc20630c is only available for 1st ata channel + struct pdc20630c_t { + bx_bool prog_mode; + Bit8u prog_count; + Bit32u p1f3_value; + Bit32u p1f4_value; + } pdc20230c; +#endif + + }; +#endif // INCLUDE_ONLY_SPARSE_HEADER + diff --git a/tools/ioemu/iodev/ioapic.cc b/tools/ioemu/iodev/ioapic.cc new file mode 100644 index 0000000000..8aaf67f848 --- /dev/null +++ b/tools/ioemu/iodev/ioapic.cc @@ -0,0 +1,175 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: ioapic.cc,v 1.11 2002/11/19 05:47:45 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// +#include <stdio.h> +#include "bochs.h" +#if BX_SUPPORT_APIC + +class bx_ioapic_c bx_ioapic; +#define LOG_THIS bx_ioapic. + +void +bx_io_redirect_entry_t::parse_value () +{ + dest = (value >> 56) & 0xff; + masked = (value >> 16) & 1; + trig_mode = (value >> 15) & 1; + remote_irr = (value >> 14) & 1; + polarity = (value >> 13) & 1; + //delivery_status = (value >> 12) & 1; + delivery_status = 0; // always say the message has gone through + dest_mode = (value >> 11) & 1; + delivery_mode = (value >> 8) & 7; + vector = (value >> 0) & 0xff; +} + +void +bx_io_redirect_entry_t::sprintf_self (char *buf) +{ + sprintf (buf, "dest=%02x, masked=%d, trig_mode=%d, remote_irr=%d, polarity=%d, delivery_status=%d, dest_mode=%d, delivery_mode=%d, vector=%02x", dest, masked, trig_mode, remote_irr, polarity, delivery_status, dest_mode, delivery_mode, vector); +} + +bx_ioapic_c::bx_ioapic_c () + : bx_generic_apic_c () +{ + put("IOAP"); + settype(IOAPICLOG); +} + +bx_ioapic_c::~bx_ioapic_c () { +} + +void +bx_ioapic_c::init () +{ + bx_generic_apic_c::init (); + BX_DEBUG(("initializing I/O APIC")); + base_addr = 0xfec00000; + ioregsel = 0; + // all interrupts masked + for (int i=0; i<BX_IOAPIC_NUM_PINS; i++) { + ioredtbl[i].set_even_word (0x00010000); + ioredtbl[i].set_odd_word (0x00000000); + } + irr = 0; +} + +void +bx_ioapic_c::reset (unsigned type) +{ +} + +void +bx_ioapic_c::read_aligned(Bit32u address, Bit32u *data, unsigned len) +{ + BX_DEBUG( ("I/O APIC read_aligned addr=%08x, len=%d", address, len)); + BX_ASSERT (len == 4); + address &= 0xff; + if (address == 0x00) { + // select register + *data = ioregsel; + return; + } else if (address != 0x10) { + BX_PANIC(("IOAPIC: read from unsupported address")); + } + // only reached when reading data register + switch (ioregsel) { + case 0x00: // APIC ID + *data = ((id & 0xf) << 24); + return; + case 0x01: // version + *data = (((BX_IOAPIC_NUM_PINS-1) & 0xff) << 16) + | (BX_IOAPIC_VERSION_ID & 0x0f); + return; + case 0x02: + BX_INFO(("IOAPIC: arbitration ID unsupported, returned 0")); + *data = 0; + return; + default: + int index = (ioregsel - 0x10) >> 1; + if (index >= 0 && index < BX_IOAPIC_NUM_PINS) { + bx_io_redirect_entry_t *entry = ioredtbl + index; + *data = (ioregsel&1) ? entry->get_odd_word() : entry->get_even_word (); + return; + } + BX_PANIC(("IOAPIC: IOREGSEL points to undefined register %02x", ioregsel)); + } +} + +void +bx_ioapic_c::write(Bit32u address, Bit32u *value, unsigned len) +{ + BX_DEBUG(("IOAPIC: write addr=%08x, data=%08x, len=%d", address, *value, len)); + address &= 0xff; + if (address == 0x00) { + ioregsel = *value; + return; + } else if (address != 0x10) { + BX_PANIC(("IOAPIC: write to unsupported address")); + } + // only reached when writing data register + switch (ioregsel) { + case 0x00: // set APIC ID + { + Bit8u newid = (*value >> 24) & 0xf; + BX_INFO(("IOAPIC: setting id to 0x%x", newid)); + set_id (newid); + return; + } + case 0x01: // version + case 0x02: // arbitration id + BX_INFO(("IOAPIC: could not write, IOREGSEL=0x%02x", ioregsel)); + return; + default: + int index = (ioregsel - 0x10) >> 1; + if (index >= 0 && index < BX_IOAPIC_NUM_PINS) { + bx_io_redirect_entry_t *entry = ioredtbl + index; + if (ioregsel&1) + entry->set_odd_word (*value); + else + entry->set_even_word (*value); + char buf[1024]; + entry->sprintf_self (buf); + BX_DEBUG(("IOAPIC: now entry[%d] is %s", index, buf)); + service_ioapic (); + return; + } + BX_PANIC(("IOAPIC: IOREGSEL points to undefined register %02x", ioregsel)); + } +} + +void bx_ioapic_c::trigger_irq (unsigned vector, unsigned from) +{ + BX_DEBUG(("IOAPIC: received interrupt %d", vector)); + if (vector >= 0 && vector < BX_IOAPIC_NUM_PINS) { + Bit32u bit = 1<<vector; + if ((irr & bit) == 0) { + irr |= bit; + service_ioapic (); + } + } else BX_PANIC(("IOAPIC: vector %d out of range", vector)); +} + +void bx_ioapic_c::untrigger_irq (unsigned num, unsigned from) +{ + BX_DEBUG(("IOAPIC: interrupt %d went away", num)); +} + +void bx_ioapic_c::service_ioapic () +{ + // look in IRR and deliver any interrupts that are not masked. + BX_DEBUG(("IOAPIC: servicing")); + for (unsigned bit=0; bit < BX_IOAPIC_NUM_PINS; bit++) { + if (irr & (1<<bit)) { + bx_io_redirect_entry_t *entry = ioredtbl + bit; + if (!entry->masked) { + // clear irr bit and deliver + bx_bool done = deliver (entry->dest, entry->dest_mode, entry->delivery_mode, entry->vector, entry->polarity, entry->trig_mode); + if (done) irr &= ~(1<<bit); + } + } + } +} + +#endif /* if BX_SUPPORT_APIC */ diff --git a/tools/ioemu/iodev/ioapic.h b/tools/ioemu/iodev/ioapic.h new file mode 100644 index 0000000000..be008f04c8 --- /dev/null +++ b/tools/ioemu/iodev/ioapic.h @@ -0,0 +1,54 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: ioapic.h,v 1.5 2002/10/25 11:44:40 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// +extern class bx_ioapic_c bx_ioapic; + +#define BX_IOAPIC_VERSION_ID 0x00170011 // same version as 82093 IOAPIC +#define BX_IOAPIC_NUM_PINS 0x18 + +class bx_io_redirect_entry_t { + Bit64u value; +public: + Bit32u get_even_word () { return value & 0xffffffff; } + Bit32u get_odd_word () { return (value>>32) & 0xffffffff; } + void set_even_word (Bit32u even) { + // keep high 32 bits of value, replace low 32 + value = ((value >> 32) << 32) | (even & 0xffffffff); + parse_value (); + } + void set_odd_word (Bit32u odd) { + // keep low 32 bits of value, replace high 32 + value = (((Bit64u)odd & 0xffffffff) << 32) | (value & 0xffffffff); + parse_value (); + } + void parse_value (); + // parse_value sets the value and all the fields below. Do not change + // these fields except by calling parse_value. + Bit8u dest, masked, trig_mode, remote_irr, polarity, delivery_status, dest_mode, delivery_mode, vector; + void sprintf_self (char *buf); +}; + +class bx_ioapic_c : public bx_generic_apic_c { + Bit32u ioregsel; // selects between various registers + // interrupt request bitmask, not visible from the outside. Bits in the + // irr are set when trigger_irq is called, and cleared when the interrupt + // is delivered to the processor. If an interrupt is masked, the irr + // will still be set but delivery will not occur until it is unmasked. + // It's not clear if this is how the real device works. + Bit32u irr; +public: + bx_io_redirect_entry_t ioredtbl[BX_IOAPIC_NUM_PINS]; // table of redirections + bx_ioapic_c (); + ~bx_ioapic_c (); + virtual void init (); + virtual void reset (unsigned type); + virtual void read_aligned(Bit32u address, Bit32u *data, unsigned len); + virtual void write(Bit32u address, Bit32u *value, unsigned len); + void trigger_irq (unsigned num, unsigned from); + void untrigger_irq (unsigned num, unsigned from); + void service_ioapic (); + virtual bx_bool match_logical_addr (Bit8u address) { return false; } + virtual bx_bool is_local_apic () { return false; } + virtual bx_apic_type_t get_type () { return APIC_TYPE_IOAPIC; } +}; diff --git a/tools/ioemu/iodev/iodebug.cc b/tools/ioemu/iodev/iodebug.cc new file mode 100644 index 0000000000..ca2314ede6 --- /dev/null +++ b/tools/ioemu/iodev/iodebug.cc @@ -0,0 +1,354 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: iodebug.cc,v 1.15 2002/11/19 05:47:45 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// +#include "bochs.h" +#if BX_IODEBUG_SUPPORT + + + +bx_iodebug_c bx_iodebug; +bx_iodebug_c *bx_iodebug_ptr; + + struct bx_iodebug_s_type { + bx_bool enabled; + unsigned int register_select; + Bit32u registers[2]; + Bit32u monitored_mem_areas_start[BX_IODEBUG_MAX_AREAS]; + Bit32u monitored_mem_areas_end[BX_IODEBUG_MAX_AREAS]; + } bx_iodebug_s; + + + + +// Constructor +bx_iodebug_c::bx_iodebug_c( void ) +{ + put("IODEBUG"); + settype(IODEBUGLOG); + +} + + + + + +// Destructor +bx_iodebug_c::~bx_iodebug_c( void ) +{ +} + + + + + +void bx_iodebug_c::init(void) +{ + int i; + + DEV_register_ioread_handler(this, read_handler, 0x8A00,"BOCHS IODEBUG", 7); + DEV_register_iowrite_handler(this, write_handler, 0x8A00,"BOCHS IODEBUG", 7); + DEV_register_iowrite_handler(this, write_handler, 0x8A01,"BOCHS IODEBUG", 7); +// fprintf( stderr, "IODEBUG initialized\n"); + + bx_iodebug_s.enabled = 0; + bx_iodebug_s.register_select = 0; + for(i=0;i<BX_IODEBUG_MAX_AREAS;i++) { + bx_iodebug_s.monitored_mem_areas_start[i] = 0; + bx_iodebug_s.monitored_mem_areas_end[i] = 0; + } +} + +void bx_iodebug_c::reset(unsigned type) +{ +} + + +Bit32u bx_iodebug_c::read_handler(void *this_ptr, Bit32u addr, unsigned io_len) +{ + bx_iodebug_ptr = (bx_iodebug_c *) this_ptr; + return( bx_iodebug_ptr->read(addr, io_len) ); +} + + + + + + +Bit32u bx_iodebug_c::read( Bit32u addr, unsigned io_len ) +{ + + if(bx_iodebug_s.enabled) return(0x8A00); + return(0); +} + + + + + + + + + + +void bx_iodebug_c::write_handler(void *this_ptr, Bit32u addr, Bit32u dvalue, unsigned io_len) +{ + bx_iodebug_c *class_ptr = (bx_iodebug_c *) this_ptr; + class_ptr->write( addr, dvalue, io_len ); +} + + + + + + +void bx_iodebug_c::write( Bit32u addr, Bit32u dvalue, unsigned int io_len ) +{ + + +// fprintf(stderr, "IODEBUG addr: %4x\tdvalue: %8x\tio_len: %8x\n", (unsigned int)addr, (unsigned int)dvalue, io_len); + + if( addr == 0x8A01 && io_len == 2 ) + { + bx_iodebug_s.registers[bx_iodebug_s.register_select] = + (bx_iodebug_s.registers[bx_iodebug_s.register_select] << 16) + + (dvalue & 0x0000FFFF ); + } + + if( (addr != 0x8A00) || (io_len != 2) ) return; + + if( !bx_iodebug_s.enabled ) + { + if( dvalue == 0x8A00 ) + { + bx_iodebug_s.enabled = 1; +// fprintf(stderr, "IODEBUG enabled\n"); + bx_iodebug_s.registers[0] = 0; + bx_iodebug_s.registers[1] = 0; + } + return; + } + + switch( dvalue ) + { + case( 0x8A01 ): + bx_iodebug_s.register_select = 0; +// fprintf( stderr, "IODEBUG register 0 selected\n"); + break; + + case( 0x8A02 ): + bx_iodebug_s.register_select = 1; +// fprintf( stderr, "IODEBUG register 1 selected\n"); + break; + + case( 0x8A80 ): + bx_iodebug_s.register_select = 0; + bx_iodebug_c::add_range( + bx_iodebug_s.registers[0], + bx_iodebug_s.registers[1]); + bx_iodebug_s.registers[0] = 0; + bx_iodebug_s.registers[1] = 0; + break; + +#if BX_DEBUGGER + case( 0x8AE0 ): + fprintf( stderr, "request return to dbg prompt received, 0x8AE0 command (iodebug)\n"); + bx_guard.interrupt_requested=1; + break; + + case( 0x8AE2): + fprintf( stderr, "request made by the guest os to disable tracing, iodebug port 0x8A00->0x8AE2\n"); + BX_CPU(dbg_cpu)->trace = 0; + break; + + case( 0x8AE3 ): + fprintf( stderr, "request made by the guest os to enable tracing, iodebug port 0x8A00->0x8AE3\n"); + BX_CPU(dbg_cpu)->trace = 1; + break; + + case( 0x8AE4 ): + fprintf( stderr, "request made by the guest os to disable register tracing, iodebug port 0x8A00->0x8AE4\n"); + BX_CPU(dbg_cpu)->trace_reg = 0; + break; + + case( 0x8AE5 ): + fprintf( stderr, "request made by the guest os to enable register tracing, iodebug port 0x8A00->0x8AE5\n"); + BX_CPU(dbg_cpu)->trace_reg = 1; + break; + +#endif + + case( 0x8AFF ): + bx_iodebug_s.enabled = 0; +// fprintf( stderr, "IODEBUG device deactivated\n"); +// break; + +// default: +// fprintf(stderr,"IODEBUG unsupported register code\n"); + } +} + + + + + + + + +// Static function +void bx_iodebug_c::mem_write( BX_CPU_C *cpu, Bit32u addr, unsigned len, void *data) +{ + Bit32u data32; + Bit16u data16; + Bit8u data8; + + unsigned int area; + if( !bx_iodebug_s.enabled ) return; + + area = bx_iodebug_c::range_test( addr, len ); + // Device is enabled, testing address ranges + if( area ) + { + area--; +#if BX_DEBUGGER + fprintf( stdout, "%s @ eip: %08X wrote at monitored memory location %8X\n", cpu->name, cpu->get_EIP(), addr); + bx_guard.interrupt_requested=1; +#else + fprintf( stderr, + "IODEBUG write to monitored memory area: %2i\tby EIP:\t\t%08X\n\trange start: \t\t%08X\trange end:\t%08X\n\taddress accessed:\t%08X\tdata written:\t", + area, + cpu->get_EIP(), + bx_iodebug_s.monitored_mem_areas_start[area], + bx_iodebug_s.monitored_mem_areas_end[area], + (unsigned int)addr); + + data32 = * (Bit32u *)data; + data16 = (Bit16u)data32; + data8 = (Bit8u)data32; + + switch(len) + { + case(1): + fprintf(stderr,"%02X\n", (unsigned int)data8); + break; + + case(2): + fprintf(stderr,"%04X\n", (unsigned int)data16); + break; + + case(4): + fprintf(stderr,"%08X\n", (unsigned int)data32); + break; + + default: + fprintf(stderr, "unsupported write size\n"); + } +#endif + } +} + + + + + + + + +void bx_iodebug_c::mem_read( BX_CPU_C *cpu, Bit32u addr, unsigned len, void *data) +{ + Bit32u data32; + Bit16u data16; + Bit8u data8; + + unsigned int area; + if( !bx_iodebug_s.enabled ) return; + + area = bx_iodebug_c::range_test( addr, len ); + // Device is enabled, testing address ranges + if( area ) + { + area--; +#if BX_DEBUGGER + fprintf( stdout, "%s @ eip: %8X wrote at monitored memory location %8X\n", cpu->name, cpu->get_EIP(), addr); + bx_guard.interrupt_requested=1; +#else + fprintf( stderr, + "IODEBUG read to monitored memory area: %2i\tby EIP:\t\t%08X\n\trange start: \t\t%08X\trange end:\t%08X\n\taddress accessed:\t%08X\tdata written:\t", + area, + cpu->get_EIP(), + bx_iodebug_s.monitored_mem_areas_start[area], + bx_iodebug_s.monitored_mem_areas_end[area], + (unsigned int)addr); + data32 = * (Bit32u *)data; + data16 = (Bit16u)data32; + data8 = (Bit8u)data32; + + switch(len) + { + case(1): + fprintf(stderr,"%02X\n", (unsigned int)data8); + break; + + case(2): + fprintf(stderr,"%04X\n", (unsigned int)data16); + break; + + case(4): + fprintf(stderr,"%08X\n", (unsigned int)data32); + break; + + default: + fprintf(stderr, "unsupported write size\n"); + } +#endif + } +} + + + + + + + +unsigned int bx_iodebug_c::range_test( Bit32u addr, unsigned int len ) +{ + unsigned int i; + + for(i=0;i<BX_IODEBUG_MAX_AREAS;i++) + { + if( (bx_iodebug_s.monitored_mem_areas_start[i]!=0) || + (bx_iodebug_s.monitored_mem_areas_end[i]!=0) ) + { + if( (Bit32u)(addr+len-1) < bx_iodebug_s.monitored_mem_areas_start[i] ) + continue; + if( addr < bx_iodebug_s.monitored_mem_areas_end[i] ) + { + return(++i); + } + } + } + return(0); +} + + + + + + +void bx_iodebug_c::add_range( Bit32u addr_start, Bit32u addr_end ) +{ + unsigned int i; + for(i=0;i<BX_IODEBUG_MAX_AREAS;i++) + { + if( !bx_iodebug_s.monitored_mem_areas_start[i] && + !bx_iodebug_s.monitored_mem_areas_end[i] ) + { + bx_iodebug_s.monitored_mem_areas_start[i] = addr_start; + bx_iodebug_s.monitored_mem_areas_end[i] = addr_end; +// fprintf(stderr, "IODEBUG added range successfully in slot: %i\n",i); + return; + } + } +// fprintf(stderr, "IODEBUG unable to register memory range, all slots taken\n"); +} +#endif /* if BX_IODEBUG_SUPPORT */ diff --git a/tools/ioemu/iodev/iodebug.h b/tools/ioemu/iodev/iodebug.h new file mode 100644 index 0000000000..a31f7cfa92 --- /dev/null +++ b/tools/ioemu/iodev/iodebug.h @@ -0,0 +1,35 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: iodebug.h,v 1.7 2002/10/26 03:53:22 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// +#ifndef _BX_IODEBUG_H +#define _BX_IODEBUG_H + +#include "config.h" + +#define BX_IODEBUG_THIS this-> + +#define BX_IODEBUG_MAX_AREAS 30 + +class bx_iodebug_c : public bx_devmodel_c +{ +public: + bx_iodebug_c( void ); + ~bx_iodebug_c( void ); + virtual void init(void); + virtual void reset (unsigned type); + static void mem_write( BX_CPU_C *cpu, Bit32u addr, unsigned len, void *data); + static void mem_read( BX_CPU_C *cpu, Bit32u addr, unsigned len, void *data); + +private: + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); + Bit32u read(Bit32u addr, unsigned int io_len); + void write(Bit32u addr, Bit32u dvalue, unsigned int io_len); + static unsigned int range_test(Bit32u addr, unsigned int len); + static void add_range( Bit32u addr_start, Bit32u addr_end); + +}; + +extern bx_iodebug_c bx_iodebug; +#endif diff --git a/tools/ioemu/iodev/iodev.h b/tools/ioemu/iodev/iodev.h new file mode 100644 index 0000000000..3057f6c334 --- /dev/null +++ b/tools/ioemu/iodev/iodev.h @@ -0,0 +1,422 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: iodev.h,v 1.37 2003/08/04 16:03:09 akrisak Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + + +/* maximum number of emulated devices allowed. floppy, vga, etc... + you can increase this to anything below 256 since an 8-bit handle + is used for each device */ +#define BX_MAX_IO_DEVICES 30 + +/* the last device in the array is the "default" I/O device */ +#define BX_DEFAULT_IO_DEVICE (BX_MAX_IO_DEVICES-1) + +/* number of IRQ lines supported. In an ISA PC there are two + PIC chips cascaded together. each has 8 IRQ lines, so there + should be 16 IRQ's total */ +#define BX_MAX_IRQS 16 +#define BX_NO_IRQ -1 + + +class bx_pit_c; +class bx_keyb_c; +class bx_ioapic_c; +class bx_g2h_c; +#if BX_IODEBUG_SUPPORT +class bx_iodebug_c; +#endif + + + +typedef Bit32u (*bx_read_handler_t)(void *, Bit32u, unsigned); +typedef void (*bx_write_handler_t)(void *, Bit32u, Bit32u, unsigned); + + +#if BX_USE_DEV_SMF +# define BX_DEV_SMF static +# define BX_DEV_THIS bx_devices. +#else +# define BX_DEV_SMF +# define BX_DEV_THIS this-> +#endif + +////////////////////////////////////////////////////////////////////// +// bx_devmodel_c declaration +////////////////////////////////////////////////////////////////////// + +// This class defines virtual methods that are common to all devices. +// Child classes do not need to implement all of them, because in this +// definition they are defined as empty, as opposed to being pure +// virtual (= 0). +class BOCHSAPI bx_devmodel_c : public logfunctions { + public: + virtual ~bx_devmodel_c () {} + virtual void init_mem(BX_MEM_C *) {} + virtual void init(void) {} + virtual void reset(unsigned type) {} + virtual void device_load_state () {} + virtual void device_save_state () {} +}; + +////////////////////////////////////////////////////////////////////// +// declare stubs for devices +////////////////////////////////////////////////////////////////////// + +#define STUBFUNC(dev,method) \ + pluginlog->panic("%s called in %s stub. you must not have loaded the %s plugin", #dev, #method, #dev ) + +class BOCHSAPI bx_keyb_stub_c : public bx_devmodel_c { + public: + virtual ~bx_keyb_stub_c () {} + // stubs for bx_keyb_c methods + virtual void mouse_motion(int delta_x, int delta_y, unsigned button_state) { + STUBFUNC(keyboard, mouse_motion); + } + virtual void gen_scancode(Bit32u key) { + STUBFUNC(keyboard, gen_scancode); + } + virtual void paste_bytes(Bit8u *data, Bit32s length) { + STUBFUNC(keyboard, paste_bytes); + } + virtual void paste_delay_changed () { + STUBFUNC(keyboard, paste_delay_changed); + } + virtual void mouse_enabled_changed(bool enabled) { + STUBFUNC(keyboard, mouse_enabled_changed); + } +}; + +class BOCHSAPI bx_hard_drive_stub_c : public bx_devmodel_c { + public: + virtual void close_harddrive(void) { + STUBFUNC(HD, close_harddrive); + } + virtual void init() { + STUBFUNC(HD, init); + } + virtual void reset(unsigned type) { + STUBFUNC(HD, reset); + } + virtual Bit32u get_device_handle(Bit8u channel, Bit8u device) { + STUBFUNC(HD, get_device_handle); return 0; + } + virtual Bit32u get_first_cd_handle(void) { + STUBFUNC(HD, get_first_cd_handle); return 0; + } + virtual unsigned get_cd_media_status(Bit32u handle) { + STUBFUNC(HD, get_cd_media_status); return 0; + } + virtual unsigned set_cd_media_status(Bit32u handle, unsigned status) { + STUBFUNC(HD, set_cd_media_status); return 0; + } + virtual Bit32u virt_read_handler(Bit32u address, unsigned io_len) + { + STUBFUNC(HD, virt_read_handler); return 0; + } + virtual void virt_write_handler(Bit32u address, + Bit32u value, unsigned io_len) + { + STUBFUNC(HD, virt_write_handler); + } +}; + +class BOCHSAPI bx_floppy_stub_c : public bx_devmodel_c { + public: + virtual unsigned get_media_status(unsigned drive) { + STUBFUNC(floppy, get_media_status); return 0; + } + virtual unsigned set_media_status(unsigned drive, unsigned status) { + STUBFUNC(floppy, set_media_status); return 0; + } +}; + +class BOCHSAPI bx_cmos_stub_c : public bx_devmodel_c { + public: + virtual Bit32u get_reg(unsigned reg) { + STUBFUNC(cmos, get_reg); return 0; + } + virtual void set_reg(unsigned reg, Bit32u val) { + STUBFUNC(cmos, set_reg); + } + virtual time_t get_timeval() { + // STUBFUNC(cmos, get_timeval); + return 0; + } + virtual void checksum_cmos(void) { + STUBFUNC(cmos, checksum); + } +}; + +class BOCHSAPI bx_dma_stub_c : public bx_devmodel_c { + public: + virtual unsigned registerDMA8Channel( + unsigned channel, + void (* dmaRead)(Bit8u *data_byte), + void (* dmaWrite)(Bit8u *data_byte), + const char *name + ) { + STUBFUNC(dma, registerDMA8Channel); return 0; + } + virtual unsigned registerDMA16Channel( + unsigned channel, + void (* dmaRead)(Bit16u *data_word), + void (* dmaWrite)(Bit16u *data_word), + const char *name + ) { + STUBFUNC(dma, registerDMA16Channel); return 0; + } + virtual unsigned unregisterDMAChannel(unsigned channel) { + STUBFUNC(dma, unregisterDMAChannel); return 0; + } + virtual unsigned get_TC(void) { + STUBFUNC(dma, get_TC); return 0; + } + virtual void set_DRQ(unsigned channel, bx_bool val) { + STUBFUNC(dma, set_DRQ); + } + virtual void raise_HLDA(void) { + STUBFUNC(dma, raise_HLDA); + } +}; + +class BOCHSAPI bx_pic_stub_c : public bx_devmodel_c { + public: + virtual void raise_irq(unsigned irq_no) { + STUBFUNC(pic, raise_irq); + } + virtual void lower_irq(unsigned irq_no) { + STUBFUNC(pic, lower_irq); + } + virtual Bit8u IAC(void) { + STUBFUNC(pic, IAC); return 0; + } + virtual void show_pic_state(void) { + STUBFUNC(pic, show_pic_state); + } +}; + +class BOCHSAPI bx_vga_stub_c : public bx_devmodel_c { + public: + virtual void redraw_area(unsigned x0, unsigned y0, + unsigned width, unsigned height) { + STUBFUNC(vga, redraw_area); + } + virtual Bit8u mem_read(Bit32u addr) { + STUBFUNC(vga, mem_read); return 0; + } + virtual void mem_write(Bit32u addr, Bit8u value) { + STUBFUNC(vga, mem_write); + } + virtual void get_text_snapshot(Bit8u **text_snapshot, + unsigned *txHeight, unsigned *txWidth) { + STUBFUNC(vga, get_text_snapshot); + } + virtual void trigger_timer(void *this_ptr) { + STUBFUNC(vga, trigger_timer); + } + virtual void set_update_interval (unsigned interval) { + STUBFUNC(vga, set_update_interval); + } + virtual Bit8u get_actl_palette_idx(Bit8u index) { + return 0; + } +}; + +class BOCHSAPI bx_pci_stub_c : public bx_devmodel_c { + public: + virtual bx_bool register_pci_handlers(void *this_ptr, + Bit32u (*bx_pci_read_handler)(void *, Bit8u, unsigned), + void(*bx_pci_write_handler)(void *, Bit8u, Bit32u, unsigned), + Bit8u devfunc, const char *name) { + STUBFUNC(pci, register_pci_handlers); return 0; + } + virtual Bit8u rd_memType (Bit32u addr) { + return 0; + } + virtual Bit8u wr_memType (Bit32u addr) { + return 0; + } + virtual void print_i440fx_state(void) {} +}; + +class BOCHSAPI bx_ne2k_stub_c : public bx_devmodel_c { + public: + virtual void print_info(FILE *file, int page, int reg, int nodups) {} +}; + +class BOCHSAPI bx_devices_c : public logfunctions { +public: + bx_devices_c(void); + ~bx_devices_c(void); + // Register I/O addresses and IRQ lines. Initialize any internal + // structures. init() is called only once, even if the simulator + // reboots or is restarted. + void init(BX_MEM_C *); + // Enter reset state in response to a reset condition. + // The types of reset conditions are defined in bochs.h: + // power-on, hardware, or software. + void reset(unsigned type); + BX_MEM_C *mem; // address space associated with these devices + bx_bool register_io_read_handler(void *this_ptr, bx_read_handler_t f, Bit32u addr, const char *name, Bit8u mask ); + bx_bool register_io_write_handler(void *this_ptr, bx_write_handler_t f, Bit32u addr, const char *name, Bit8u mask ); + bx_bool register_default_io_read_handler(void *this_ptr, bx_read_handler_t f, const char *name, Bit8u mask ); + bx_bool register_default_io_write_handler(void *this_ptr, bx_write_handler_t f, const char *name, Bit8u mask ); + bx_bool register_irq(unsigned irq, const char *name); + bx_bool unregister_irq(unsigned irq, const char *name); + void iodev_init(void); + Bit32u inp(Bit16u addr, unsigned io_len) BX_CPP_AttrRegparmN(2); + void outp(Bit16u addr, Bit32u value, unsigned io_len) BX_CPP_AttrRegparmN(3); + + static void timer_handler(void *); + void timer(void); + + bx_devmodel_c *pluginBiosDevice; + bx_ioapic_c *ioapic; + bx_pci_stub_c *pluginPciBridge; + bx_devmodel_c *pluginPci2IsaBridge; + bx_devmodel_c *pluginPciVgaAdapter; + bx_devmodel_c *pluginPciUSBAdapter; + bx_pit_c *pit; + bx_keyb_stub_c *pluginKeyboard; + bx_dma_stub_c *pluginDmaDevice; + bx_floppy_stub_c *pluginFloppyDevice; + bx_cmos_stub_c *pluginCmosDevice; + bx_devmodel_c *pluginSerialDevice; + bx_devmodel_c *pluginParallelDevice; + bx_devmodel_c *pluginUnmapped; + bx_vga_stub_c *pluginVgaDevice; + bx_pic_stub_c *pluginPicDevice; + bx_hard_drive_stub_c *pluginHardDrive; + bx_devmodel_c *pluginSB16Device; + bx_ne2k_stub_c *pluginNE2kDevice; + bx_g2h_c *g2h; + bx_devmodel_c *pluginExtFpuIrq; + bx_devmodel_c *pluginGameport; +#if BX_IODEBUG_SUPPORT + bx_iodebug_c *iodebug; +#endif + + // stub classes that the pointers (above) can point to until a plugin is + // loaded + bx_cmos_stub_c stubCmos; + bx_keyb_stub_c stubKeyboard; + bx_hard_drive_stub_c stubHardDrive; + bx_dma_stub_c stubDma; + bx_pic_stub_c stubPic; + bx_floppy_stub_c stubFloppy; + bx_vga_stub_c stubVga; + bx_pci_stub_c stubPci; + bx_ne2k_stub_c stubNE2k; + + // Some info to pass to devices which can handled bulk IO. This allows + // the interface to remain the same for IO devices which can't handle + // bulk IO. We should probably implement special INPBulk() and OUTBulk() + // functions which stick these values in the bx_devices_c class, and + // then call the normal functions rather than having gross globals + // variables. + Bit32u bulkIOHostAddr; + unsigned bulkIOQuantumsRequested; + unsigned bulkIOQuantumsTransferred; + +private: + + Bit8u read_handler_id[0x10000]; // 64K + struct { + bx_read_handler_t funct; + void *this_ptr; + const char *handler_name; // name of device + Bit8u mask; // io_len mask + } io_read_handler[BX_MAX_IO_DEVICES]; + unsigned num_read_handles; + + Bit8u write_handler_id[0x10000]; // 64K + struct { + bx_write_handler_t funct; + void *this_ptr; + const char *handler_name; // name of device + Bit8u mask; // io_len mask + } io_write_handler[BX_MAX_IO_DEVICES]; + unsigned num_write_handles; + + // more for informative purposes, the names of the devices which + // are use each of the IRQ 0..15 lines are stored here + const char *irq_handler_name[BX_MAX_IRQS]; + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); + BX_DEV_SMF Bit32u port92_read(Bit32u address, unsigned io_len); + BX_DEV_SMF void port92_write(Bit32u address, Bit32u value, unsigned io_len); + + static Bit32u default_read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void default_write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); + + int timer_handle; + bx_bool is_serial_enabled (); + bx_bool is_usb_enabled (); + bx_bool is_parallel_enabled (); + }; + + + +#if BX_PCI_SUPPORT +#include "iodev/pci.h" +#include "iodev/pci2isa.h" +#if BX_PCI_VGA_SUPPORT +#include "iodev/pcivga.h" +#endif +#if BX_PCI_USB_SUPPORT +#include "iodev/pciusb.h" +#endif +#endif +#include "iodev/vga.h" +#if BX_SUPPORT_APIC +# include "iodev/ioapic.h" +#endif +#include "iodev/biosdev.h" +#include "iodev/cmos.h" +#include "iodev/dma.h" +#include "iodev/floppy.h" +#include "iodev/harddrv.h" +#if BX_IODEBUG_SUPPORT +# include "iodev/iodebug.h" +#endif +#include "iodev/keyboard.h" +#include "iodev/parallel.h" +#include "iodev/pic.h" +#include "iodev/pit.h" +#include "iodev/pit_wrap.h" +#include "iodev/virt_timer.h" +#include "iodev/serial.h" +#if BX_SUPPORT_SB16 +# include "iodev/sb16.h" +#endif +#include "iodev/unmapped.h" +#include "iodev/eth.h" +#include "iodev/ne2k.h" +#include "iodev/guest2host.h" +#include "iodev/slowdown_timer.h" +#include "iodev/extfpuirq.h" +#include "iodev/gameport.h" diff --git a/tools/ioemu/iodev/keyboard.cc b/tools/ioemu/iodev/keyboard.cc new file mode 100644 index 0000000000..693f4a4c60 --- /dev/null +++ b/tools/ioemu/iodev/keyboard.cc @@ -0,0 +1,1611 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: keyboard.cc,v 1.82 2003/11/11 18:18:36 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + +// Now features proper implementation of keyboard opcodes 0xF4 to 0xF6 +// Silently ignores PS/2 keyboard extensions (0xF7 to 0xFD) +// Explicit panic on resend (0xFE) +// +// Emmanuel Marty <core@ggi-project.org> + +// NB: now the PS/2 mouse support is in, outb changes meaning +// in conjunction with auxb +// auxb == 0 && outb == 0 => both buffers empty (nothing to read) +// auxb == 0 && outb == 1 => keyboard controller output buffer full +// auxb == 1 && outb == 0 => not used +// auxb == 1 && outb == 1 => mouse output buffer full. +// (das) + +// Notes from Christophe Bothamy <cbbochs@free.fr> +// +// This file includes code from Ludovic Lange (http://ludovic.lange.free.fr) +// Implementation of 3 scancodes sets mf1,mf2,mf3 with or without translation. +// Default is mf2 with translation +// Ability to switch between scancodes sets +// Ability to turn translation on or off + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#include <math.h> +#include "scancodes.h" + +#define LOG_THIS theKeyboard-> +#define VERBOSE_KBD_DEBUG 0 + + +bx_keyb_c *theKeyboard = NULL; + + int +libkeyboard_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + // Create one instance of the keyboard device object. + theKeyboard = new bx_keyb_c (); + // Before this plugin was loaded, pluginKeyboard pointed to a stub. + // Now make it point to the real thing. + bx_devices.pluginKeyboard = theKeyboard; + // Register this device. + BX_REGISTER_DEVICE_DEVMODEL (plugin, type, theKeyboard, BX_PLUGIN_KEYBOARD); + return(0); // Success +} + + void +libkeyboard_LTX_plugin_fini(void) +{ + BX_INFO (("keyboard plugin_fini")); +} + +bx_keyb_c::bx_keyb_c(void) +{ + // constructor + put("KBD"); + settype(KBDLOG); +} + +bx_keyb_c::~bx_keyb_c(void) +{ + // destructor + BX_DEBUG(("Exit.")); +} + + +// flush internal buffer and reset keyboard settings to power-up condition + void +bx_keyb_c::resetinternals(bx_bool powerup) +{ + Bit32u i; + + BX_KEY_THIS s.kbd_internal_buffer.num_elements = 0; + for (i=0; i<BX_KBD_ELEMENTS; i++) + BX_KEY_THIS s.kbd_internal_buffer.buffer[i] = 0; + BX_KEY_THIS s.kbd_internal_buffer.head = 0; + + BX_KEY_THIS s.kbd_internal_buffer.expecting_typematic = 0; + + // Default scancode set is mf2 with translation + BX_KEY_THIS s.kbd_controller.expecting_scancodes_set = 0; + BX_KEY_THIS s.kbd_controller.current_scancodes_set = 1; + BX_KEY_THIS s.kbd_controller.scancodes_translate = 1; + + if (powerup) { + BX_KEY_THIS s.kbd_internal_buffer.expecting_led_write = 0; + BX_KEY_THIS s.kbd_internal_buffer.delay = 1; // 500 mS + BX_KEY_THIS s.kbd_internal_buffer.repeat_rate = 0x0b; // 10.9 chars/sec + } +} + + + + void +bx_keyb_c::init(void) +{ + BX_DEBUG(("Init $Id: keyboard.cc,v 1.82 2003/11/11 18:18:36 vruppert Exp $")); + Bit32u i; + + DEV_register_irq(1, "8042 Keyboard controller"); + DEV_register_irq(12, "8042 Keyboard controller (PS/2 mouse)"); + + DEV_register_ioread_handler(this, read_handler, + 0x0060, "8042 Keyboard controller", 1); + DEV_register_ioread_handler(this, read_handler, + 0x0064, "8042 Keyboard controller", 1); + DEV_register_iowrite_handler(this, write_handler, + 0x0060, "8042 Keyboard controller", 1); + DEV_register_iowrite_handler(this, write_handler, + 0x0064, "8042 Keyboard controller", 1); + BX_KEY_THIS timer_handle = bx_pc_system.register_timer( this, timer_handler, + bx_options.Okeyboard_serial_delay->get(), 1, 1, + "8042 Keyboard controller"); + + resetinternals(1); + + BX_KEY_THIS s.kbd_internal_buffer.led_status = 0; + BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled = 1; + + BX_KEY_THIS s.mouse_internal_buffer.num_elements = 0; + for (i=0; i<BX_MOUSE_BUFF_SIZE; i++) + BX_KEY_THIS s.mouse_internal_buffer.buffer[i] = 0; + BX_KEY_THIS s.mouse_internal_buffer.head = 0; + + // BX_INFO(("kbd: %04d outb 0 auxb 0",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.pare = 0; + BX_KEY_THIS s.kbd_controller.tim = 0; + BX_KEY_THIS s.kbd_controller.auxb = 0; + BX_KEY_THIS s.kbd_controller.keyl = 1; + BX_KEY_THIS s.kbd_controller.c_d = 1; + BX_KEY_THIS s.kbd_controller.sysf = 0; + BX_KEY_THIS s.kbd_controller.inpb = 0; + BX_KEY_THIS s.kbd_controller.outb = 0; + + BX_KEY_THIS s.kbd_controller.kbd_clock_enabled = 1; + BX_KEY_THIS s.kbd_controller.aux_clock_enabled = 0; + BX_KEY_THIS s.kbd_controller.allow_irq1 = 1; + BX_KEY_THIS s.kbd_controller.allow_irq12 = 1; + BX_KEY_THIS s.kbd_controller.kbd_output_buffer = 0; + BX_KEY_THIS s.kbd_controller.aux_output_buffer = 0; + BX_KEY_THIS s.kbd_controller.last_comm = 0; + BX_KEY_THIS s.kbd_controller.expecting_port60h = 0; + BX_KEY_THIS s.kbd_controller.irq1_requested = 0; + BX_KEY_THIS s.kbd_controller.irq12_requested = 0; + BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 0; + +//BX_DEBUG(( "# Okeyboard_serial_delay is %u usec", +// (unsigned) bx_options.Okeyboard_serial_delay->get ())); + BX_KEY_THIS s.kbd_controller.timer_pending = 0; + + // Mouse initialization stuff + BX_KEY_THIS s.mouse.sample_rate = 100; // reports per second + BX_KEY_THIS s.mouse.resolution_cpmm = 4; // 4 counts per millimeter + BX_KEY_THIS s.mouse.scaling = 1; /* 1:1 (default) */ + BX_KEY_THIS s.mouse.mode = MOUSE_MODE_RESET; + BX_KEY_THIS s.mouse.enable = 0; + BX_KEY_THIS s.mouse.delayed_dx = 0; + BX_KEY_THIS s.mouse.delayed_dy = 0; + + for (i=0; i<BX_KBD_CONTROLLER_QSIZE; i++) + BX_KEY_THIS s.controller_Q[i] = 0; + BX_KEY_THIS s.controller_Qsize = 0; + BX_KEY_THIS s.controller_Qsource = 0; + + // clear paste buffer + BX_KEY_THIS pastebuf = NULL; + BX_KEY_THIS pastebuf_len = 0; + BX_KEY_THIS pastebuf_ptr = 0; + BX_KEY_THIS paste_delay_changed (); + BX_KEY_THIS stop_paste = 0; + + // mouse port installed on system board + DEV_cmos_set_reg(0x14, DEV_cmos_get_reg(0x14) | 0x04); + +#if BX_WITH_WX + static bx_bool first_time = 1; + if (first_time) { + first_time = 0; + // register shadow params (Experimental, not a complete list by far) + bx_list_c *list = new bx_list_c (BXP_KBD_PARAMETERS, "Keyboard State", "", 20); + list->add (new bx_shadow_bool_c (BXP_KBD_IRQ1_REQ, + "Keyboard IRQ1 requested: ", "", + &BX_KEY_THIS s.kbd_controller.irq1_requested)); + list->add (new bx_shadow_bool_c (BXP_KBD_IRQ12_REQ, + "Keyboard IRQ12 requested: ", "", + &BX_KEY_THIS s.kbd_controller.irq12_requested)); + list->add (new bx_shadow_num_c (BXP_KBD_TIMER_PENDING, + "Keyboard timer pending: ", "", + &BX_KEY_THIS s.kbd_controller.timer_pending)); + list->add (new bx_shadow_bool_c (BXP_KBD_PARE, + "Keyboard PARE", "", + &BX_KEY_THIS s.kbd_controller.pare)); + list->add (new bx_shadow_bool_c (BXP_KBD_TIM, + "Keyboard TIM", "", + &BX_KEY_THIS s.kbd_controller.tim)); + list->add (new bx_shadow_bool_c (BXP_KBD_AUXB, + "Keyboard AUXB", "", + &BX_KEY_THIS s.kbd_controller.auxb)); + list->add (new bx_shadow_bool_c (BXP_KBD_KEYL, + "Keyboard KEYL", "", + &BX_KEY_THIS s.kbd_controller.keyl)); + list->add (new bx_shadow_bool_c (BXP_KBD_C_D, + "Keyboard C_D", "", + &BX_KEY_THIS s.kbd_controller.c_d)); + list->add (new bx_shadow_bool_c (BXP_KBD_SYSF, + "Keyboard SYSF", "", + &BX_KEY_THIS s.kbd_controller.sysf)); + list->add (new bx_shadow_bool_c (BXP_KBD_INPB, + "Keyboard INPB", "", + &BX_KEY_THIS s.kbd_controller.inpb)); + list->add (new bx_shadow_bool_c (BXP_KBD_OUTB, + "Keyboard OUTB", "", + &BX_KEY_THIS s.kbd_controller.outb)); + } +#endif +} + + void +bx_keyb_c::reset(unsigned type) +{ + if (BX_KEY_THIS pastebuf != NULL) { + BX_KEY_THIS stop_paste = 1; + } +} + + void +bx_keyb_c::paste_delay_changed() +{ + BX_KEY_THIS pastedelay = bx_options.Okeyboard_paste_delay->get()/BX_IODEV_HANDLER_PERIOD; + BX_INFO(("will paste characters every %d keyboard ticks",BX_KEY_THIS pastedelay)); +} + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + +// read function - the big picture: +// if address == data port then +// if byte for mouse then return it +// else if byte for keyboard then return it +// else address== status port +// assemble the status bits and return them. +// + Bit32u +bx_keyb_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_KEY_SMF + bx_keyb_c *class_ptr = (bx_keyb_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + Bit32u +bx_keyb_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_KEY_SMF + +//BX_DEBUG(( "read from port 0x%04x", (unsigned) address)); + + if (address == 0x60) { /* output buffer */ + Bit8u val; + if (BX_KEY_THIS s.kbd_controller.auxb) { /* mouse byte available */ + val = BX_KEY_THIS s.kbd_controller.aux_output_buffer; + BX_KEY_THIS s.kbd_controller.aux_output_buffer = 0; + // BX_INFO(("kbd: %04d outb 0 auxb 0",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 0; + BX_KEY_THIS s.kbd_controller.auxb = 0; + BX_KEY_THIS s.kbd_controller.irq12_requested = 0; + + if (BX_KEY_THIS s.controller_Qsize) { + unsigned i; + BX_KEY_THIS s.kbd_controller.aux_output_buffer = BX_KEY_THIS s.controller_Q[0]; + // BX_INFO(("kbd: %04d outb 1 auxb 1",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 1; + BX_KEY_THIS s.kbd_controller.auxb = 1; + if (BX_KEY_THIS s.kbd_controller.allow_irq12) + BX_KEY_THIS s.kbd_controller.irq12_requested = 1; + for (i=0; i<BX_KEY_THIS s.controller_Qsize-1; i++) { + // move Q elements towards head of queue by one + BX_KEY_THIS s.controller_Q[i] = BX_KEY_THIS s.controller_Q[i+1]; + } + BX_KEY_THIS s.controller_Qsize--; + } + +//BX_DEBUG(("mouse: ___io_read aux = 0x%02x", (unsigned) val)); + + DEV_pic_lower_irq(12); + activate_timer(); + BX_DEBUG(("READ(%02x) (from mouse) = %02x", (unsigned) address, + (unsigned) val)); + return val; + } + else if (BX_KEY_THIS s.kbd_controller.outb) { /* kbd byte available */ + val = BX_KEY_THIS s.kbd_controller.kbd_output_buffer; + // BX_INFO(("kbd: %04d outb 0 auxb 0",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 0; + BX_KEY_THIS s.kbd_controller.auxb = 0; + BX_KEY_THIS s.kbd_controller.irq1_requested = 0; +//BX_DEBUG(( "___io_read kbd")); + + if (BX_KEY_THIS s.controller_Qsize) { + unsigned i; + BX_KEY_THIS s.kbd_controller.aux_output_buffer = BX_KEY_THIS s.controller_Q[0]; + // BX_INFO(("kbd: %04d outb 1 auxb 1",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 1; + BX_KEY_THIS s.kbd_controller.auxb = 1; + if (BX_KEY_THIS s.kbd_controller.allow_irq1) + BX_KEY_THIS s.kbd_controller.irq1_requested = 1; + for (i=0; i<BX_KEY_THIS s.controller_Qsize-1; i++) { + // move Q elements towards head of queue by one + BX_KEY_THIS s.controller_Q[i] = BX_KEY_THIS s.controller_Q[i+1]; + } + BX_DEBUG(("s.controller_Qsize: %02X",BX_KEY_THIS s.controller_Qsize)); + BX_KEY_THIS s.controller_Qsize--; + } + + DEV_pic_lower_irq(1); + activate_timer(); + BX_DEBUG(("READ(%02x) = %02x", (unsigned) address, + (unsigned) val)); + return val; + } + else { + BX_DEBUG(("num_elements = %d", BX_KEY_THIS s.kbd_internal_buffer.num_elements)); + BX_DEBUG(("read from port 60h with outb empty")); +// val = BX_KEY_THIS s.kbd_controller.kbd_output_buffer; + return BX_KEY_THIS s.kbd_controller.kbd_output_buffer; + } + } + +#if BX_CPU_LEVEL >= 2 + else if (address == 0x64) { /* status register */ + + return (BX_KEY_THIS s.kbd_controller.pare << 7) | + (BX_KEY_THIS s.kbd_controller.tim << 6) | + (BX_KEY_THIS s.kbd_controller.auxb << 5) | + (BX_KEY_THIS s.kbd_controller.keyl << 4) | + (BX_KEY_THIS s.kbd_controller.c_d << 3) | + (BX_KEY_THIS s.kbd_controller.sysf << 2) | + (BX_KEY_THIS s.kbd_controller.inpb << 1) | + BX_KEY_THIS s.kbd_controller.outb; + } + +#else /* BX_CPU_LEVEL > 0 */ + /* XT MODE, System 8255 Mode Register */ + else if (address == 0x64) { /* status register */ + BX_DEBUG(("IO read from port 64h, system 8255 mode register")); + return BX_KEY_THIS s.kbd_controller.outb; + } +#endif /* BX_CPU_LEVEL > 0 */ + + BX_PANIC(("unknown address in io read to keyboard port %x", + (unsigned) address)); + return 0; /* keep compiler happy */ +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_keyb_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_KEY_SMF + bx_keyb_c *class_ptr = (bx_keyb_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + void +bx_keyb_c::write( Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_KEY_SMF + Bit8u command_byte; + static int kbd_initialized=0; + + BX_DEBUG(("keyboard: 8-bit write to %04x = %02x", (unsigned)address, (unsigned)value)); + + switch (address) { + case 0x60: // input buffer + // if expecting data byte from command last sent to port 64h + if (BX_KEY_THIS s.kbd_controller.expecting_port60h) { + BX_KEY_THIS s.kbd_controller.expecting_port60h = 0; + // data byte written last to 0x60 + BX_KEY_THIS s.kbd_controller.c_d = 0; + if (BX_KEY_THIS s.kbd_controller.inpb) { + BX_PANIC(("write to port 60h, not ready for write")); + } + switch (BX_KEY_THIS s.kbd_controller.last_comm) { + case 0x60: // write command byte + { + bx_bool scan_convert, disable_keyboard, + disable_aux; + + scan_convert = (value >> 6) & 0x01; + disable_aux = (value >> 5) & 0x01; + disable_keyboard = (value >> 4) & 0x01; + BX_KEY_THIS s.kbd_controller.sysf = (value >> 2) & 0x01; + BX_KEY_THIS s.kbd_controller.allow_irq1 = (value >> 0) & 0x01; + BX_KEY_THIS s.kbd_controller.allow_irq12 = (value >> 1) & 0x01; + set_kbd_clock_enable(!disable_keyboard); + set_aux_clock_enable(!disable_aux); + if (BX_KEY_THIS s.kbd_controller.allow_irq12 && BX_KEY_THIS s.kbd_controller.auxb) + BX_KEY_THIS s.kbd_controller.irq12_requested = 1; + else if (BX_KEY_THIS s.kbd_controller.allow_irq1 && BX_KEY_THIS s.kbd_controller.outb) + BX_KEY_THIS s.kbd_controller.irq1_requested = 1; + + BX_DEBUG(( " allow_irq12 set to %u", (unsigned) + BX_KEY_THIS s.kbd_controller.allow_irq12)); + if ( !scan_convert ) + BX_ERROR(("keyboard: (mch) scan convert turned off")); + + // (mch) NT needs this + BX_KEY_THIS s.kbd_controller.scancodes_translate = scan_convert; + } + break; + case 0xd1: // write output port + BX_DEBUG(("write output port with value %02xh", + (unsigned) value)); + BX_SET_ENABLE_A20( (value & 0x02) != 0 ); + if (!(value & 0x01)) + BX_PANIC(("IO write: processor reset requested!")); + break; + case 0xd4: // Write to mouse + // I don't think this enables the AUX clock + //set_aux_clock_enable(1); // enable aux clock line + kbd_ctrl_to_mouse(value); + // ??? should I reset to previous value of aux enable? + break; + + case 0xd3: // write mouse output buffer + // Queue in mouse output buffer + controller_enQ(value, 1); + break; + + case 0xd2: + // Queue in keyboard output buffer + controller_enQ(value, 0); + break; + + default: + BX_PANIC(("=== unsupported write to port 60h(lastcomm=%02x): %02x", + (unsigned) BX_KEY_THIS s.kbd_controller.last_comm, (unsigned) value)); + } + } + else { + // data byte written last to 0x60 + BX_KEY_THIS s.kbd_controller.c_d = 0; + BX_KEY_THIS s.kbd_controller.expecting_port60h = 0; + /* pass byte to keyboard */ + /* ??? should conditionally pass to mouse device here ??? */ + if (BX_KEY_THIS s.kbd_controller.kbd_clock_enabled==0) { + BX_ERROR(("keyboard disabled & send of byte %02x to kbd", + (unsigned) value)); + } + kbd_ctrl_to_kbd(value); + } + break; + + case 0x64: // control register + // command byte written last to 0x64 + BX_KEY_THIS s.kbd_controller.c_d = 1; + BX_KEY_THIS s.kbd_controller.last_comm = value; + // most commands NOT expecting port60 write next + BX_KEY_THIS s.kbd_controller.expecting_port60h = 0; + + switch (value) { + case 0x20: // get keyboard command byte + BX_DEBUG(("get keyboard command byte")); + // controller output buffer must be empty + if (BX_KEY_THIS s.kbd_controller.outb) { + BX_ERROR(("kbd: OUTB set and command 0x%02x encountered", value)); + break; + } + command_byte = + (BX_KEY_THIS s.kbd_controller.scancodes_translate << 6) | + ((!BX_KEY_THIS s.kbd_controller.aux_clock_enabled) << 5) | + ((!BX_KEY_THIS s.kbd_controller.kbd_clock_enabled) << 4) | + (0 << 3) | + (BX_KEY_THIS s.kbd_controller.sysf << 2) | + (BX_KEY_THIS s.kbd_controller.allow_irq12 << 1) | + (BX_KEY_THIS s.kbd_controller.allow_irq1 << 0); + controller_enQ(command_byte, 0); + break; + case 0x60: // write command byte + BX_DEBUG(("write command byte")); + // following byte written to port 60h is command byte + BX_KEY_THIS s.kbd_controller.expecting_port60h = 1; + break; + + case 0xa0: + BX_DEBUG(("keyboard BIOS name not supported")); + break; + + case 0xa1: + BX_DEBUG(("keyboard BIOS version not supported")); + break; + + case 0xa7: // disable the aux device + set_aux_clock_enable(0); + BX_DEBUG(("aux device disabled")); + break; + case 0xa8: // enable the aux device + set_aux_clock_enable(1); + BX_DEBUG(("aux device enabled")); + break; + case 0xa9: // Test Mouse Port + // controller output buffer must be empty + if (BX_KEY_THIS s.kbd_controller.outb) { + BX_PANIC(("kbd: OUTB set and command 0x%02x encountered", value)); + break; + } + controller_enQ(0x00, 0); // no errors detected + break; + case 0xaa: // motherboard controller self test + BX_DEBUG(("Self Test")); + if( kbd_initialized == 0 ) + { + BX_KEY_THIS s.controller_Qsize = 0; + BX_KEY_THIS s.kbd_controller.outb = 0; + kbd_initialized++; + } + // controller output buffer must be empty + if (BX_KEY_THIS s.kbd_controller.outb) { + BX_ERROR(("kbd: OUTB set and command 0x%02x encountered", value)); + break; + } + // (mch) Why is this commented out??? Enabling + BX_KEY_THIS s.kbd_controller.sysf = 1; // self test complete + controller_enQ(0x55, 0); // controller OK + break; + case 0xab: // Interface Test + // controller output buffer must be empty + if (BX_KEY_THIS s.kbd_controller.outb) { +BX_PANIC(("kbd: OUTB set and command 0x%02x encountered", value)); + break; + } + controller_enQ(0x00, 0); + break; + case 0xad: // disable keyboard + set_kbd_clock_enable(0); + BX_DEBUG(("keyboard disabled")); + break; + case 0xae: // enable keyboard + set_kbd_clock_enable(1); + BX_DEBUG(("keyboard enabled")); + break; + case 0xc0: // read input port + // controller output buffer must be empty + if (BX_KEY_THIS s.kbd_controller.outb) { +BX_PANIC(("kbd: OUTB set and command 0x%02x encountered", value)); + break; + } + // keyboard power normal + controller_enQ(0x00, 0); + break; + case 0xd0: // read output port: next byte read from port 60h + BX_DEBUG(("io write to port 64h, command d0h (partial)")); + // controller output buffer must be empty + if (BX_KEY_THIS s.kbd_controller.outb) { +BX_PANIC(("kbd: OUTB set and command 0x%02x encountered", value)); + break; + } + controller_enQ( + (BX_KEY_THIS s.kbd_controller.auxb << 5) | + (BX_KEY_THIS s.kbd_controller.outb << 4) | + (BX_GET_ENABLE_A20() << 1) | + 0x01, 0); + break; + + case 0xd1: // write output port: next byte written to port 60h + BX_DEBUG(("write output port")); + // following byte to port 60h written to output port + BX_KEY_THIS s.kbd_controller.expecting_port60h = 1; + break; + + case 0xd3: // write mouse output buffer + //FIXME: Why was this a panic? + BX_DEBUG(("io write 0x64: command = 0xD3(write mouse outb)")); + // following byte to port 60h written to output port as mouse write. + BX_KEY_THIS s.kbd_controller.expecting_port60h = 1; + break; + + case 0xd4: // write to mouse + BX_DEBUG(("io write 0x64: command = 0xD4 (write to mouse)")); + // following byte written to port 60h + BX_KEY_THIS s.kbd_controller.expecting_port60h = 1; + break; + + case 0xd2: // write keyboard output buffer + BX_DEBUG(("io write 0x64: write keyboard output buffer")); + BX_KEY_THIS s.kbd_controller.expecting_port60h = 1; + break; + case 0xdd: // Disable A20 Address Line + BX_SET_ENABLE_A20(0); + break; + case 0xdf: // Enable A20 Address Line + BX_SET_ENABLE_A20(1); + break; + case 0xc1: // Continuous Input Port Poll, Low + case 0xc2: // Continuous Input Port Poll, High + case 0xe0: // Read Test Inputs + BX_PANIC(("io write 0x64: command = %02xh", (unsigned) value)); + break; + + case 0xfe: // System Reset, transition to real mode + BX_INFO(("system reset")); + bx_pc_system.ResetSignal( PCS_SET ); /* XXX is this right? */ + { + for (int i=0; i<BX_SMP_PROCESSORS; i++) + BX_CPU(i)->reset(BX_RESET_HARDWARE); + } + // Use bx_pc_system if necessary bx_cpu.reset_cpu(); + // bx_pc_system.ResetSignal( PCS_SET ); + break; + + default: + if (value==0xff || (value>=0xf0 && value<=0xfd)) { + /* useless pulse output bit commands ??? */ + BX_DEBUG(("io write to port 64h, useless command %02x", + (unsigned) value)); + return; + } + BX_PANIC(("unsupported io write to keyboard port %x, value = %x", + (unsigned) address, (unsigned) value)); + break; + } + break; + + default: BX_PANIC(("unknown address in bx_keyb_c::write()")); + } +} + +// service_paste_buf() transfers data from the paste buffer to the hardware +// keyboard buffer. It tries to transfer as many chars as possible at a +// time, but because different chars require different numbers of scancodes +// we have to be conservative. Note that this process depends on the +// keymap tables to know what chars correspond to what keys, and which +// chars require a shift or other modifier. +void +bx_keyb_c::service_paste_buf () +{ + if (!BX_KEY_THIS pastebuf) return; + BX_DEBUG (("service_paste_buf: ptr at %d out of %d", BX_KEY_THIS pastebuf_ptr, BX_KEY_THIS pastebuf_len)); + int fill_threshold = BX_KBD_ELEMENTS - 8; + while ( (BX_KEY_THIS pastebuf_ptr < BX_KEY_THIS pastebuf_len) && ! BX_KEY_THIS stop_paste) { + if (BX_KEY_THIS s.kbd_internal_buffer.num_elements >= fill_threshold) + return; + // there room in the buffer for a keypress and a key release. + // send one keypress and a key release. + Bit8u byte = BX_KEY_THIS pastebuf[BX_KEY_THIS pastebuf_ptr]; + BXKeyEntry *entry = bx_keymap.findAsciiChar (byte); + if (!entry) { + BX_ERROR (("paste character 0x%02x ignored", byte)); + } else { + BX_DEBUG (("pasting character 0x%02x. baseKey is %04x", byte, entry->baseKey)); + if (entry->modKey != BX_KEYMAP_UNKNOWN) + BX_KEY_THIS gen_scancode (entry->modKey); + BX_KEY_THIS gen_scancode (entry->baseKey); + BX_KEY_THIS gen_scancode (entry->baseKey | BX_KEY_RELEASED); + if (entry->modKey != BX_KEYMAP_UNKNOWN) + BX_KEY_THIS gen_scancode (entry->modKey | BX_KEY_RELEASED); + } + BX_KEY_THIS pastebuf_ptr++; + } + // reached end of pastebuf. free the memory it was using. + delete [] BX_KEY_THIS pastebuf; + BX_KEY_THIS pastebuf = NULL; + BX_KEY_THIS stop_paste = 0; +} + +// paste_bytes schedules an arbitrary number of ASCII characters to be +// inserted into the hardware queue as it become available. Any previous +// paste which is still in progress will be thrown out. BYTES is a pointer +// to a region of memory containing the chars to be pasted. When the paste +// is complete, the keyboard code will call delete [] bytes; +void +bx_keyb_c::paste_bytes (Bit8u *bytes, Bit32s length) +{ + BX_DEBUG (("paste_bytes: %d bytes", length)); + if (BX_KEY_THIS pastebuf) { + BX_ERROR (("previous paste was not completed! %d chars lost", + BX_KEY_THIS pastebuf_len - BX_KEY_THIS pastebuf_ptr)); + delete [] BX_KEY_THIS pastebuf; // free the old paste buffer + } + BX_KEY_THIS pastebuf = bytes; + BX_KEY_THIS pastebuf_ptr = 0; + BX_KEY_THIS pastebuf_len = length; + BX_KEY_THIS service_paste_buf (); +} + + void +bx_keyb_c::gen_scancode(Bit32u key) +{ + unsigned char *scancode; + Bit8u i; + + BX_DEBUG(( "gen_scancode(): %s %s", bx_keymap.getBXKeyName(key), (key >> 31)?"released":"pressed")); + + if (!BX_KEY_THIS s.kbd_controller.scancodes_translate) + BX_DEBUG(("keyboard: gen_scancode with scancode_translate cleared")); + + // Ignore scancode if keyboard clock is driven low + if (BX_KEY_THIS s.kbd_controller.kbd_clock_enabled==0) + return; + + // Ignore scancode if scanning is disabled + if (BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled==0) + return; + + // Switch between make and break code + if (key & BX_KEY_RELEASED) + scancode=(unsigned char *)scancodes[(key&0xFF)][BX_KEY_THIS s.kbd_controller.current_scancodes_set].brek; + else + scancode=(unsigned char *)scancodes[(key&0xFF)][BX_KEY_THIS s.kbd_controller.current_scancodes_set].make; + + if (BX_KEY_THIS s.kbd_controller.scancodes_translate) { + // Translate before send + Bit8u escaped=0x00; + + for (i=0; i<strlen( (const char *)scancode ); i++) { + if (scancode[i] == 0xF0) + escaped=0x80; + else { + BX_DEBUG(("gen_scancode(): writing translated %02x",translation8042[scancode[i] ] | escaped)); + kbd_enQ(translation8042[scancode[i] ] | escaped ); + escaped=0x00; + } + } + } + else { + // Send raw data + for (i=0; i<strlen( (const char *)scancode ); i++) { + BX_DEBUG(("gen_scancode(): writing raw %02x",scancode[i])); + kbd_enQ( scancode[i] ); + } + } +} + + + + void BX_CPP_AttrRegparmN(1) +bx_keyb_c::set_kbd_clock_enable(Bit8u value) +{ + bx_bool prev_kbd_clock_enabled; + + if (value==0) { + BX_KEY_THIS s.kbd_controller.kbd_clock_enabled = 0; + } + else { + /* is another byte waiting to be sent from the keyboard ? */ + prev_kbd_clock_enabled = BX_KEY_THIS s.kbd_controller.kbd_clock_enabled; + BX_KEY_THIS s.kbd_controller.kbd_clock_enabled = 1; + + if (prev_kbd_clock_enabled==0 && BX_KEY_THIS s.kbd_controller.outb==0) { + activate_timer(); + } + } +} + + + + void +bx_keyb_c::set_aux_clock_enable(Bit8u value) +{ + bx_bool prev_aux_clock_enabled; + + BX_DEBUG(("set_aux_clock_enable(%u)", (unsigned) value)); + if (value==0) { + BX_KEY_THIS s.kbd_controller.aux_clock_enabled = 0; + } + else { + /* is another byte waiting to be sent from the keyboard ? */ + prev_aux_clock_enabled = BX_KEY_THIS s.kbd_controller.aux_clock_enabled; + BX_KEY_THIS s.kbd_controller.aux_clock_enabled = 1; + if (prev_aux_clock_enabled==0 && BX_KEY_THIS s.kbd_controller.outb==0) + activate_timer(); + } +} + + Bit8u +bx_keyb_c::get_kbd_enable(void) +{ + BX_DEBUG(("get_kbd_enable(): getting kbd_clock_enabled of: %02x", + (unsigned) BX_KEY_THIS s.kbd_controller.kbd_clock_enabled)); + + return(BX_KEY_THIS s.kbd_controller.kbd_clock_enabled); +} + + void +bx_keyb_c::controller_enQ(Bit8u data, unsigned source) +{ + // source is 0 for keyboard, 1 for mouse + + BX_DEBUG(("controller_enQ(%02x) source=%02x", (unsigned) data,source)); + + if (BX_KEY_THIS s.kbd_controller.outb) + BX_ERROR(("controller_enQ(): OUTB set!")); + + // see if we need to Q this byte from the controller + // remember this includes mouse bytes. + if (BX_KEY_THIS s.kbd_controller.outb) { + if (BX_KEY_THIS s.controller_Qsize >= BX_KBD_CONTROLLER_QSIZE) + BX_PANIC(("controller_enq(): controller_Q full!")); + BX_KEY_THIS s.controller_Q[BX_KEY_THIS s.controller_Qsize++] = data; + BX_KEY_THIS s.controller_Qsource = source; + return; + } + + // the Q is empty + if (source == 0) { // keyboard + BX_KEY_THIS s.kbd_controller.kbd_output_buffer = data; + // BX_INFO(("kbd: %04d outb 1 auxb 0",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 1; + BX_KEY_THIS s.kbd_controller.auxb = 0; + BX_KEY_THIS s.kbd_controller.inpb = 0; + if (BX_KEY_THIS s.kbd_controller.allow_irq1) + BX_KEY_THIS s.kbd_controller.irq1_requested = 1; + } + else { // mouse + BX_KEY_THIS s.kbd_controller.aux_output_buffer = data; + // BX_INFO(("kbd: %04d outb 1 auxb 1",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 1; + BX_KEY_THIS s.kbd_controller.auxb = 1; + BX_KEY_THIS s.kbd_controller.inpb = 0; + if (BX_KEY_THIS s.kbd_controller.allow_irq12) + BX_KEY_THIS s.kbd_controller.irq12_requested = 1; + } +} + +void +bx_keyb_c::kbd_enQ_imm(Bit8u val) +{ + int tail; + + if (BX_KEY_THIS s.kbd_internal_buffer.num_elements >= BX_KBD_ELEMENTS) { + BX_PANIC(("internal keyboard buffer full (imm)")); + return; + } + + /* enqueue scancode in multibyte internal keyboard buffer */ + tail = (BX_KEY_THIS s.kbd_internal_buffer.head + BX_KEY_THIS s.kbd_internal_buffer.num_elements) % + BX_KBD_ELEMENTS; + + BX_KEY_THIS s.kbd_controller.kbd_output_buffer = val; + // BX_INFO(("kbd: %04d outb 1",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 1; + + if (BX_KEY_THIS s.kbd_controller.allow_irq1) + BX_KEY_THIS s.kbd_controller.irq1_requested = 1; +} + + + void +bx_keyb_c::kbd_enQ(Bit8u scancode) +{ + int tail; + + BX_DEBUG(("kbd_enQ(0x%02x)", (unsigned) scancode)); + + if (BX_KEY_THIS s.kbd_internal_buffer.num_elements >= BX_KBD_ELEMENTS) { + BX_INFO(("internal keyboard buffer full, ignoring scancode.(%02x)", + (unsigned) scancode)); + return; + } + + /* enqueue scancode in multibyte internal keyboard buffer */ + BX_DEBUG(("kbd_enQ: putting scancode 0x%02x in internal buffer", + (unsigned) scancode)); + tail = (BX_KEY_THIS s.kbd_internal_buffer.head + BX_KEY_THIS s.kbd_internal_buffer.num_elements) % + BX_KBD_ELEMENTS; + BX_KEY_THIS s.kbd_internal_buffer.buffer[tail] = scancode; + BX_KEY_THIS s.kbd_internal_buffer.num_elements++; + + if (!BX_KEY_THIS s.kbd_controller.outb && BX_KEY_THIS s.kbd_controller.kbd_clock_enabled) { + activate_timer(); + BX_DEBUG(("activating timer...")); + return; + } +//BX_DEBUG(( "# not activating timer..."); +//BX_DEBUG(( "# allow_irq1 = %u", (unsigned) BX_KEY_THIS s.kbd_controller.allow_irq1); +//BX_DEBUG(( "# outb = %u", (unsigned) BX_KEY_THIS s.kbd_controller.outb); +//BX_DEBUG(( "# clock_enab = %u", (unsigned) BX_KEY_THIS s.kbd_controller.kbd_clock_enabled); +//BX_DEBUG(( "# out_buffer = %u", (unsigned) BX_KEY_THIS s.kbd_controller.kbd_output_buffer); +} + + bx_bool BX_CPP_AttrRegparmN(3) +bx_keyb_c::mouse_enQ_packet(Bit8u b1, Bit8u b2, Bit8u b3) +{ + if ((BX_KEY_THIS s.mouse_internal_buffer.num_elements + 3) >= BX_MOUSE_BUFF_SIZE) { + return(0); /* buffer doesn't have the space */ + } + +//BX_DEBUG(("mouse: enQ_packet(%02x, %02x, %02x)", +// (unsigned) b1, (unsigned) b2, (unsigned) b3)); + + mouse_enQ(b1); + mouse_enQ(b2); + mouse_enQ(b3); + return(1); +} + + + void +bx_keyb_c::mouse_enQ(Bit8u mouse_data) +{ + int tail; + + BX_DEBUG(("mouse_enQ(%02x)", (unsigned) mouse_data)); + + if (BX_KEY_THIS s.mouse_internal_buffer.num_elements >= BX_MOUSE_BUFF_SIZE) { + BX_ERROR(("mouse: internal mouse buffer full, ignoring mouse data.(%02x)", + (unsigned) mouse_data)); + return; + } +//BX_DEBUG(( "# mouse_enq() aux_clock_enabled = %u", +// (unsigned) BX_KEY_THIS s.kbd_controller.aux_clock_enabled); + + /* enqueue mouse data in multibyte internal mouse buffer */ + tail = (BX_KEY_THIS s.mouse_internal_buffer.head + BX_KEY_THIS s.mouse_internal_buffer.num_elements) % + BX_MOUSE_BUFF_SIZE; + BX_KEY_THIS s.mouse_internal_buffer.buffer[tail] = mouse_data; + BX_KEY_THIS s.mouse_internal_buffer.num_elements++; + + if (!BX_KEY_THIS s.kbd_controller.outb && BX_KEY_THIS s.kbd_controller.aux_clock_enabled) { + activate_timer(); +//BX_DEBUG(( "# activating timer..."); + return; + } +//BX_DEBUG(( "# not activating timer..."); +//BX_DEBUG(( "# allow_irq12= %u", (unsigned) BX_KEY_THIS s.kbd_controller.allow_irq12); +//BX_DEBUG(( "# outb = %u", (unsigned) BX_KEY_THIS s.kbd_controller.outb); +//BX_DEBUG(( "# clock_enab = %u", (unsigned) BX_KEY_THIS s.kbd_controller.aux_clock_enabled); +//BX_DEBUG(( "# out_buffer = %u", (unsigned) BX_KEY_THIS s.kbd_controller.aux_output_buffer); +} + + void +bx_keyb_c::kbd_ctrl_to_kbd(Bit8u value) +{ + + BX_DEBUG(("controller passed byte %02xh to keyboard", value)); + + if (BX_KEY_THIS s.kbd_internal_buffer.expecting_typematic) { + BX_KEY_THIS s.kbd_internal_buffer.expecting_typematic = 0; + BX_KEY_THIS s.kbd_internal_buffer.delay = (value >> 5) & 0x03; + switch (BX_KEY_THIS s.kbd_internal_buffer.delay) { + case 0: BX_INFO(("setting delay to 250 mS (unused)")); break; + case 1: BX_INFO(("setting delay to 500 mS (unused)")); break; + case 2: BX_INFO(("setting delay to 750 mS (unused)")); break; + case 3: BX_INFO(("setting delay to 1000 mS (unused)")); break; + } + BX_KEY_THIS s.kbd_internal_buffer.repeat_rate = value & 0x1f; + double cps = 1 /((double)(8 + (value & 0x07)) * (double)exp(log((double)2) * (double)((value >> 3) & 0x03)) * 0.00417); + BX_INFO(("setting repeat rate to %.1f cps (unused)", cps)); + kbd_enQ(0xFA); // send ACK + return; + } + + if (BX_KEY_THIS s.kbd_internal_buffer.expecting_led_write) { + BX_KEY_THIS s.kbd_internal_buffer.expecting_led_write = 0; + BX_KEY_THIS s.kbd_internal_buffer.led_status = value; + BX_DEBUG(("LED status set to %02x", + (unsigned) BX_KEY_THIS s.kbd_internal_buffer.led_status)); + kbd_enQ(0xFA); // send ACK %%% + return; + } + + if (BX_KEY_THIS s.kbd_controller.expecting_scancodes_set) { + BX_KEY_THIS s.kbd_controller.expecting_scancodes_set = 0; + if( value != 0 ) { + if( value<4 ) { + BX_KEY_THIS s.kbd_controller.current_scancodes_set = (value-1); + BX_INFO(("Switched to scancode set %d\n", + (unsigned) BX_KEY_THIS s.kbd_controller.current_scancodes_set + 1)); + kbd_enQ(0xFA); + } + else { + BX_ERROR(("Received scancodes set out of range: %d\n", value )); + kbd_enQ(0xFF); // send ERROR + } + } + else { + // Send current scancodes set to port 0x60 + kbd_enQ( 1 + (BX_KEY_THIS s.kbd_controller.current_scancodes_set) ); + } + return; + } + + switch (value) { + case 0x00: // ??? ignore and let OS timeout with no response + kbd_enQ(0xFA); // send ACK %%% + return; + break; + + case 0x05: // ??? + // (mch) trying to get this to work... + BX_KEY_THIS s.kbd_controller.sysf = 1; + kbd_enQ_imm(0xfe); + return; + break; + + case 0xed: // LED Write + BX_KEY_THIS s.kbd_internal_buffer.expecting_led_write = 1; + kbd_enQ_imm(0xFA); // send ACK %%% + return; + break; + + case 0xee: // echo + kbd_enQ(0xEE); // return same byte (EEh) as echo diagnostic + return; + break; + + case 0xf0: // Select alternate scan code set + BX_KEY_THIS s.kbd_controller.expecting_scancodes_set = 1; + BX_DEBUG(("Expecting scancode set info...\n")); + kbd_enQ(0xFA); // send ACK + return; + break; + + case 0xf2: // identify keyboard + BX_INFO(("identify keyboard command received")); + + // XT sends nothing, AT sends ACK + // MFII with translation sends ACK+ABh+41h + // MFII without translation sends ACK+ABh+83h + if (bx_options.Okeyboard_type->get() != BX_KBD_XT_TYPE) { + kbd_enQ(0xFA); + if (bx_options.Okeyboard_type->get() == BX_KBD_MF_TYPE) { + kbd_enQ(0xAB); + + if(BX_KEY_THIS s.kbd_controller.scancodes_translate) + kbd_enQ(0x41); + else + kbd_enQ(0x83); + } + } + return; + break; + + case 0xf3: // typematic info + BX_KEY_THIS s.kbd_internal_buffer.expecting_typematic = 1; + BX_INFO(("setting typematic info")); + kbd_enQ(0xFA); // send ACK + return; + break; + + case 0xf4: // enable keyboard + BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled = 1; + kbd_enQ(0xFA); // send ACK + return; + break; + + case 0xf5: // reset keyboard to power-up settings and disable scanning + resetinternals(1); + kbd_enQ(0xFA); // send ACK + BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled = 0; + BX_INFO(("reset-disable command received")); + return; + break; + + case 0xf6: // reset keyboard to power-up settings and enable scanning + resetinternals(1); + kbd_enQ(0xFA); // send ACK + BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled = 1; + BX_INFO(("reset-enable command received")); + return; + break; + + case 0xf7: // PS/2 Set All Keys To Typematic + case 0xf8: // PS/2 Set All Keys to Make/Break + case 0xf9: // PS/2 PS/2 Set All Keys to Make + case 0xfa: // PS/2 Set All Keys to Typematic Make/Break + case 0xfb: // PS/2 Set Key Type to Typematic + case 0xfc: // PS/2 Set Key Type to Make/Break + case 0xfd: // PS/2 Set Key Type to Make + // Silently ignore and let the OS timeout, for now. + // If anyone has code around that makes use of that, I can + // provide documentation on their behavior (ask core@ggi-project.org) + return; + break; + + case 0xfe: // resend. aiiee. + BX_PANIC( ("got 0xFE (resend)")); + return; + break; + + case 0xff: // reset: internal keyboard reset and afterwards the BAT + BX_DEBUG(("reset command received")); + resetinternals(1); + kbd_enQ(0xFA); // send ACK + kbd_enQ(0xAA); // BAT test passed + return; + break; + + case 0xd3: + kbd_enQ(0xfa); + return; + + default: + /* XXX fix this properly: + http://panda.cs.ndsu.nodak.edu/~achapwes/PICmicro/mouse/mouse.html + http://sourceforge.net/tracker/index.php?func=detail&aid=422457&group_id=12580&atid=112580 + */ + BX_ERROR(("kbd_ctrl_to_kbd(): got value of %02x", + (unsigned) value)); + kbd_enQ(0xFA); /* send ACK ??? */ + return; + break; + } +} + + void +bx_keyb_c::timer_handler(void *this_ptr) +{ + bx_keyb_c *class_ptr = (bx_keyb_c *) this_ptr; + unsigned retval; + + // retval=class_ptr->periodic( bx_options.Okeyboard_serial_delay->get()); + retval=class_ptr->periodic(1); + + if(retval&0x01) + DEV_pic_raise_irq(1); + if(retval&0x02) + DEV_pic_raise_irq(12); +} + + unsigned +bx_keyb_c::periodic( Bit32u usec_delta ) +{ +/* static int multiple=0; */ + static unsigned count_before_paste=0; + Bit8u retval; + + UNUSED( usec_delta ); + + if (BX_KEY_THIS s.kbd_controller.kbd_clock_enabled ) { + if(++count_before_paste>=BX_KEY_THIS pastedelay) { + // after the paste delay, consider adding moving more chars + // from the paste buffer to the keyboard buffer. + BX_KEY_THIS service_paste_buf (); + count_before_paste=0; + } + } + + retval = BX_KEY_THIS s.kbd_controller.irq1_requested | (BX_KEY_THIS s.kbd_controller.irq12_requested << 1); + BX_KEY_THIS s.kbd_controller.irq1_requested = 0; + BX_KEY_THIS s.kbd_controller.irq12_requested = 0; + + if ( BX_KEY_THIS s.kbd_controller.timer_pending == 0 ) { + return(retval); + } + + if ( usec_delta >= BX_KEY_THIS s.kbd_controller.timer_pending ) { + BX_KEY_THIS s.kbd_controller.timer_pending = 0; + } + else { + BX_KEY_THIS s.kbd_controller.timer_pending -= usec_delta; + return(retval); + } + + if (BX_KEY_THIS s.kbd_controller.outb) { + return(retval); + } + + /* nothing in outb, look for possible data xfer from keyboard or mouse */ + if (BX_KEY_THIS s.kbd_controller.kbd_clock_enabled && BX_KEY_THIS s.kbd_internal_buffer.num_elements) { +//BX_DEBUG(( "# servicing keyboard code"); + BX_DEBUG(("service_keyboard: key in internal buffer waiting")); + BX_KEY_THIS s.kbd_controller.kbd_output_buffer = + BX_KEY_THIS s.kbd_internal_buffer.buffer[BX_KEY_THIS s.kbd_internal_buffer.head]; + // BX_INFO(("kbd: %04d outb 1",__LINE__)); // das + BX_KEY_THIS s.kbd_controller.outb = 1; + // commented out since this would override the current state of the + // mouse buffer flag - no bug seen - just seems wrong (das) + // BX_KEY_THIS s.kbd_controller.auxb = 0; +//BX_DEBUG(( "# ___kbd::periodic kbd"); + BX_KEY_THIS s.kbd_internal_buffer.head = (BX_KEY_THIS s.kbd_internal_buffer.head + 1) % + BX_KBD_ELEMENTS; + BX_KEY_THIS s.kbd_internal_buffer.num_elements--; + if (BX_KEY_THIS s.kbd_controller.allow_irq1) + BX_KEY_THIS s.kbd_controller.irq1_requested = 1; + } + else { + create_mouse_packet(0); + if (BX_KEY_THIS s.kbd_controller.aux_clock_enabled && BX_KEY_THIS s.mouse_internal_buffer.num_elements) { +//BX_DEBUG(( "# servicing mouse code"); + BX_DEBUG(("service_keyboard: key(from mouse) in internal buffer waiting")); + BX_KEY_THIS s.kbd_controller.aux_output_buffer = + BX_KEY_THIS s.mouse_internal_buffer.buffer[BX_KEY_THIS s.mouse_internal_buffer.head]; + + // BX_INFO(("kbd: %04d outb 1 auxb 1",__LINE__)); //das + BX_KEY_THIS s.kbd_controller.outb = 1; + BX_KEY_THIS s.kbd_controller.auxb = 1; +//BX_DEBUG(( "# ___kbd:periodic aux"); + BX_KEY_THIS s.mouse_internal_buffer.head = (BX_KEY_THIS s.mouse_internal_buffer.head + 1) % + BX_MOUSE_BUFF_SIZE; + BX_KEY_THIS s.mouse_internal_buffer.num_elements--; +//BX_DEBUG(( "# allow12 = %u", (unsigned) BX_KEY_THIS s.kbd_controller.allow_irq12); + if (BX_KEY_THIS s.kbd_controller.allow_irq12) + BX_KEY_THIS s.kbd_controller.irq12_requested = 1; + } + else { + BX_DEBUG(("service_keyboard(): no keys waiting")); + } + } + return(retval); +} + + + + + void +bx_keyb_c::activate_timer(void) +{ + if (BX_KEY_THIS s.kbd_controller.timer_pending == 0) { + // BX_KEY_THIS s.kbd_controller.timer_pending = bx_options.Okeyboard_serial_delay->get (); + BX_KEY_THIS s.kbd_controller.timer_pending = 1; + } +} + + + void +bx_keyb_c::kbd_ctrl_to_mouse(Bit8u value) +{ +BX_DEBUG(("MOUSE: kbd_ctrl_to_mouse(%02xh)", (unsigned) value)); +BX_DEBUG((" enable = %u", (unsigned) BX_KEY_THIS s.mouse.enable)); +BX_DEBUG((" allow_irq12 = %u", + (unsigned) BX_KEY_THIS s.kbd_controller.allow_irq12)); +BX_DEBUG((" aux_clock_enabled = %u", + (unsigned) BX_KEY_THIS s.kbd_controller.aux_clock_enabled)); +//BX_DEBUG(( "MOUSE: kbd_ctrl_to_mouse(%02xh)", (unsigned) value)); + + // an ACK (0xFA) is always the first response to any valid input + // received from the system other than Set-Wrap-Mode & Resend-Command + + + if (BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter) { + BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 0; + switch (BX_KEY_THIS s.kbd_controller.last_mouse_command) { + case 0xf3: // Set Mouse Sample Rate + BX_KEY_THIS s.mouse.sample_rate = value; + BX_DEBUG(("[mouse] Sampling rate set: %d Hz", value)); + controller_enQ(0xFA, 1); // ack + break; + + case 0xe8: // Set Mouse Resolution + switch (value) { + case 0: + BX_KEY_THIS s.mouse.resolution_cpmm = 1; + break; + case 1: + BX_KEY_THIS s.mouse.resolution_cpmm = 2; + break; + case 2: + BX_KEY_THIS s.mouse.resolution_cpmm = 4; + break; + case 3: + BX_KEY_THIS s.mouse.resolution_cpmm = 8; + break; + default: + BX_PANIC(("[mouse] Unknown resolution %d", value)); + break; + } + BX_DEBUG(("[mouse] Resolution set to %d counts per mm", + BX_KEY_THIS s.mouse.resolution_cpmm)); + + controller_enQ(0xFA, 1); // ack + break; + + default: + BX_PANIC(("MOUSE: unknown last command (%02xh)", (unsigned) BX_KEY_THIS s.kbd_controller.last_mouse_command)); + } + } else { + BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 0; + BX_KEY_THIS s.kbd_controller.last_mouse_command = value; + + // test for wrap mode first + if (BX_KEY_THIS s.mouse.mode == MOUSE_MODE_WRAP) { + // if not a reset command or reset wrap mode + // then just echo the byte. + if ((value != 0xff) && (value != 0xec)) { + if (bx_dbg.mouse) + BX_INFO(("[mouse] wrap mode: Ignoring command %0X02.",value)); + controller_enQ(value,1); + // bail out + return; + } + } + switch ( value ) { + case 0xe6: // Set Mouse Scaling to 1:1 + controller_enQ(0xFA, 1); // ACK + BX_KEY_THIS s.mouse.scaling = 2; + BX_DEBUG(("[mouse] Scaling set to 1:1")); + break; + + case 0xe7: // Set Mouse Scaling to 2:1 + controller_enQ(0xFA, 1); // ACK + BX_KEY_THIS s.mouse.scaling = 2; + BX_DEBUG(("[mouse] Scaling set to 2:1")); + break; + + case 0xe8: // Set Mouse Resolution + controller_enQ(0xFA, 1); // ACK + BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 1; + break; + + case 0xea: // Set Stream Mode + if (bx_dbg.mouse) + BX_INFO(("[mouse] Mouse stream mode on.")); + BX_KEY_THIS s.mouse.mode = MOUSE_MODE_STREAM; + controller_enQ(0xFA, 1); // ACK + break; + + case 0xec: // Reset Wrap Mode + // unless we are in wrap mode ignore the command + if ( BX_KEY_THIS s.mouse.mode == MOUSE_MODE_WRAP) { + if (bx_dbg.mouse) + BX_INFO(("[mouse] Mouse wrap mode off.")); + // restore previous mode except disable stream mode reporting. + // ### TODO disabling reporting in stream mode + BX_KEY_THIS s.mouse.mode = BX_KEY_THIS s.mouse.saved_mode; + controller_enQ(0xFA, 1); // ACK + } + break; + case 0xee: // Set Wrap Mode + // ### TODO flush output queue. + // ### TODO disable interrupts if in stream mode. + if (bx_dbg.mouse) + BX_INFO(("[mouse] Mouse wrap mode on.")); + BX_KEY_THIS s.mouse.saved_mode = BX_KEY_THIS s.mouse.mode; + BX_KEY_THIS s.mouse.mode = MOUSE_MODE_WRAP; + controller_enQ(0xFA, 1); // ACK + break; + + case 0xf0: // Set Remote Mode (polling mode, i.e. not stream mode.) + if (bx_dbg.mouse) + BX_INFO(("[mouse] Mouse remote mode on.")); + // ### TODO should we flush/discard/ignore any already queued packets? + BX_KEY_THIS s.mouse.mode = MOUSE_MODE_REMOTE; + controller_enQ(0xFA, 1); // ACK + break; + + + case 0xf2: // Read Device Type + controller_enQ(0xFA, 1); // ACK + controller_enQ(0x00, 1); // Device ID + BX_DEBUG(("[mouse] Read mouse ID")); + break; + + case 0xf3: // Set Mouse Sample Rate (sample rate written to port 60h) + controller_enQ(0xFA, 1); // ACK + BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 1; + break; + + case 0xf4: // Enable (in stream mode) + BX_KEY_THIS s.mouse.enable = 1; + controller_enQ(0xFA, 1); // ACK + BX_DEBUG(("[mouse] Mouse enabled (stream mode)")); + break; + + case 0xf5: // Disable (in stream mode) + BX_KEY_THIS s.mouse.enable = 0; + controller_enQ(0xFA, 1); // ACK + BX_DEBUG(("[mouse] Mouse disabled (stream mode)")); + break; + + case 0xf6: // Set Defaults + BX_KEY_THIS s.mouse.sample_rate = 100; /* reports per second (default) */ + BX_KEY_THIS s.mouse.resolution_cpmm = 4; /* 4 counts per millimeter (default) */ + BX_KEY_THIS s.mouse.scaling = 1; /* 1:1 (default) */ + BX_KEY_THIS s.mouse.enable = 0; + BX_KEY_THIS s.mouse.mode = MOUSE_MODE_STREAM; + controller_enQ(0xFA, 1); // ACK + BX_DEBUG(("[mouse] Set Defaults")); + break; + + case 0xff: // Reset + BX_KEY_THIS s.mouse.sample_rate = 100; /* reports per second (default) */ + BX_KEY_THIS s.mouse.resolution_cpmm = 4; /* 4 counts per millimeter (default) */ + BX_KEY_THIS s.mouse.scaling = 1; /* 1:1 (default) */ + BX_KEY_THIS s.mouse.mode = MOUSE_MODE_RESET; + BX_KEY_THIS s.mouse.enable = 0; + /* (mch) NT expects an ack here */ + controller_enQ(0xFA, 1); // ACK + controller_enQ(0xAA, 1); // completion code + controller_enQ(0x00, 1); // ID code (normal mouse, wheelmouse has id 0x3) + BX_DEBUG(("[mouse] Mouse reset")); + break; + + case 0xe9: // Get mouse information + // should we ack here? (mch): Yes + controller_enQ(0xFA, 1); // ACK + controller_enQ(BX_KEY_THIS s.mouse.get_status_byte(), 1); // status + controller_enQ(BX_KEY_THIS s.mouse.get_resolution_byte(), 1); // resolution + controller_enQ(BX_KEY_THIS s.mouse.sample_rate, 1); // sample rate + BX_DEBUG(("[mouse] Get mouse information")); + break; + + case 0xeb: // Read Data (send a packet when in Remote Mode) + controller_enQ(0xFA, 1); // ACK + // perhaps we should be adding some movement here. + mouse_enQ_packet( ((BX_KEY_THIS s.mouse.button_status & 0x0f) | 0x08), + 0x00, 0x00 ); // bit3 of first byte always set + //assumed we really aren't in polling mode, a rather odd assumption. + BX_ERROR(("[mouse] Warning: Read Data command partially supported.")); + break; + + default: + //FEh Resend + BX_PANIC(("MOUSE: kbd_ctrl_to_mouse(%02xh)", (unsigned) value)); + } + } +} + +void +bx_keyb_c::create_mouse_packet(bool force_enq) { + Bit8u b1, b2, b3; + + // BX_DEBUG("Calling create_mouse_packet: force_enq=%d\n",force_enq); + + if(BX_KEY_THIS s.mouse_internal_buffer.num_elements && !force_enq) + return; + + // BX_DEBUG("Got to first milestone: force_enq=%d\n",force_enq); + + Bit16s delta_x = BX_KEY_THIS s.mouse.delayed_dx; + Bit16s delta_y = BX_KEY_THIS s.mouse.delayed_dy; + Bit8u button_state=BX_KEY_THIS s.mouse.button_status | 0x08; + + if(!force_enq && !delta_x && !delta_y) { + return; + } + + // BX_DEBUG("Got to second milestone: delta_x=%d, delta_y=%d\n",delta_x,delta_y); + + if(delta_x>254) delta_x=254; + if(delta_x<-254) delta_x=-254; + if(delta_y>254) delta_y=254; + if(delta_y<-254) delta_y=-254; + + b1 = (button_state & 0x0f) | 0x08; // bit3 always set + + if ( (delta_x>=0) && (delta_x<=255) ) { + b2 = (Bit8u) delta_x; + BX_KEY_THIS s.mouse.delayed_dx-=delta_x; + } + else if ( delta_x > 255 ) { + b2 = (Bit8u) 0xff; + BX_KEY_THIS s.mouse.delayed_dx-=255; + } + else if ( delta_x >= -256 ) { + b2 = (Bit8u) delta_x; + b1 |= 0x10; + BX_KEY_THIS s.mouse.delayed_dx-=delta_x; + } + else { + b2 = (Bit8u) 0x00; + b1 |= 0x10; + BX_KEY_THIS s.mouse.delayed_dx+=256; + } + + if ( (delta_y>=0) && (delta_y<=255) ) { + b3 = (Bit8u) delta_y; + BX_KEY_THIS s.mouse.delayed_dy-=delta_y; + } + else if ( delta_y > 255 ) { + b3 = (Bit8u) 0xff; + BX_KEY_THIS s.mouse.delayed_dy-=255; + } + else if ( delta_y >= -256 ) { + b3 = (Bit8u) delta_y; + b1 |= 0x20; + BX_KEY_THIS s.mouse.delayed_dy-=delta_y; + } + else { + b3 = (Bit8u) 0x00; + b1 |= 0x20; + BX_KEY_THIS s.mouse.delayed_dy+=256; + } + mouse_enQ_packet(b1, b2, b3); +} + + +void +bx_keyb_c::mouse_enabled_changed(bool enabled) { + if(s.mouse.delayed_dx || BX_KEY_THIS s.mouse.delayed_dy) { + create_mouse_packet(1); + } + s.mouse.delayed_dx=0; + s.mouse.delayed_dy=0; + BX_DEBUG(("Keyboard mouse disable called.")); +} + + void +bx_keyb_c::mouse_motion(int delta_x, int delta_y, unsigned button_state) +{ + bool force_enq=0; + + // If mouse events are disabled on the GUI headerbar, don't + // generate any mouse data + if (bx_options.Omouse_enabled->get () == 0) + return; + + + // don't generate interrupts if we are in remote mode. + if ( BX_KEY_THIS s.mouse.mode == MOUSE_MODE_REMOTE) + // is there any point in doing any work if we don't act on the result + // so go home. + return; + + + // Note: enable only applies in STREAM MODE. + if ( BX_KEY_THIS s.mouse.enable==0 ) + return; + + // scale down the motion + if ( (delta_x < -1) || (delta_x > 1) ) + delta_x /= 2; + if ( (delta_y < -1) || (delta_y > 1) ) + delta_y /= 2; + +#ifdef VERBOSE_KBD_DEBUG + if (delta_x != 0 || delta_y != 0) + BX_DEBUG(("[mouse] Dx=%d Dy=%d", delta_x, delta_y)); +#endif /* ifdef VERBOSE_KBD_DEBUG */ + + if( (delta_x==0) && (delta_y==0) && (BX_KEY_THIS s.mouse.button_status == (button_state & 0x3) ) ) { + BX_DEBUG(("Ignoring useless mouse_motion call:\n")); + BX_DEBUG(("This should be fixed in the gui code.\n")); + return; + } + + if(BX_KEY_THIS s.mouse.button_status != (button_state & 0x3)) { + force_enq=1; + } + + BX_KEY_THIS s.mouse.button_status = button_state & 0x3; + + if(delta_x>255) delta_x=255; + if(delta_y>255) delta_y=255; + if(delta_x<-256) delta_x=-256; + if(delta_y<-256) delta_y=-256; + + BX_KEY_THIS s.mouse.delayed_dx+=delta_x; + BX_KEY_THIS s.mouse.delayed_dy+=delta_y; + + if((BX_KEY_THIS s.mouse.delayed_dx>255)|| + (BX_KEY_THIS s.mouse.delayed_dx<-256)|| + (BX_KEY_THIS s.mouse.delayed_dy>255)|| + (BX_KEY_THIS s.mouse.delayed_dy<-256)) { + force_enq=1; + } + + create_mouse_packet(force_enq); +} + + + int +bx_keyb_c::SaveState( class state_file *fd ) +{ + fd->write_check ("keyboard start"); + fd->write (&BX_KEY_THIS s, sizeof (BX_KEY_THIS s)); + fd->write_check ("keyboard end"); + return(0); +} + + + int +bx_keyb_c::LoadState( class state_file *fd ) +{ + fd->read_check ("keyboard start"); + fd->read (&BX_KEY_THIS s, sizeof (BX_KEY_THIS s)); + fd->read_check ("keyboard end"); + return(0); +} + diff --git a/tools/ioemu/iodev/keyboard.h b/tools/ioemu/iodev/keyboard.h new file mode 100644 index 0000000000..24d9ccfdf0 --- /dev/null +++ b/tools/ioemu/iodev/keyboard.h @@ -0,0 +1,234 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: keyboard.h,v 1.22 2003/07/13 19:51:21 vruppert 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 + + +#ifndef _PCKEY_H +#define _PCKEY_H + + +#define BX_KBD_ELEMENTS 16 +#define BX_MOUSE_BUFF_SIZE 48 + +// these keywords should only be used in keyboard.cc +#if BX_USE_KEY_SMF +# define BX_KEY_SMF static +# define BX_KEY_THIS theKeyboard-> +#else +# define BX_KEY_SMF +# define BX_KEY_THIS +#endif + +#define MOUSE_MODE_RESET 10 +#define MOUSE_MODE_STREAM 11 +#define MOUSE_MODE_REMOTE 12 +#define MOUSE_MODE_WRAP 13 + +class bx_keyb_c : public bx_keyb_stub_c { +public: + bx_keyb_c(void); + ~bx_keyb_c(void); + // implement bx_devmodel_c interface + virtual void init(void); + virtual void reset(unsigned type); + // override stubs from bx_keyb_stub_c + virtual void gen_scancode(Bit32u key); + virtual void paste_bytes(Bit8u *data, Bit32s length); + virtual void mouse_motion(int delta_x, int delta_y, unsigned button_state); + + // update the paste delay based on bx_options.Okeyboard_paste_delay + virtual void paste_delay_changed (); + virtual void mouse_enabled_changed(bool enabled); + +private: + BX_KEY_SMF Bit8u get_kbd_enable(void); + BX_KEY_SMF void service_paste_buf (); + BX_KEY_SMF void create_mouse_packet(bool force_enq); + BX_KEY_SMF void mouse_button(unsigned mouse_state); + BX_KEY_SMF int SaveState( class state_file *fd ); + BX_KEY_SMF int LoadState( class state_file *fd ); + BX_KEY_SMF unsigned periodic( Bit32u usec_delta ); + + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#if !BX_USE_KEY_SMF + void write(Bit32u address, Bit32u value, unsigned io_len); + Bit32u read(Bit32u address, unsigned io_len); +#endif + + struct { + struct { + /* status bits matching the status port*/ + bx_bool pare; // Bit7, 1= parity error from keyboard/mouse - ignored. + bx_bool tim; // Bit6, 1= timeout from keyboard - ignored. + bx_bool auxb; // Bit5, 1= mouse data waiting for CPU to read. + bx_bool keyl; // Bit4, 1= keyswitch in lock position - ignored. + bx_bool c_d; /* Bit3, 1=command to port 64h, 0=data to port 60h */ + bx_bool sysf; // Bit2, + bx_bool inpb; // Bit1, + bx_bool outb; // Bit0, 1= keyboard data or mouse data ready for CPU + // check aux to see which. Or just keyboard + // data before AT style machines + + /* internal to our version of the keyboard controller */ + bx_bool kbd_clock_enabled; + bx_bool aux_clock_enabled; + bx_bool allow_irq1; + bx_bool allow_irq12; + Bit8u kbd_output_buffer; + Bit8u aux_output_buffer; + Bit8u last_comm; + Bit8u expecting_port60h; + Bit8u expecting_mouse_parameter; + Bit8u last_mouse_command; + Bit32u timer_pending; + bx_bool irq1_requested; + bx_bool irq12_requested; + bx_bool scancodes_translate; + bx_bool expecting_scancodes_set; + Bit8u current_scancodes_set; + } kbd_controller; + + struct mouseStruct { + Bit8u sample_rate; + Bit8u resolution_cpmm; // resolution in counts per mm + Bit8u scaling; + Bit8u mode; + Bit8u saved_mode; // the mode prior to entering wrap mode + bx_bool enable; + + Bit8u get_status_byte () + { + // top bit is 0 , bit 6 is 1 if remote mode. + Bit8u ret = (Bit8u) ((mode == MOUSE_MODE_REMOTE) ? 0x40 : 0); + ret |= (enable << 5); + ret |= (scaling == 1) ? 0 : (1 << 4); + ret |= ((button_status & 0x1) << 2); + ret |= ((button_status & 0x2) << 0); + return ret; + } + + Bit8u get_resolution_byte () + { + Bit8u ret = 0; + + switch (resolution_cpmm) { + case 1: + ret = 0; + break; + + case 2: + ret = 1; + break; + + case 4: + ret = 2; + break; + + case 8: + ret = 3; + break; + + default: + genlog->panic("mouse: invalid resolution_cpmm"); + }; + return ret; + } + + Bit8u button_status; + Bit16s delayed_dx; + Bit16s delayed_dy; + } mouse; + + struct { + int num_elements; + Bit8u buffer[BX_KBD_ELEMENTS]; + int head; + bx_bool expecting_typematic; + bx_bool expecting_led_write; + Bit8u delay; + Bit8u repeat_rate; + Bit8u led_status; + bx_bool scanning_enabled; + } kbd_internal_buffer; + + struct { + int num_elements; + Bit8u buffer[BX_MOUSE_BUFF_SIZE]; + int head; + } mouse_internal_buffer; +#define BX_KBD_CONTROLLER_QSIZE 5 + Bit8u controller_Q[BX_KBD_CONTROLLER_QSIZE]; + unsigned controller_Qsize; + unsigned controller_Qsource; // 0=keyboard, 1=mouse + } s; // State information for saving/loading + + // The paste buffer does NOT exist in the hardware. It is a bochs + // construction that allows the user to "paste" arbitrary length sequences of + // keystrokes into the emulated machine. Since the hardware buffer is only + // 16 bytes, a very small amount of data can be added to the hardware buffer + // at a time. The paste buffer keeps track of the bytes that have not yet + // been pasted. + // + // Lifetime of a paste buffer: The paste data comes from the system + // clipboard, which must be accessed using platform independent code in the + // gui. Because every gui has its own way of managing the clipboard memory + // (in X windows, you're supposed to call Xfree for example), in the platform + // specific code we make a copy of the clipboard buffer with + // "new Bit8u[length]". Then the pointer is passed into + // bx_keyb_c::paste_bytes, along with the length. The gui code never touches + // the pastebuf again, and does not free it. The keyboard code is + // responsible for deallocating the paste buffer using delete [] buf. The + // paste buffer is binary data, and it is probably NOT null terminated. + // + // Summary: A paste buffer is allocated (new) in the platform-specific gui + // code, passed to the keyboard model, and is freed (delete[]) when it is no + // longer needed. + Bit8u *pastebuf; // ptr to bytes to be pasted, or NULL if none in progress + Bit32u pastebuf_len; // length of pastebuf + Bit32u pastebuf_ptr; // ptr to next byte to be added to hw buffer + Bit32u pastedelay; // count before paste + bx_bool stop_paste; // stop the current paste operation on hardware reset + + BX_KEY_SMF void resetinternals(bx_bool powerup); + BX_KEY_SMF void set_kbd_clock_enable(Bit8u value) BX_CPP_AttrRegparmN(1); + BX_KEY_SMF void set_aux_clock_enable(Bit8u value); + BX_KEY_SMF void kbd_ctrl_to_kbd(Bit8u value); + BX_KEY_SMF void kbd_ctrl_to_mouse(Bit8u value); + BX_KEY_SMF void kbd_enQ(Bit8u scancode); + BX_KEY_SMF void kbd_enQ_imm(Bit8u val); + BX_KEY_SMF void activate_timer(void); + BX_KEY_SMF void controller_enQ(Bit8u data, unsigned source); + BX_KEY_SMF bx_bool mouse_enQ_packet(Bit8u b1, Bit8u b2, Bit8u b3) BX_CPP_AttrRegparmN(3); + BX_KEY_SMF void mouse_enQ(Bit8u mouse_data); + + static void timer_handler(void *); + void timer(void); + int timer_handle; + }; + + +#endif // #ifndef _PCKEY_H diff --git a/tools/ioemu/iodev/load32bitOShack.cc b/tools/ioemu/iodev/load32bitOShack.cc new file mode 100644 index 0000000000..6a0a4904d8 --- /dev/null +++ b/tools/ioemu/iodev/load32bitOShack.cc @@ -0,0 +1,322 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: load32bitOShack.cc,v 1.14 2003/08/08 00:05:53 cbothamy 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 + + + + +#include "bochs.h" +#define LOG_THIS genlog-> + + + +static void bx_load_linux_hack(void); +static void bx_load_null_kernel_hack(void); +static Bit32u bx_load_kernel_image(char *path, Bit32u paddr); + + void +bx_load32bitOSimagehack(void) +{ + // Replay IO from log to initialize IO devices to + // a reasonable state needed for the OS. This is done + // in lieu of running the 16-bit BIOS to init things, + // since we want to test straight 32bit stuff for + // freemware. + +#ifndef BX_USE_VMX + FILE *fp; + + fp = fopen(bx_options.load32bitOSImage.Oiolog->getptr (), "r"); + + if (fp == NULL) { + BX_PANIC(("could not open IO init file.")); + } + + while (1) { + unsigned len, op, port, val; + int ret; + ret = fscanf(fp, "%u %u %x %x\n", + &len, &op, &port, &val); + if (ret != 4) { + BX_PANIC(("could not open IO init file.")); + } + if (op == 0) { + // read + (void) bx_devices.inp(port, len); + } + else if (op == 1) { + // write + bx_devices.outp(port, val, len); + } + else { + BX_PANIC(("bad IO op in init filen")); + } + if (feof(fp)) break; + } +#endif + + // Invoke proper hack depending on which OS image we're loading + switch (bx_options.load32bitOSImage.OwhichOS->get ()) { + case Load32bitOSLinux: + bx_load_linux_hack(); + break; + case Load32bitOSNullKernel: + bx_load_null_kernel_hack(); + break; + default: + BX_PANIC(("load32bitOSImage: OS not recognized")); + } +} + +struct gdt_entry +{ + Bit32u low; + Bit32u high; +}; +struct linux_setup_params +{ + /* 0x000 */ Bit8u orig_x; + /* 0x001 */ Bit8u orig_y; + /* 0x002 */ Bit16u memory_size_std; + /* 0x004 */ Bit16u orig_video_page; + /* 0x006 */ Bit8u orig_video_mode; + /* 0x007 */ Bit8u orig_video_cols; + /* 0x008 */ Bit16u unused1; + /* 0x00a */ Bit16u orig_video_ega_bx; + /* 0x00c */ Bit16u unused2; + /* 0x00e */ Bit8u orig_video_lines; + /* 0x00f */ Bit8u orig_video_isVGA; + /* 0x010 */ Bit16u orig_video_points; + /* 0x012 */ Bit8u pad1[0x40 - 0x12]; + /* 0x040 */ Bit8u apm_info[0x80 - 0x40]; + /* 0x080 */ Bit8u hd0_info[16]; + /* 0x090 */ Bit8u hd1_info[16]; + /* 0x0a0 */ Bit8u pad2[0x1e0 - 0xa0]; + /* 0x1e0 */ Bit32u memory_size_ext; + /* 0x1e4 */ Bit8u pad3[0x1f1 - 0x1e4]; + /* 0x1f1 */ Bit8u setup_sects; + /* 0x1f2 */ Bit16u mount_root_rdonly; + /* 0x1f4 */ Bit16u sys_size; + /* 0x1f6 */ Bit16u swap_dev; + /* 0x1f8 */ Bit16u ramdisk_flags; + /* 0x1fa */ Bit16u vga_mode; + /* 0x1fc */ Bit16u orig_root_dev; + /* 0x1fe */ Bit16u bootsect_magic; + /* 0x200 */ Bit8u pad4[0x210 - 0x200]; + /* 0x210 */ Bit32u loader_type; + /* 0x214 */ Bit32u kernel_start; + /* 0x218 */ Bit32u initrd_start; + /* 0x21c */ Bit32u initrd_size; + /* 0x220 */ Bit8u pad5[0x400 - 0x220]; + /* 0x400 */ struct gdt_entry gdt[128]; + /* 0x800 */ Bit8u commandline[2048]; +}; + + static void +bx_load_linux_setup_params( Bit32u initrd_start, Bit32u initrd_size ) +{ + BX_MEM_C *mem = BX_MEM(0); + struct linux_setup_params *params = + (struct linux_setup_params *) &mem->vector[0x00090000]; + + memset( params, '\0', sizeof(*params) ); + + /* Video settings (standard VGA) */ + params->orig_x = 0; + params->orig_y = 0; + params->orig_video_page = 0; + params->orig_video_mode = 3; + params->orig_video_cols = 80; + params->orig_video_lines = 25; + params->orig_video_points = 16; + params->orig_video_isVGA = 1; + params->orig_video_ega_bx = 3; + + /* Memory size (total mem - 1MB, in KB) */ + params->memory_size_ext = (mem->megabytes - 1) * 1024; + + /* Boot parameters */ + params->loader_type = 1; + params->bootsect_magic = 0xaa55; + params->mount_root_rdonly = 0; + params->orig_root_dev = 0x0100; + params->initrd_start = initrd_start; + params->initrd_size = initrd_size; + + /* Initial GDT */ + params->gdt[2].high = 0x00cf9a00; + params->gdt[2].low = 0x0000ffff; + params->gdt[3].high = 0x00cf9200; + params->gdt[3].low = 0x0000ffff; +} + + void +bx_load_linux_hack(void) +{ +#ifndef BX_USE_VMX + Bit32u initrd_start = 0, initrd_size = 0; + + // The RESET function will have been called first. + // Set CPU and memory features which are assumed at this point. + + // Load Linux kernel image + bx_load_kernel_image( bx_options.load32bitOSImage.Opath->getptr (), 0x100000 ); + + // Load initial ramdisk image if requested + if ( bx_options.load32bitOSImage.Oinitrd->getptr () ) + { + initrd_start = 0x00800000; /* FIXME: load at top of memory */ + initrd_size = bx_load_kernel_image( bx_options.load32bitOSImage.Oinitrd->getptr (), initrd_start ); + } + + // Setup Linux startup parameters buffer + bx_load_linux_setup_params( initrd_start, initrd_size ); +#endif + + // Enable A20 line + BX_SET_ENABLE_A20( 1 ); + + // Setup PICs the way Linux likes it + BX_OUTP( 0x20, 0x11, 1 ); + BX_OUTP( 0xA0, 0x11, 1 ); + BX_OUTP( 0x21, 0x20, 1 ); + BX_OUTP( 0xA1, 0x28, 1 ); + BX_OUTP( 0x21, 0x04, 1 ); + BX_OUTP( 0xA1, 0x02, 1 ); + BX_OUTP( 0x21, 0x01, 1 ); + BX_OUTP( 0xA1, 0x01, 1 ); + BX_OUTP( 0x21, 0xFF, 1 ); + BX_OUTP( 0xA1, 0xFB, 1 ); + +#ifndef BX_USE_VMX + // Disable interrupts and NMIs + BX_CPU(0)->clear_IF (); +#endif + + BX_OUTP( 0x70, 0x80, 1 ); + +#ifndef BX_USE_VMX + // Enter protected mode + // Fixed by george (kyriazis at nvidia.com) + // BX_CPU(0)->cr0.pe = 1; + // BX_CPU(0)->cr0.val32 |= 0x01; + + BX_CPU(0)->SetCR0(BX_CPU(0)->cr0.val32 | 0x01); + + // load esi with real_mode + BX_CPU(0)->gen_reg[BX_32BIT_REG_ESI].dword.erx = 0x90000; + + // Set up initial GDT + BX_CPU(0)->gdtr.limit = 0x400; + BX_CPU(0)->gdtr.base = 0x00090400; + + // Jump to protected mode entry point + BX_CPU(0)->jump_protected( NULL, 0x10, 0x00100000 ); +#endif +} + + void +bx_load_null_kernel_hack(void) +{ +#ifndef BX_USE_VMX + // The RESET function will have been called first. + // Set CPU and memory features which are assumed at this point. + + bx_load_kernel_image(bx_options.load32bitOSImage.Opath->getptr (), 0x100000); + + // EIP deltas + BX_CPU(0)->prev_eip = + BX_CPU(0)->dword.eip = 0x00100000; + + // CS deltas + BX_CPU(0)->sregs[BX_SEG_REG_CS].cache.u.segment.base = 0x00000000; + BX_CPU(0)->sregs[BX_SEG_REG_CS].cache.u.segment.limit = 0xFFFFF; + BX_CPU(0)->sregs[BX_SEG_REG_CS].cache.u.segment.limit_scaled = 0xFFFFFFFF; + BX_CPU(0)->sregs[BX_SEG_REG_CS].cache.u.segment.g = 1; // page gran + BX_CPU(0)->sregs[BX_SEG_REG_CS].cache.u.segment.d_b = 1; // 32bit + + // DS deltas + BX_CPU(0)->sregs[BX_SEG_REG_DS].cache.u.segment.base = 0x00000000; + BX_CPU(0)->sregs[BX_SEG_REG_DS].cache.u.segment.limit = 0xFFFFF; + BX_CPU(0)->sregs[BX_SEG_REG_DS].cache.u.segment.limit_scaled = 0xFFFFFFFF; + BX_CPU(0)->sregs[BX_SEG_REG_DS].cache.u.segment.g = 1; // page gran + BX_CPU(0)->sregs[BX_SEG_REG_DS].cache.u.segment.d_b = 1; // 32bit + + // CR0 deltas + BX_CPU(0)->cr0.pe = 1; // protected mode +#endif // BX_USE_VMX +} + + Bit32u +bx_load_kernel_image(char *path, Bit32u paddr) +{ + struct stat stat_buf; + int fd, ret; + unsigned long size, offset; + Bit32u page_size; + + // read in ROM BIOS image file + fd = open(path, O_RDONLY +#ifdef O_BINARY + | O_BINARY +#endif + ); + if (fd < 0) { + BX_INFO(( "load_kernel_image: couldn't open image file '%s'.", path )); + BX_EXIT(1); + } + ret = fstat(fd, &stat_buf); + if (ret) { + BX_INFO(( "load_kernel_image: couldn't stat image file '%s'.", path )); + BX_EXIT(1); + } + + size = stat_buf.st_size; + page_size = ((Bit32u)size + 0xfff) & ~0xfff; + + BX_MEM_C *mem = BX_MEM(0); + if ( (paddr + size) > mem->len ) { + BX_INFO(( "load_kernel_image: address range > physical memsize!" )); + BX_EXIT(1); + } + + offset = 0; + while (size > 0) { + ret = read(fd, (bx_ptr_t) &mem->vector[paddr + offset], size); + if (ret <= 0) { + BX_INFO(( "load_kernel_image: read failed on image" )); + BX_EXIT(1); + } + size -= ret; + offset += ret; + } + close(fd); + BX_INFO(( "#(%u) load_kernel_image: '%s', size=%u read into memory at %08x", + BX_SIM_ID, path, + (unsigned) stat_buf.st_size, + (unsigned) paddr )); + + return page_size; +} diff --git a/tools/ioemu/iodev/logio.cc b/tools/ioemu/iodev/logio.cc new file mode 100644 index 0000000000..2b79719a2c --- /dev/null +++ b/tools/ioemu/iodev/logio.cc @@ -0,0 +1,631 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: logio.cc,v 1.42 2003/08/24 10:30:07 cbothamy 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 + + + +#include "bochs.h" +#include <assert.h> +#include "state_file.h" + +#if BX_WITH_CARBON +#include <Carbon/Carbon.h> +#endif + +// Just for the iofunctions + + +int Allocio=0; + +void +iofunctions::flush(void) { + if(logfd && magic == MAGIC_LOGNUM) { + fflush(logfd); + } +} + +void +iofunctions::init(void) { + // iofunctions methods must not be called before this magic + // number is set. + magic=MAGIC_LOGNUM; + + // sets the default logprefix + strcpy(logprefix,"%t%e%d"); + n_logfn = 0; + init_log(stderr); + log = new logfunc_t(this); + log->put("IO"); + log->settype(IOLOG); + log->ldebug ("Init(log file: '%s').",logfn); +} + +void +iofunctions::add_logfn (logfunc_t *fn) +{ + assert (n_logfn < MAX_LOGFNS); + logfn_list[n_logfn++] = fn; +} + +void +iofunctions::set_log_action (int loglevel, int action) +{ + for (int i=0; i<n_logfn; i++) + logfn_list[i]->setonoff(loglevel, action); +} + +void +iofunctions::init_log(const char *fn) +{ + assert (magic==MAGIC_LOGNUM); + // use newfd/newfn so that we can log the message to the OLD log + // file descriptor. + FILE *newfd = stderr; + char *newfn = "/dev/stderr"; + if( strcmp( fn, "-" ) != 0 ) { + newfd = fopen(fn, "w"); + if(newfd != NULL) { + newfn = strdup(fn); + log->ldebug ("Opened log file '%s'.", fn ); + } else { + // in constructor, genlog might not exist yet, so do it the safe way. + log->error("Couldn't open log file: %s, using stderr instead", fn); + newfd = stderr; + } + } + logfd = newfd; + logfn = newfn; +} + +void +iofunctions::init_log(FILE *fs) +{ + assert (magic==MAGIC_LOGNUM); + logfd = fs; + + if(fs == stderr) { + logfn = "/dev/stderr"; + } else if(fs == stdout) { + logfn = "/dev/stdout"; + } else { + logfn = "(unknown)"; + } + +} + +void +iofunctions::init_log(int fd) +{ + assert (magic==MAGIC_LOGNUM); + FILE *tmpfd; + if( (tmpfd = fdopen(fd,"w")) == NULL ) { + log->panic("Couldn't open fd %d as a stream for writing", fd); + return; + } + + init_log(tmpfd); + return; +}; + +// all other functions may use genlog safely. +#define LOG_THIS genlog-> + +// This converts the option string to a printf style string with the following args: +// 1. timer, 2. event, 3. cpu0 eip, 4. device +void +iofunctions::set_log_prefix(const char* prefix) { + + strcpy(logprefix,prefix); +} + +// iofunctions::out( class, level, prefix, fmt, ap) +// DO NOT nest out() from ::info() and the like. +// fmt and ap retained for direct printinf from iofunctions only! + +void +iofunctions::out(int f, int l, const char *prefix, const char *fmt, va_list ap) +{ + char c=' ', *s; + assert (magic==MAGIC_LOGNUM); + assert (this != NULL); + assert (logfd != NULL); + + //if( showtick ) + // fprintf(logfd, "%011lld", bx_pc_system.time_ticks()); + + switch(l) { + case LOGLEV_INFO: c='i'; break; + case LOGLEV_PANIC: c='p'; break; + case LOGLEV_PASS: c='s'; break; + case LOGLEV_ERROR: c='e'; break; + case LOGLEV_DEBUG: c='d'; break; + default: break; + } + //fprintf(logfd, "-%c",c); + + //if(prefix != NULL) + // fprintf(logfd, "%s ", prefix); + + s=logprefix; + while(*s) { + switch(*s) { + case '%': + if(*(s+1))s++; + else break; + switch(*s) { + case 'd': + fprintf(logfd, "%s", prefix==NULL?"":prefix); + break; + case 't': + fprintf(logfd, "%011lld", bx_pc_system.time_ticks()); + break; +#ifndef BX_USE_VMX + case 'i': + fprintf(logfd, "%08x", BX_CPU(0)==NULL?0:BX_CPU(0)->dword.eip); + break; +#endif + case 'e': + fprintf(logfd, "%c", c); + break; + case '%': + fprintf(logfd,"%%"); + break; + default: + fprintf(logfd,"%%%c",*s); + } + break; + default : + fprintf(logfd,"%c",*s); + } + s++; + } + + fprintf(logfd," "); + + if(l==LOGLEV_PANIC) + fprintf(logfd, ">>PANIC<< "); + if(l==LOGLEV_PASS) + fprintf(logfd, ">>PASS<< "); + + vfprintf(logfd, fmt, ap); + fprintf(logfd, "\n"); + fflush(logfd); + + return; +} + +iofunctions::iofunctions(FILE *fs) +{ + init(); + init_log(fs); +} + +iofunctions::iofunctions(const char *fn) +{ + init(); + init_log(fn); +} + +iofunctions::iofunctions(int fd) +{ + init(); + init_log(fd); +} + +iofunctions::iofunctions(void) +{ + this->init(); +} + +iofunctions::~iofunctions(void) +{ + // flush before erasing magic number, or flush does nothing. + this->flush(); + this->magic=0; +} + +#define LOG_THIS genlog-> + +int logfunctions::default_onoff[N_LOGLEV] = { + ACT_IGNORE, // ignore debug + ACT_REPORT, // report info + ACT_REPORT, // report error +#if BX_WITH_WX + ACT_ASK, // on panic, ask user what to do +#else + ACT_FATAL, // on panic, quit +#endif + ACT_FATAL +}; + +logfunctions::logfunctions(void) +{ + prefix = NULL; + put(" "); + settype(GENLOG); + if (io == NULL && Allocio == 0) { + Allocio = 1; + io = new iofunc_t(stderr); + } + setio(io); + // BUG: unfortunately this can be called before the bochsrc is read, + // which means that the bochsrc has no effect on the actions. + for (int i=0; i<N_LOGLEV; i++) + onoff[i] = get_default_action(i); +} + +logfunctions::logfunctions(iofunc_t *iofunc) +{ + prefix = NULL; + put(" "); + settype(GENLOG); + setio(iofunc); + // BUG: unfortunately this can be called before the bochsrc is read, + // which means that the bochsrc has no effect on the actions. + for (int i=0; i<N_LOGLEV; i++) + onoff[i] = get_default_action(i); +} + +logfunctions::~logfunctions(void) +{ + if ( this->prefix ) + { + free(this->prefix); + this->prefix = NULL; + } +} + +void +logfunctions::setio(iofunc_t *i) +{ + // add pointer to iofunction object to use + this->logio = i; + // give iofunction a pointer to me + i->add_logfn (this); +} + +void +logfunctions::put(char *p) +{ + char *tmpbuf; + tmpbuf=strdup("[ ]");// if we ever have more than 32 chars, + // we need to rethink this + + if ( tmpbuf == NULL) + { + return ; /* allocation not successful */ + } + if ( this->prefix != NULL ) + { + free(this->prefix); /* free previously allocated memory */ + this->prefix = NULL; + } + int len=strlen(p); + for(int i=1;i<len+1;i++) { + tmpbuf[i]=p[i-1]; + } + + switch(len) { + case 1: tmpbuf[2]=' '; + case 2: tmpbuf[3]=' '; + case 3: tmpbuf[4]=' '; + case 4: tmpbuf[5]=' '; + default: tmpbuf[6]=']'; tmpbuf[7]='\0'; break; + } + + this->prefix=tmpbuf; +} + +void +logfunctions::settype(int t) +{ + type=t; +} + +void +logfunctions::info(const char *fmt, ...) +{ + va_list ap; + + assert (this != NULL); + assert (this->logio != NULL); + + if(!onoff[LOGLEV_INFO]) return; + + va_start(ap, fmt); + this->logio->out(this->type,LOGLEV_INFO,this->prefix, fmt, ap); + if (onoff[LOGLEV_INFO] == ACT_ASK) + ask (LOGLEV_INFO, this->prefix, fmt, ap); + if (onoff[LOGLEV_INFO] == ACT_FATAL) + fatal (this->prefix, fmt, ap, 1); + va_end(ap); + +} + +void +logfunctions::error(const char *fmt, ...) +{ + va_list ap; + + assert (this != NULL); + assert (this->logio != NULL); + + if(!onoff[LOGLEV_ERROR]) return; + + va_start(ap, fmt); + this->logio->out(this->type,LOGLEV_ERROR,this->prefix, fmt, ap); + if (onoff[LOGLEV_ERROR] == ACT_ASK) + ask (LOGLEV_ERROR, this->prefix, fmt, ap); + if (onoff[LOGLEV_ERROR] == ACT_FATAL) + fatal (this->prefix, fmt, ap, 1); + va_end(ap); +} + +void +logfunctions::panic(const char *fmt, ...) +{ + va_list ap; + + assert (this != NULL); + assert (this->logio != NULL); + + // Special case for panics since they are so important. Always print + // the panic to the log, no matter what the log action says. + //if(!onoff[LOGLEV_PANIC]) return; + + va_start(ap, fmt); + this->logio->out(this->type,LOGLEV_PANIC,this->prefix, fmt, ap); + + // This fixes a funny bug on linuxppc where va_list is no pointer but a struct + va_end(ap); + va_start(ap, fmt); + + if (onoff[LOGLEV_PANIC] == ACT_ASK) + ask (LOGLEV_PANIC, this->prefix, fmt, ap); + if (onoff[LOGLEV_PANIC] == ACT_FATAL) + fatal (this->prefix, fmt, ap, 1); + va_end(ap); +} + +void +logfunctions::pass(const char *fmt, ...) +{ + va_list ap; + + assert (this != NULL); + assert (this->logio != NULL); + + // Special case for panics since they are so important. Always print + // the panic to the log, no matter what the log action says. + //if(!onoff[LOGLEV_PASS]) return; + + va_start(ap, fmt); + this->logio->out(this->type,LOGLEV_PASS,this->prefix, fmt, ap); + + // This fixes a funny bug on linuxppc where va_list is no pointer but a struct + va_end(ap); + va_start(ap, fmt); + + if (onoff[LOGLEV_PASS] == ACT_ASK) + ask (LOGLEV_PASS, this->prefix, fmt, ap); + if (onoff[LOGLEV_PASS] == ACT_FATAL) + fatal (this->prefix, fmt, ap, 101); + va_end(ap); +} + +void +logfunctions::ldebug(const char *fmt, ...) +{ + va_list ap; + + assert (this != NULL); + assert (this->logio != NULL); + + if(!onoff[LOGLEV_DEBUG]) return; + + va_start(ap, fmt); + this->logio->out(this->type,LOGLEV_DEBUG,this->prefix, fmt, ap); + if (onoff[LOGLEV_DEBUG] == ACT_ASK) + ask (LOGLEV_DEBUG, this->prefix, fmt, ap); + if (onoff[LOGLEV_DEBUG] == ACT_FATAL) + fatal (this->prefix, fmt, ap, 1); + va_end(ap); +} + +void +logfunctions::ask (int level, const char *prefix, const char *fmt, va_list ap) +{ + // Guard against reentry on ask() function. The danger is that some + // function that's called within ask() could trigger another + // BX_PANIC that could call ask() again, leading to infinite + // recursion and infinite asks. + static char in_ask_already = 0; + char buf1[1024]; + if (in_ask_already) { + fprintf (stderr, "logfunctions::ask() should not reenter!!\n"); + return; + } + in_ask_already = 1; + vsprintf (buf1, fmt, ap); + // FIXME: facility set to 0 because it's unknown. + + // update vga screen. This is useful because sometimes useful messages + // are printed on the screen just before a panic. It's also potentially + // dangerous if this function calls ask again... That's why I added + // the reentry check above. + if (SIM->get_init_done()) DEV_vga_refresh(); + +#if !BX_EXTERNAL_DEBUGGER + // ensure the text screen is showing + SIM->set_display_mode (DISP_MODE_CONFIG); + int val = SIM->log_msg (prefix, level, buf1); + switch (val) + { + case BX_LOG_ASK_CHOICE_CONTINUE: + break; + case BX_LOG_ASK_CHOICE_CONTINUE_ALWAYS: + // user said continue, and don't "ask" for this facility again. + setonoff (level, ACT_REPORT); + break; + case BX_LOG_ASK_CHOICE_DIE: + bx_user_quit = 1; + in_ask_already = 0; // because fatal will longjmp out + fatal (prefix, fmt, ap, 1); + // should never get here + BX_PANIC (("in ask(), fatal() should never return!")); + break; + case BX_LOG_ASK_CHOICE_DUMP_CORE: + fprintf (stderr, "User chose to dump core...\n"); +#if BX_HAVE_ABORT + abort (); +#else + // do something highly illegal that should kill the process. + // Hey, this is fun! + { + char *crashptr = (char *)0; char c = *crashptr; + } + fprintf (stderr, "Sorry, I couldn't find your abort() function. Exiting."); + exit (0); +#endif +#if BX_DEBUGGER + case BX_LOG_ASK_CHOICE_ENTER_DEBUG: + // user chose debugger. To "drop into the debugger" we just set the + // interrupt_requested bit and continue execution. Before the next + // instruction, it should notice the user interrupt and return to + // the debugger. + bx_guard.interrupt_requested = 1; + break; +#endif + default: + // this happens if panics happen before the callback is initialized + // in gui/control.cc. + fprintf (stderr, "WARNING: log_msg returned unexpected value %d\n", val); + } +#else + // external debugger ask code goes here +#endif + // return to simulation mode + SIM->set_display_mode (DISP_MODE_SIM); + in_ask_already = 0; +} + +#if BX_WITH_CARBON +/* Panic button to display fatal errors. + Completely self contained, can't rely on carbon.cc being available */ +static void carbonFatalDialog(const char *error, const char *exposition) +{ + DialogRef alertDialog; + CFStringRef cfError; + CFStringRef cfExposition; + DialogItemIndex index; + AlertStdCFStringAlertParamRec alertParam = {0}; + + // Init libraries + InitCursor(); + // Assemble dialog + cfError = CFStringCreateWithCString(NULL, error, kCFStringEncodingASCII); + if(exposition != NULL) + { + cfExposition = CFStringCreateWithCString(NULL, exposition, kCFStringEncodingASCII); + } + else { cfExposition = NULL; } + alertParam.version = kStdCFStringAlertVersionOne; + alertParam.defaultText = CFSTR("Quit"); + alertParam.position = kWindowDefaultPosition; + alertParam.defaultButton = kAlertStdAlertOKButton; + // Display Dialog + CreateStandardAlert( + kAlertStopAlert, + cfError, + cfExposition, /* can be NULL */ + &alertParam, /* can be NULL */ + &alertDialog); + RunStandardAlert( alertDialog, NULL, &index); + // Cleanup + CFRelease( cfError ); + if( cfExposition != NULL ) { CFRelease( cfExposition ); } +} +#endif + +void +logfunctions::fatal (const char *prefix, const char *fmt, va_list ap, int exit_status) +{ + bx_atexit(); +#if BX_WITH_CARBON + if(!isatty(STDIN_FILENO) && !SIM->get_init_done()) + { + char buf1[1024]; + char buf2[1024]; + vsprintf (buf1, fmt, ap); + sprintf (buf2, "Bochs startup error\n%s", buf1); + carbonFatalDialog(buf2, + "For more information, try running Bochs within Terminal by clicking on \"bochs.scpt\"."); + } +#endif +#if !BX_WITH_WX + static char *divider = "========================================================================"; + fprintf (stderr, "%s\n", divider); + fprintf (stderr, "Bochs is exiting with the following message:\n"); + fprintf (stderr, "%s ", prefix); + vfprintf (stderr, fmt, ap); + fprintf (stderr, "\n%s\n", divider); +#endif +#if 0 && defined(WIN32) +#error disabled because it is not working yet! + // wait for a keypress before quitting. Depending on how bochs is + // installed, the console window can disappear before the user has + // a chance to read the final message. + fprintf (stderr, "\n\nPress Enter to exit...\n"); + char buf[8]; + fgets (buf, 8, stdin); +#endif +#if !BX_DEBUGGER + BX_EXIT(exit_status); +#else + static bx_bool dbg_exit_called = 0; + if (dbg_exit_called == 0) { + dbg_exit_called = 1; + bx_dbg_exit(exit_status); + } +#endif + // not safe to use BX_* log functions in here. + fprintf (stderr, "fatal() should never return, but it just did\n"); +} + +iofunc_t *io = NULL; +logfunc_t *genlog = NULL; + +void bx_center_print (FILE *file, char *line, int maxwidth) +{ + int imax; + int len = strlen(line); + if (len > maxwidth) + BX_PANIC (("bx_center_print: line is too long: '%s'", line)); + imax = (maxwidth - len) >> 1; + for (int i=0; i<imax; i++) fputc (' ', file); + fputs (line, file); +} + + diff --git a/tools/ioemu/iodev/main.cc b/tools/ioemu/iodev/main.cc new file mode 100644 index 0000000000..c784adb555 --- /dev/null +++ b/tools/ioemu/iodev/main.cc @@ -0,0 +1,4067 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: main.cc,v 1.256.2.3 2004/02/08 14:39:50 cbothamy Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 MandrakeSoft S.A. +// Copyright (C) 2004 Arun Sharma <arun.sharma@intel.com> +// Copyright (C) 2004 Intel Corp +// +// 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 + +#include "bochs.h" +#include <assert.h> +#include "state_file.h" + +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif + +extern "C" { +#include <signal.h> +} + +#if BX_GUI_SIGHANDLER +bx_bool bx_gui_sighandler = 0; +#endif + +// single processor simulation, so there's one of everything +BOCHSAPI BX_CPU_C bx_cpu; +BOCHSAPI BX_MEM_C bx_mem; + +int xc_handle; +int bochsrc_include_count = 0; + +// some prototypes from iodev/ +// I want to stay away from including iodev/iodev.h here +Bit32u bx_unmapped_io_read_handler(Bit32u address, unsigned io_len); +void bx_unmapped_io_write_handler(Bit32u address, Bit32u value, + unsigned io_len); +void bx_close_harddrive(void); + + +void bx_init_bx_dbg (void); +static char *divider = "========================================================================"; +static logfunctions thePluginLog; +logfunctions *pluginlog = &thePluginLog; + +bx_startup_flags_t bx_startup_flags; +bx_bool bx_user_quit; + +/* typedefs */ + +#define LOG_THIS genlog-> + +#if ( BX_PROVIDE_DEVICE_MODELS==1 ) +bx_pc_system_c bx_pc_system; +class state_file state_stuff("state_file.out", "options"); +#endif + +bx_debug_t bx_dbg; + +bx_options_t bx_options; // initialized in bx_init_options() +char *bochsrc_filename = NULL; + +static Bit32s parse_line_unformatted(char *context, char *line); +static Bit32s parse_line_formatted(char *context, int num_params, char *params[]); +static int parse_bochsrc(char *rcfile); + +static Bit64s +bx_param_handler (bx_param_c *param, int set, Bit64s val) +{ + bx_id id = param->get_id (); + switch (id) { + case BXP_VGA_UPDATE_INTERVAL: + // if after init, notify the vga device to change its timer. + if (set && SIM->get_init_done ()) + DEV_vga_set_update_interval (val); + break; + case BXP_MOUSE_ENABLED: + // if after init, notify the GUI + if (set && SIM->get_init_done ()) { + bx_gui->mouse_enabled_changed (val!=0); + DEV_mouse_enabled_changed (val!=0); + } + break; + case BXP_NE2K_PRESENT: + if (set) { + int enable = (val != 0); + SIM->get_param (BXP_NE2K_IOADDR)->set_enabled (enable); + SIM->get_param (BXP_NE2K_IRQ)->set_enabled (enable); + SIM->get_param (BXP_NE2K_MACADDR)->set_enabled (enable); + SIM->get_param (BXP_NE2K_ETHMOD)->set_enabled (enable); + SIM->get_param (BXP_NE2K_ETHDEV)->set_enabled (enable); + SIM->get_param (BXP_NE2K_SCRIPT)->set_enabled (enable); + } + break; + case BXP_LOAD32BITOS_WHICH: + if (set) { + int enable = (val != Load32bitOSNone); + SIM->get_param (BXP_LOAD32BITOS_PATH)->set_enabled (enable); + SIM->get_param (BXP_LOAD32BITOS_IOLOG)->set_enabled (enable); + SIM->get_param (BXP_LOAD32BITOS_INITRD)->set_enabled (enable); + } + break; + case BXP_ATA0_MASTER_STATUS: + case BXP_ATA0_SLAVE_STATUS: + case BXP_ATA1_MASTER_STATUS: + case BXP_ATA1_SLAVE_STATUS: + case BXP_ATA2_MASTER_STATUS: + case BXP_ATA2_SLAVE_STATUS: + case BXP_ATA3_MASTER_STATUS: + case BXP_ATA3_SLAVE_STATUS: + if ((set) && (SIM->get_init_done ())) { + Bit8u device = id - BXP_ATA0_MASTER_STATUS; + Bit32u handle = DEV_hd_get_device_handle (device/2, device%2); + DEV_hd_set_cd_media_status(handle, val == BX_INSERTED); + bx_gui->update_drive_status_buttons (); + } + break; + case BXP_FLOPPYA_TYPE: + if ((set) && (!SIM->get_init_done ())) { + bx_options.floppya.Odevtype->set (val); + } + break; + case BXP_FLOPPYA_STATUS: + if ((set) && (SIM->get_init_done ())) { + DEV_floppy_set_media_status(0, val == BX_INSERTED); + bx_gui->update_drive_status_buttons (); + } + break; + case BXP_FLOPPYB_TYPE: + if ((set) && (!SIM->get_init_done ())) { + bx_options.floppyb.Odevtype->set (val); + } + break; + case BXP_FLOPPYB_STATUS: + if ((set) && (SIM->get_init_done ())) { + DEV_floppy_set_media_status(1, val == BX_INSERTED); + bx_gui->update_drive_status_buttons (); + } + break; + case BXP_KBD_PASTE_DELAY: + if ((set) && (SIM->get_init_done ())) { + DEV_kbd_paste_delay_changed (); + } + break; + + case BXP_ATA0_MASTER_MODE: + case BXP_ATA0_SLAVE_MODE: + case BXP_ATA1_MASTER_MODE: + case BXP_ATA1_SLAVE_MODE: + case BXP_ATA2_MASTER_MODE: + case BXP_ATA2_SLAVE_MODE: + case BXP_ATA3_MASTER_MODE: + case BXP_ATA3_SLAVE_MODE: + if (set) { + int device = id - BXP_ATA0_MASTER_MODE; + switch (val) { + case BX_ATA_MODE_UNDOABLE: + case BX_ATA_MODE_VOLATILE: + //case BX_ATA_MODE_Z_UNDOABLE: + //case BX_ATA_MODE_Z_VOLATILE: + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_JOURNAL + device))->set_enabled (1); + break; + default: + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_JOURNAL + device))->set_enabled (0); + } + } + break; + + case BXP_ATA0_MASTER_TYPE: + case BXP_ATA0_SLAVE_TYPE: + case BXP_ATA1_MASTER_TYPE: + case BXP_ATA1_SLAVE_TYPE: + case BXP_ATA2_MASTER_TYPE: + case BXP_ATA2_SLAVE_TYPE: + case BXP_ATA3_MASTER_TYPE: + case BXP_ATA3_SLAVE_TYPE: + if (set) { + int device = id - BXP_ATA0_MASTER_TYPE; + switch (val) { + case BX_ATA_DEVICE_DISK: + SIM->get_param_num ((bx_id)(BXP_ATA0_MASTER_PRESENT + device))->set (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_MODE + device))->set_enabled (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_PATH + device))->set_enabled (1); + //SIM->get_param ((bx_id)(BXP_ATA0_MASTER_JOURNAL + device))->set_enabled (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_CYLINDERS + device))->set_enabled (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_HEADS + device))->set_enabled (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_SPT + device))->set_enabled (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_STATUS + device))->set_enabled (0); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_MODEL + device))->set_enabled (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_BIOSDETECT + device))->set_enabled (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_TRANSLATION + device))->set_enabled (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_PATH + device))->set_runtime_param (0); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_STATUS + device))->set_runtime_param (0); + break; + case BX_ATA_DEVICE_CDROM: + SIM->get_param_num ((bx_id)(BXP_ATA0_MASTER_PRESENT + device))->set (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_MODE + device))->set_enabled (0); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_PATH + device))->set_enabled (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_JOURNAL + device))->set_enabled (0); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_CYLINDERS + device))->set_enabled (0); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_HEADS + device))->set_enabled (0); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_SPT + device))->set_enabled (0); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_STATUS + device))->set_enabled (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_MODEL + device))->set_enabled (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_BIOSDETECT + device))->set_enabled (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_TRANSLATION + device))->set_enabled (0); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_PATH + device))->set_runtime_param (1); + SIM->get_param ((bx_id)(BXP_ATA0_MASTER_STATUS + device))->set_runtime_param (1); + break; + } + } + break; + default: + BX_PANIC (("bx_param_handler called with unknown id %d", id)); + return -1; + } + return val; +} + +char *bx_param_string_handler (bx_param_string_c *param, int set, char *val, int maxlen) +{ + bx_id id = param->get_id (); + + int empty = 0; + if ((strlen(val) < 1) || !strcmp ("none", val)) { + empty = 1; + val = "none"; + } + switch (id) { + case BXP_FLOPPYA_PATH: + if (set==1) { + if (SIM->get_init_done ()) { + if (empty) { + DEV_floppy_set_media_status(0, 0); + bx_gui->update_drive_status_buttons (); + } else { + if (!SIM->get_param_num(BXP_FLOPPYA_TYPE)->get_enabled()) { + BX_ERROR(("Cannot add a floppy drive at runtime")); + bx_options.floppya.Opath->set ("none"); + } + } + if ((DEV_floppy_present()) && + (SIM->get_param_num(BXP_FLOPPYA_STATUS)->get () == BX_INSERTED)) { + // tell the device model that we removed, then inserted the disk + DEV_floppy_set_media_status(0, 0); + DEV_floppy_set_media_status(0, 1); + } + } else { + SIM->get_param_num(BXP_FLOPPYA_DEVTYPE)->set_enabled (!empty); + SIM->get_param_num(BXP_FLOPPYA_TYPE)->set_enabled (!empty); + SIM->get_param_num(BXP_FLOPPYA_STATUS)->set_enabled (!empty); + } + } + break; + case BXP_FLOPPYB_PATH: + if (set==1) { + if (SIM->get_init_done ()) { + if (empty) { + DEV_floppy_set_media_status(1, 0); + bx_gui->update_drive_status_buttons (); + } else { + if (!SIM->get_param_num(BXP_FLOPPYB_TYPE)->get_enabled ()) { + BX_ERROR(("Cannot add a floppy drive at runtime")); + bx_options.floppyb.Opath->set ("none"); + } + } + if ((DEV_floppy_present()) && + (SIM->get_param_num(BXP_FLOPPYB_STATUS)->get () == BX_INSERTED)) { + // tell the device model that we removed, then inserted the disk + DEV_floppy_set_media_status(1, 0); + DEV_floppy_set_media_status(1, 1); + } + } else { + SIM->get_param_num(BXP_FLOPPYB_DEVTYPE)->set_enabled (!empty); + SIM->get_param_num(BXP_FLOPPYB_TYPE)->set_enabled (!empty); + SIM->get_param_num(BXP_FLOPPYB_STATUS)->set_enabled (!empty); + } + } + break; + + case BXP_ATA0_MASTER_PATH: + case BXP_ATA0_SLAVE_PATH: + case BXP_ATA1_MASTER_PATH: + case BXP_ATA1_SLAVE_PATH: + case BXP_ATA2_MASTER_PATH: + case BXP_ATA2_SLAVE_PATH: + case BXP_ATA3_MASTER_PATH: + case BXP_ATA3_SLAVE_PATH: + if (set==1) { + if (SIM->get_init_done ()) { + + Bit8u device = id - BXP_ATA0_MASTER_PATH; + Bit32u handle = DEV_hd_get_device_handle(device/2, device%2); + + if (empty) { + DEV_hd_set_cd_media_status(handle, 0); + bx_gui->update_drive_status_buttons (); + } else { + if (!SIM->get_param_num((bx_id)(BXP_ATA0_MASTER_PRESENT + device))->get ()) { + BX_ERROR(("Cannot add a cdrom drive at runtime")); + bx_options.atadevice[device/2][device%2].Opresent->set (0); + } + if (SIM->get_param_num((bx_id)(BXP_ATA0_MASTER_TYPE + device))->get () != BX_ATA_DEVICE_CDROM) { + BX_ERROR(("Device is not a cdrom drive")); + bx_options.atadevice[device/2][device%2].Opresent->set (0); + } + } + if (DEV_hd_present() && + (SIM->get_param_num((bx_id)(BXP_ATA0_MASTER_STATUS + device))->get () == BX_INSERTED) && + (SIM->get_param_num((bx_id)(BXP_ATA0_MASTER_TYPE + device))->get () == BX_ATA_DEVICE_CDROM)) { + // tell the device model that we removed, then inserted the cd + DEV_hd_set_cd_media_status(handle, 0); + DEV_hd_set_cd_media_status(handle, 1); + } + } + } + break; + + case BXP_SCREENMODE: + if (set==1) { + BX_INFO (("Screen mode changed to %s", val)); + } + break; + default: + BX_PANIC (("bx_string_handler called with unexpected parameter %d", param->get_id())); + } + return val; +} + +static int +bx_param_enable_handler (bx_param_c *param, int val) +{ + bx_id id = param->get_id (); + switch (id) { + case BXP_ATA0_MASTER_STATUS: + case BXP_ATA0_SLAVE_STATUS: + case BXP_ATA1_MASTER_STATUS: + case BXP_ATA1_SLAVE_STATUS: + case BXP_ATA2_MASTER_STATUS: + case BXP_ATA2_SLAVE_STATUS: + case BXP_ATA3_MASTER_STATUS: + case BXP_ATA3_SLAVE_STATUS: + if (val != 0) { + Bit8u device = id - BXP_ATA0_MASTER_STATUS; + + switch (SIM->get_param_enum ((bx_id)(BXP_ATA0_MASTER_TYPE + device))->get()) { + case BX_ATA_DEVICE_CDROM: + return (1); + break; + } + } + return (0); + break; + + case BXP_ATA0_MASTER_JOURNAL: + case BXP_ATA0_SLAVE_JOURNAL: + case BXP_ATA1_MASTER_JOURNAL: + case BXP_ATA1_SLAVE_JOURNAL: + case BXP_ATA2_MASTER_JOURNAL: + case BXP_ATA2_SLAVE_JOURNAL: + case BXP_ATA3_MASTER_JOURNAL: + case BXP_ATA3_SLAVE_JOURNAL: + if (val != 0) { + Bit8u device = id - BXP_ATA0_MASTER_JOURNAL; + + switch (SIM->get_param_enum ((bx_id)(BXP_ATA0_MASTER_TYPE + device))->get()) { + case BX_ATA_DEVICE_DISK: + switch (SIM->get_param_enum ((bx_id)(BXP_ATA0_MASTER_MODE + device))->get()) { + case BX_ATA_MODE_UNDOABLE: + case BX_ATA_MODE_VOLATILE: + //case BX_ATA_MODE_Z_UNDOABLE: + //case BX_ATA_MODE_Z_VOLATILE: + return (1); + break; + } + } + } + return (0); + break; + + default: + BX_PANIC (("bx_param_handler called with unknown id %d", id)); + } + return val; +} + + + +void bx_init_options () +{ + int i; + bx_list_c *menu; + bx_list_c *deplist; + char name[1024], descr[1024], label[1024]; + + memset (&bx_options, 0, sizeof(bx_options)); + + // quick start option, set by command line arg + new bx_param_enum_c (BXP_BOCHS_START, + "Bochs start types", + "Bochs start types", + bochs_start_names, + BX_RUN_START, + BX_QUICK_START); + + // floppya + bx_options.floppya.Opath = new bx_param_filename_c (BXP_FLOPPYA_PATH, + "floppya:path", + "Pathname of first floppy image file or device. If you're booting from floppy, this should be a bootable floppy.", + "", BX_PATHNAME_LEN); + bx_options.floppya.Opath->set_ask_format ("Enter new filename, or 'none' for no disk: [%s] "); + bx_options.floppya.Opath->set_label ("First floppy image/device"); + bx_options.floppya.Odevtype = new bx_param_enum_c (BXP_FLOPPYA_DEVTYPE, + "floppya:devtype", + "Type of floppy drive", + floppy_type_names, + BX_FLOPPY_NONE, + BX_FLOPPY_NONE); + bx_options.floppya.Otype = new bx_param_enum_c (BXP_FLOPPYA_TYPE, + "floppya:type", + "Type of floppy disk", + floppy_type_names, + BX_FLOPPY_NONE, + BX_FLOPPY_NONE); + bx_options.floppya.Otype->set_ask_format ("What type of floppy disk? [%s] "); + bx_options.floppya.Ostatus = new bx_param_enum_c (BXP_FLOPPYA_STATUS, + "Is floppya inserted", + "Inserted or ejected", + floppy_status_names, + BX_INSERTED, + BX_EJECTED); + bx_options.floppya.Ostatus->set_ask_format ("Is the floppy inserted or ejected? [%s] "); + bx_options.floppya.Opath->set_format ("%s"); + bx_options.floppya.Otype->set_format ("size=%s"); + bx_options.floppya.Ostatus->set_format ("%s"); + bx_param_c *floppya_init_list[] = { + // if the order "path,type,status" changes, corresponding changes must + // be made in gui/wxmain.cc, MyFrame::editFloppyConfig. + bx_options.floppya.Opath, + bx_options.floppya.Otype, + bx_options.floppya.Ostatus, + NULL + }; + menu = new bx_list_c (BXP_FLOPPYA, "Floppy Disk 0", "All options for first floppy disk", floppya_init_list); + menu->get_options ()->set (menu->SERIES_ASK); + bx_options.floppya.Opath->set_handler (bx_param_string_handler); + bx_options.floppya.Opath->set ("none"); + bx_options.floppya.Otype->set_handler (bx_param_handler); + bx_options.floppya.Ostatus->set_handler (bx_param_handler); + + bx_options.floppyb.Opath = new bx_param_filename_c (BXP_FLOPPYB_PATH, + "floppyb:path", + "Pathname of second floppy image file or device.", + "", BX_PATHNAME_LEN); + bx_options.floppyb.Opath->set_ask_format ("Enter new filename, or 'none' for no disk: [%s] "); + bx_options.floppyb.Opath->set_label ("Second floppy image/device"); + bx_options.floppyb.Odevtype = new bx_param_enum_c (BXP_FLOPPYB_DEVTYPE, + "floppyb:devtype", + "Type of floppy drive", + floppy_type_names, + BX_FLOPPY_NONE, + BX_FLOPPY_NONE); + bx_options.floppyb.Otype = new bx_param_enum_c (BXP_FLOPPYB_TYPE, + "floppyb:type", + "Type of floppy disk", + floppy_type_names, + BX_FLOPPY_NONE, + BX_FLOPPY_NONE); + bx_options.floppyb.Otype->set_ask_format ("What type of floppy disk? [%s] "); + bx_options.floppyb.Ostatus = new bx_param_enum_c (BXP_FLOPPYB_STATUS, + "Is floppyb inserted", + "Inserted or ejected", + floppy_status_names, + BX_INSERTED, + BX_EJECTED); + bx_options.floppyb.Ostatus->set_ask_format ("Is the floppy inserted or ejected? [%s] "); + bx_options.floppyb.Opath->set_format ("%s"); + bx_options.floppyb.Otype->set_format ("size=%s"); + bx_options.floppyb.Ostatus->set_format ("%s"); + bx_param_c *floppyb_init_list[] = { + bx_options.floppyb.Opath, + bx_options.floppyb.Otype, + bx_options.floppyb.Ostatus, + NULL + }; + menu = new bx_list_c (BXP_FLOPPYB, "Floppy Disk 1", "All options for second floppy disk", floppyb_init_list); + menu->get_options ()->set (menu->SERIES_ASK); + bx_options.floppyb.Opath->set_handler (bx_param_string_handler); + bx_options.floppyb.Opath->set ("none"); + bx_options.floppyb.Otype->set_handler (bx_param_handler); + bx_options.floppyb.Ostatus->set_handler (bx_param_handler); + + // disk options + + // FIXME use descr and name + char *s_atachannel[] = { + "ATA channel 0", + "ATA channel 1", + "ATA channel 2", + "ATA channel 3", + }; + char *s_atadevice[4][2] = { + { "First HD/CD on channel 0", + "Second HD/CD on channel 0" }, + { "First HD/CD on channel 1", + "Second HD/CD on channel 1" }, + { "First HD/CD on channel 2", + "Second HD/CD on channel 2" }, + { "First HD/CD on channel 3", + "Second HD/CD on channel 3" } + }; + Bit16u ata_default_ioaddr1[BX_MAX_ATA_CHANNEL] = { + 0x1f0, 0x170, 0x1e8, 0x168 + }; + Bit16u ata_default_ioaddr2[BX_MAX_ATA_CHANNEL] = { + 0x3f0, 0x370, 0x3e0, 0x360 + }; + Bit8u ata_default_irq[BX_MAX_ATA_CHANNEL] = { + 14, 15, 11, 9 + }; + + bx_list_c *ata[BX_MAX_ATA_CHANNEL]; + bx_list_c *ata_menu[BX_MAX_ATA_CHANNEL]; + + Bit8u channel; + for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel ++) { + + ata[channel] = new bx_list_c ((bx_id)(BXP_ATAx(channel)), s_atachannel[channel], s_atachannel[channel], 8); + ata[channel]->get_options ()->set (bx_list_c::SERIES_ASK); + + ata[channel]->add (bx_options.ata[channel].Opresent = new bx_param_bool_c ((bx_id)(BXP_ATAx_PRESENT(channel)), + "ata:present", + "Controls whether ata channel is installed or not", + 0)); + + ata[channel]->add (bx_options.ata[channel].Oioaddr1 = new bx_param_num_c ((bx_id)(BXP_ATAx_IOADDR1(channel)), + "ata:ioaddr1", + "IO adress of ata command block", + 0, 0xffff, + ata_default_ioaddr1[channel])); + + ata[channel]->add (bx_options.ata[channel].Oioaddr2 = new bx_param_num_c ((bx_id)(BXP_ATAx_IOADDR2(channel)), + "ata:ioaddr2", + "IO adress of ata control block", + 0, 0xffff, + ata_default_ioaddr2[channel])); + + ata[channel]->add (bx_options.ata[channel].Oirq = new bx_param_num_c ((bx_id)(BXP_ATAx_IRQ(channel)), + "ata:irq", + "IRQ used by this ata channel", + 0, 15, + ata_default_irq[channel])); + + // all items in the ata[channel] menu depend on the present flag. + // The menu list is complete, but a few dependent_list items will + // be added later. Use clone() to make a copy of the dependent_list + // so that it can be changed without affecting the menu. + bx_options.ata[channel].Opresent->set_dependent_list ( + ata[channel]->clone()); + + for (Bit8u slave=0; slave<2; slave++) { + + menu = bx_options.atadevice[channel][slave].Omenu = new bx_list_c ((bx_id)(BXP_ATAx_DEVICE(channel,slave)), + s_atadevice[channel][slave], + s_atadevice[channel][slave], + BXP_PARAMS_PER_ATA_DEVICE + 1 ); + menu->get_options ()->set (menu->SERIES_ASK); + + menu->add (bx_options.atadevice[channel][slave].Opresent = new bx_param_bool_c ((bx_id)(BXP_ATAx_DEVICE_PRESENT(channel,slave)), + "ata-device:present", + "Controls whether ata device is installed or not", + 0)); + + menu->add (bx_options.atadevice[channel][slave].Otype = new bx_param_enum_c ((bx_id)(BXP_ATAx_DEVICE_TYPE(channel,slave)), + "ata-device:type", + "Type of ATA device (disk or cdrom)", + atadevice_type_names, + BX_ATA_DEVICE_DISK, + BX_ATA_DEVICE_DISK)); + + menu->add (bx_options.atadevice[channel][slave].Opath = new bx_param_filename_c ((bx_id)(BXP_ATAx_DEVICE_PATH(channel,slave)), + "ata-device:path", + "Pathname of the image or physical device (cdrom only)", + "", BX_PATHNAME_LEN)); + + menu->add (bx_options.atadevice[channel][slave].Omode = new bx_param_enum_c ((bx_id)(BXP_ATAx_DEVICE_MODE(channel,slave)), + "ata-device:mode", + "Mode of the ATA harddisk", + atadevice_mode_names, + BX_ATA_MODE_FLAT, + BX_ATA_MODE_FLAT)); + + menu->add (bx_options.atadevice[channel][slave].Ostatus = new bx_param_enum_c ((bx_id)(BXP_ATAx_DEVICE_STATUS(channel,slave)), + "ata-device:status", + "CD-ROM media status (inserted / ejected)", + atadevice_status_names, + BX_INSERTED, + BX_EJECTED)); + + menu->add (bx_options.atadevice[channel][slave].Ojournal = new bx_param_filename_c ((bx_id)(BXP_ATAx_DEVICE_JOURNAL(channel,slave)), + "ata-device:journal", + "Pathname of the journal file", + "", BX_PATHNAME_LEN)); + + menu->add (bx_options.atadevice[channel][slave].Ocylinders = new bx_param_num_c ((bx_id)(BXP_ATAx_DEVICE_CYLINDERS(channel,slave)), + "ata-device:cylinders", + "Number of cylinders", + 0, 65535, + 0)); + menu->add (bx_options.atadevice[channel][slave].Oheads = new bx_param_num_c ((bx_id)(BXP_ATAx_DEVICE_HEADS(channel,slave)), + "ata-device:heads", + "Number of heads", + 0, 65535, + 0)); + menu->add (bx_options.atadevice[channel][slave].Ospt = new bx_param_num_c ((bx_id)(BXP_ATAx_DEVICE_SPT(channel,slave)), + "ata-device:spt", + "Number of sectors per track", + 0, 65535, + 0)); + + menu->add (bx_options.atadevice[channel][slave].Omodel = new bx_param_string_c ((bx_id)(BXP_ATAx_DEVICE_MODEL(channel,slave)), + "ata-device:model", + "String returned by the 'identify device' command", + "Generic 1234", 40)); + + menu->add (bx_options.atadevice[channel][slave].Obiosdetect = new bx_param_enum_c ((bx_id)(BXP_ATAx_DEVICE_BIOSDETECT(channel,slave)), + "ata-device:biosdetect", + "Type of bios detection", + atadevice_biosdetect_names, + BX_ATA_BIOSDETECT_AUTO, + BX_ATA_BIOSDETECT_NONE)); + + menu->add (bx_options.atadevice[channel][slave].Otranslation = new bx_param_enum_c ((bx_id)(BXP_ATAx_DEVICE_TRANSLATION(channel,slave)), + "How the ata-disk translation is done by the bios", + "Type of translation", + atadevice_translation_names, + BX_ATA_TRANSLATION_AUTO, + BX_ATA_TRANSLATION_NONE)); + + bx_options.atadevice[channel][slave].Opresent->set_dependent_list ( + menu->clone ()); + // the menu and all items on it depend on the Opresent flag + bx_options.atadevice[channel][slave].Opresent->get_dependent_list()->add(menu); + // the present flag depends on the ATA channel's present flag + bx_options.ata[channel].Opresent->get_dependent_list()->add ( + bx_options.atadevice[channel][slave].Opresent); + } + + // set up top level menu for ATA[i] controller configuration. This list + // controls what will appear on the ATA configure dialog. It now + // requests the USE_TAB_WINDOW display, which is implemented in wx. + char buffer[32]; + sprintf (buffer, "Configure ATA%d", channel); + ata_menu[channel] = new bx_list_c ((bx_id)(BXP_ATAx_MENU(channel)), strdup(buffer), "", 4); + ata_menu[channel]->add (ata[channel]); + ata_menu[channel]->add (bx_options.atadevice[channel][0].Omenu); + ata_menu[channel]->add (bx_options.atadevice[channel][1].Omenu); + ata_menu[channel]->get_options()->set (bx_list_c::USE_TAB_WINDOW); + } + + // Enable first ata interface by default, disable the others. + bx_options.ata[0].Opresent->set_initial_val(1); + + // now that the dependence relationships are established, call set() on + // the ata device present params to set all enables correctly. + for (i=0; i<BX_MAX_ATA_CHANNEL; i++) + bx_options.ata[i].Opresent->set (i==0); + + for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel ++) { + + bx_options.ata[channel].Opresent->set_ask_format ("Channel is enabled: [%s] "); + bx_options.ata[channel].Oioaddr1->set_ask_format ("Enter new ioaddr1: [0x%x] "); + bx_options.ata[channel].Oioaddr2->set_ask_format ("Enter new ioaddr2: [0x%x] "); + bx_options.ata[channel].Oirq->set_ask_format ("Enter new IRQ: [%d] "); +#if BX_WITH_WX + bx_options.ata[channel].Opresent->set_label ("Enable this channel?"); + bx_options.ata[channel].Oioaddr1->set_label ("I/O Address 1:"); + bx_options.ata[channel].Oioaddr2->set_label ("I/O Address 2:"); + bx_options.ata[channel].Oirq->set_label ("IRQ:"); + bx_options.ata[channel].Oirq->set_options (bx_param_num_c::USE_SPIN_CONTROL); +#else + bx_options.ata[channel].Opresent->set_format ("enabled: %s"); + bx_options.ata[channel].Oioaddr1->set_format ("ioaddr1: 0x%x"); + bx_options.ata[channel].Oioaddr2->set_format ("ioaddr2: 0x%x"); + bx_options.ata[channel].Oirq->set_format ("irq: %d"); +#endif + bx_options.ata[channel].Oioaddr1->set_base (16); + bx_options.ata[channel].Oioaddr2->set_base (16); + + for (Bit8u slave=0; slave<2; slave++) { + + bx_options.atadevice[channel][slave].Opresent->set_ask_format ( + "Device is enabled: [%s] "); + bx_options.atadevice[channel][slave].Otype->set_ask_format ( + "Enter type of ATA device, disk or cdrom: [%s] "); + bx_options.atadevice[channel][slave].Omode->set_ask_format ( + "Enter mode of ATA device, (flat, concat, etc.): [%s] "); + bx_options.atadevice[channel][slave].Opath->set_ask_format ( + "Enter new filename: [%s] "); + bx_options.atadevice[channel][slave].Ocylinders->set_ask_format ( + "Enter number of cylinders: [%d] "); + bx_options.atadevice[channel][slave].Oheads->set_ask_format ( + "Enter number of heads: [%d] "); + bx_options.atadevice[channel][slave].Ospt->set_ask_format ( + "Enter number of sectors per track: [%d] "); + bx_options.atadevice[channel][slave].Ostatus->set_ask_format ( + "Is the device inserted or ejected? [%s] "); + bx_options.atadevice[channel][slave].Omodel->set_ask_format ( + "Enter new model name: [%s]"); + bx_options.atadevice[channel][slave].Otranslation->set_ask_format ( + "Enter translation type: [%s]"); + bx_options.atadevice[channel][slave].Obiosdetect->set_ask_format ( + "Enter bios detection type: [%s]"); + bx_options.atadevice[channel][slave].Ojournal->set_ask_format ( + "Enter path of journal file: [%s]"); + +#if BX_WITH_WX + bx_options.atadevice[channel][slave].Opresent->set_label ( + "Enable this device?"); + bx_options.atadevice[channel][slave].Otype->set_label ( + "Type of ATA device:"); + bx_options.atadevice[channel][slave].Omode->set_label ( + "Type of disk image:"); + bx_options.atadevice[channel][slave].Opath->set_label ( + "Path or physical device name:"); + bx_options.atadevice[channel][slave].Ocylinders->set_label ( + "Cylinders:"); + bx_options.atadevice[channel][slave].Oheads->set_label ( + "Heads:"); + bx_options.atadevice[channel][slave].Ospt->set_label ( + "Sectors per track:"); + bx_options.atadevice[channel][slave].Ostatus->set_label ( + "Inserted?"); + bx_options.atadevice[channel][slave].Omodel->set_label ( + "Model name:"); + bx_options.atadevice[channel][slave].Otranslation->set_label ( + "Translation type:"); + bx_options.atadevice[channel][slave].Obiosdetect->set_label ( + "BIOS Detection:"); + bx_options.atadevice[channel][slave].Ojournal->set_label ( + "Path of journal file:"); +#else + bx_options.atadevice[channel][slave].Opresent->set_format ("enabled: %s"); + bx_options.atadevice[channel][slave].Otype->set_format ("type %s"); + bx_options.atadevice[channel][slave].Omode->set_format ("mode %s"); + bx_options.atadevice[channel][slave].Opath->set_format ("path '%s'"); + bx_options.atadevice[channel][slave].Ocylinders->set_format ("%d cylinders"); + bx_options.atadevice[channel][slave].Oheads->set_format ("%d heads"); + bx_options.atadevice[channel][slave].Ospt->set_format ("%d sectors/track"); + bx_options.atadevice[channel][slave].Ostatus->set_format ("%s"); + bx_options.atadevice[channel][slave].Omodel->set_format ("model '%s'"); + bx_options.atadevice[channel][slave].Otranslation->set_format ("translation '%s'"); + bx_options.atadevice[channel][slave].Obiosdetect->set_format ("biosdetect '%s'"); + bx_options.atadevice[channel][slave].Ojournal->set_format ("journal is '%s'"); +#endif + + bx_options.atadevice[channel][slave].Otype->set_handler (bx_param_handler); + bx_options.atadevice[channel][slave].Omode->set_handler (bx_param_handler); + bx_options.atadevice[channel][slave].Ostatus->set_handler (bx_param_handler); + bx_options.atadevice[channel][slave].Opath->set_handler (bx_param_string_handler); + + // Set the enable_hanlders + bx_options.atadevice[channel][slave].Ojournal->set_enable_handler (bx_param_enable_handler); + bx_options.atadevice[channel][slave].Ostatus->set_enable_handler (bx_param_enable_handler); + + } + } + + bx_options.OnewHardDriveSupport = new bx_param_bool_c (BXP_NEWHARDDRIVESUPPORT, + "New hard drive support", + "Enables new features found on newer hard drives.", + 1); + + bx_options.Obootdrive = new bx_param_enum_c (BXP_BOOTDRIVE, + "bootdrive", + "Boot A, C or CD", + floppy_bootdisk_names, + BX_BOOT_FLOPPYA, + BX_BOOT_FLOPPYA); + bx_options.Obootdrive->set_format ("Boot from: %s drive"); + bx_options.Obootdrive->set_ask_format ("Boot from floppy drive, hard drive or cdrom ? [%s] "); + + bx_options.OfloppySigCheck = new bx_param_bool_c (BXP_FLOPPYSIGCHECK, + "Skip Floppy Boot Signature Check", + "Skips check for the 0xaa55 signature on floppy boot device.", + 0); + + // disk menu + bx_param_c *disk_menu_init_list[] = { + SIM->get_param (BXP_FLOPPYA), + SIM->get_param (BXP_FLOPPYB), + SIM->get_param (BXP_ATA0), + SIM->get_param (BXP_ATA0_MASTER), + SIM->get_param (BXP_ATA0_SLAVE), +#if BX_MAX_ATA_CHANNEL>1 + SIM->get_param (BXP_ATA1), + SIM->get_param (BXP_ATA1_MASTER), + SIM->get_param (BXP_ATA1_SLAVE), +#endif +#if BX_MAX_ATA_CHANNEL>2 + SIM->get_param (BXP_ATA2), + SIM->get_param (BXP_ATA2_MASTER), + SIM->get_param (BXP_ATA2_SLAVE), +#endif +#if BX_MAX_ATA_CHANNEL>3 + SIM->get_param (BXP_ATA3), + SIM->get_param (BXP_ATA3_MASTER), + SIM->get_param (BXP_ATA3_SLAVE), +#endif + SIM->get_param (BXP_NEWHARDDRIVESUPPORT), + SIM->get_param (BXP_BOOTDRIVE), + SIM->get_param (BXP_FLOPPYSIGCHECK), + NULL + }; + menu = new bx_list_c (BXP_MENU_DISK, "Bochs Disk Options", "diskmenu", disk_menu_init_list); + menu->get_options ()->set (menu->SHOW_PARENT); + + // memory options menu + bx_options.memory.Osize = new bx_param_num_c (BXP_MEM_SIZE, + "megs", + "Amount of RAM in megabytes", + 1, BX_MAX_BIT32U, + BX_DEFAULT_MEM_MEGS); + bx_options.memory.Osize->set_ask_format ("Enter memory size (MB): [%d] "); +#if BX_WITH_WX + bx_options.memory.Osize->set_label ("Memory size (megabytes)"); + bx_options.memory.Osize->set_options (bx_param_num_c::USE_SPIN_CONTROL); +#else + bx_options.memory.Osize->set_format ("Memory size in megabytes: %d"); +#endif + + // initialize serial and parallel port options +#define PAR_SER_INIT_LIST_MAX \ + ((BXP_PARAMS_PER_PARALLEL_PORT * BX_N_PARALLEL_PORTS) \ + + (BXP_PARAMS_PER_SERIAL_PORT * BX_N_SERIAL_PORTS) \ + + (BXP_PARAMS_PER_USB_HUB * BX_N_USB_HUBS)) + bx_param_c *par_ser_init_list[1+PAR_SER_INIT_LIST_MAX]; + bx_param_c **par_ser_ptr = &par_ser_init_list[0]; + + // parallel ports + for (i=0; i<BX_N_PARALLEL_PORTS; i++) { + sprintf (name, "Enable parallel port #%d", i+1); + sprintf (descr, "Controls whether parallel port #%d is installed or not", i+1); + bx_options.par[i].Oenabled = new bx_param_bool_c ( + BXP_PARPORTx_ENABLED(i+1), + strdup(name), + strdup(descr), + (i==0)? 1 : 0); // only enable #1 by default + sprintf (name, "Parallel port #%d output file", i+1); + sprintf (descr, "Data written to parport#%d by the guest OS is written to this file", i+1); + bx_options.par[i].Ooutfile = new bx_param_filename_c ( + BXP_PARPORTx_OUTFILE(i+1), + strdup(name), + strdup(descr), + "", BX_PATHNAME_LEN); + deplist = new bx_list_c (BXP_NULL, 1); + deplist->add (bx_options.par[i].Ooutfile); + bx_options.par[i].Oenabled->set_dependent_list (deplist); + // add to menu + *par_ser_ptr++ = bx_options.par[i].Oenabled; + *par_ser_ptr++ = bx_options.par[i].Ooutfile; + } + + // serial ports + for (i=0; i<BX_N_SERIAL_PORTS; i++) { + // options for COM port + sprintf (name, "Enable serial port #%d (COM%d)", i+1, i+1); + sprintf (descr, "Controls whether COM%d is installed or not", i+1); + bx_options.com[i].Oenabled = new bx_param_bool_c ( + BXP_COMx_ENABLED(i+1), + strdup(name), + strdup(descr), + (i==0)?1 : 0); // only enable the first by default + sprintf (name, "Pathname of the serial device for COM%d", i+1); + sprintf (descr, "The path can be a real serial device or a pty (X/Unix only)"); + bx_options.com[i].Odev = new bx_param_filename_c ( + BXP_COMx_PATH(i+1), + strdup(name), + strdup(descr), + "", BX_PATHNAME_LEN); + deplist = new bx_list_c (BXP_NULL, 1); + deplist->add (bx_options.com[i].Odev); + bx_options.com[i].Oenabled->set_dependent_list (deplist); + // add to menu + *par_ser_ptr++ = bx_options.com[i].Oenabled; + *par_ser_ptr++ = bx_options.com[i].Odev; + } + + // usb hubs + for (i=0; i<BX_N_USB_HUBS; i++) { + // options for USB hub + sprintf (name, "usb%d:enabled", i+1); + sprintf (descr, "Controls whether USB%d is installed or not", i+1); + sprintf (label, "Enable usb hub #%d (USB%d)", i+1, i+1); + bx_options.usb[i].Oenabled = new bx_param_bool_c ( + BXP_USBx_ENABLED(i+1), + strdup(name), + strdup(descr), + (i==0)?1 : 0); // only enable the first by default + bx_options.usb[i].Oioaddr = new bx_param_num_c ( + BXP_USBx_IOADDR(i+1), + "usb:ioaddr", + "I/O base adress of USB hub", + 0, 0xffe0, + (i==0)?0xff80 : 0); + bx_options.usb[i].Oirq = new bx_param_num_c ( + BXP_USBx_IRQ(i+1), + "usb:irq", + "IRQ used by USB hub", + 0, 15, + (i==0)?10 : 0); + deplist = new bx_list_c (BXP_NULL, 2); + deplist->add (bx_options.usb[i].Oioaddr); + deplist->add (bx_options.usb[i].Oirq); + bx_options.usb[i].Oenabled->set_dependent_list (deplist); + // add to menu + *par_ser_ptr++ = bx_options.usb[i].Oenabled; + *par_ser_ptr++ = bx_options.usb[i].Oioaddr; + *par_ser_ptr++ = bx_options.usb[i].Oirq; + + bx_options.usb[i].Oioaddr->set_ask_format ("Enter new ioaddr: [0x%x] "); + bx_options.usb[i].Oioaddr->set_label ("I/O Address"); + bx_options.usb[i].Oioaddr->set_base (16); + bx_options.usb[i].Oenabled->set_label (strdup(label)); + bx_options.usb[i].Oirq->set_label ("USB IRQ"); + bx_options.usb[i].Oirq->set_options (bx_param_num_c::USE_SPIN_CONTROL); + } + // add final NULL at the end, and build the menu + *par_ser_ptr = NULL; + menu = new bx_list_c (BXP_MENU_SERIAL_PARALLEL, + "Serial and Parallel Port Options", + "serial_parallel_menu", + par_ser_init_list); + menu->get_options ()->set (menu->SHOW_PARENT); + + bx_options.rom.Opath = new bx_param_filename_c (BXP_ROM_PATH, + "romimage", + "Pathname of ROM image to load", + "", BX_PATHNAME_LEN); + bx_options.rom.Opath->set_format ("Name of ROM BIOS image: %s"); + bx_options.rom.Oaddress = new bx_param_num_c (BXP_ROM_ADDRESS, + "romaddr", + "The address at which the ROM image should be loaded", + 0, BX_MAX_BIT32U, + 0xf0000); + bx_options.rom.Oaddress->set_base (16); +#if BX_WITH_WX + bx_options.rom.Opath->set_label ("ROM BIOS image"); + bx_options.rom.Oaddress->set_label ("ROM BIOS address"); +#else + bx_options.rom.Oaddress->set_format ("ROM BIOS address: 0x%05x"); +#endif + + bx_options.optrom[0].Opath = new bx_param_filename_c (BXP_OPTROM1_PATH, + "optional romimage #1", + "Pathname of optional ROM image #1 to load", + "", BX_PATHNAME_LEN); + bx_options.optrom[0].Opath->set_format ("Name of optional ROM image #1 : %s"); + bx_options.optrom[0].Oaddress = new bx_param_num_c (BXP_OPTROM1_ADDRESS, + "optional romaddr #1", + "The address at which the optional ROM image #1 should be loaded", + 0, BX_MAX_BIT32U, + 0); + bx_options.optrom[0].Oaddress->set_base (16); +#if BX_WITH_WX + bx_options.optrom[0].Opath->set_label ("Optional ROM image #1"); + bx_options.optrom[0].Oaddress->set_label ("Address"); +#else + bx_options.optrom[0].Oaddress->set_format ("optional ROM #1 address: 0x%05x"); +#endif + + bx_options.optrom[1].Opath = new bx_param_filename_c (BXP_OPTROM2_PATH, + "optional romimage #2", + "Pathname of optional ROM image #2 to load", + "", BX_PATHNAME_LEN); + bx_options.optrom[1].Opath->set_format ("Name of optional ROM image #2 : %s"); + bx_options.optrom[1].Oaddress = new bx_param_num_c (BXP_OPTROM2_ADDRESS, + "optional romaddr #2", + "The address at which the optional ROM image #2 should be loaded", + 0, BX_MAX_BIT32U, + 0); + bx_options.optrom[1].Oaddress->set_base (16); +#if BX_WITH_WX + bx_options.optrom[1].Opath->set_label ("Optional ROM image #2"); + bx_options.optrom[1].Oaddress->set_label ("Address"); +#else + bx_options.optrom[1].Oaddress->set_format ("optional ROM #2 address: 0x%05x"); +#endif + + bx_options.optrom[2].Opath = new bx_param_filename_c (BXP_OPTROM3_PATH, + "optional romimage #3", + "Pathname of optional ROM image #3 to load", + "", BX_PATHNAME_LEN); + bx_options.optrom[2].Opath->set_format ("Name of optional ROM image #3 : %s"); + bx_options.optrom[2].Oaddress = new bx_param_num_c (BXP_OPTROM3_ADDRESS, + "optional romaddr #3", + "The address at which the optional ROM image #3 should be loaded", + 0, BX_MAX_BIT32U, + 0); + bx_options.optrom[2].Oaddress->set_base (16); +#if BX_WITH_WX + bx_options.optrom[2].Opath->set_label ("Optional ROM image #3"); + bx_options.optrom[2].Oaddress->set_label ("Address"); +#else + bx_options.optrom[2].Oaddress->set_format ("optional ROM #3 address: 0x%05x"); +#endif + + bx_options.optrom[3].Opath = new bx_param_filename_c (BXP_OPTROM4_PATH, + "optional romimage #4", + "Pathname of optional ROM image #4 to load", + "", BX_PATHNAME_LEN); + bx_options.optrom[3].Opath->set_format ("Name of optional ROM image #4 : %s"); + bx_options.optrom[3].Oaddress = new bx_param_num_c (BXP_OPTROM4_ADDRESS, + "optional romaddr #4", + "The address at which the optional ROM image #4 should be loaded", + 0, BX_MAX_BIT32U, + 0); + bx_options.optrom[3].Oaddress->set_base (16); +#if BX_WITH_WX + bx_options.optrom[3].Opath->set_label ("Optional ROM image #4"); + bx_options.optrom[3].Oaddress->set_label ("Address"); +#else + bx_options.optrom[3].Oaddress->set_format ("optional ROM #4 address: 0x%05x"); +#endif + + bx_options.vgarom.Opath = new bx_param_filename_c (BXP_VGA_ROM_PATH, + "vgaromimage", + "Pathname of VGA ROM image to load", + "", BX_PATHNAME_LEN); + bx_options.vgarom.Opath->set_format ("Name of VGA BIOS image: %s"); +#if BX_WITH_WX + bx_options.vgarom.Opath->set_label ("VGA BIOS image"); +#endif + bx_param_c *memory_init_list[] = { + bx_options.memory.Osize, + bx_options.vgarom.Opath, + bx_options.rom.Opath, + bx_options.rom.Oaddress, + bx_options.optrom[0].Opath, + bx_options.optrom[0].Oaddress, + bx_options.optrom[1].Opath, + bx_options.optrom[1].Oaddress, + bx_options.optrom[2].Opath, + bx_options.optrom[2].Oaddress, + bx_options.optrom[3].Opath, + bx_options.optrom[3].Oaddress, + NULL + }; + menu = new bx_list_c (BXP_MENU_MEMORY, "Bochs Memory Options", "memmenu", memory_init_list); + menu->get_options ()->set (menu->SHOW_PARENT); + + // interface + bx_options.Ovga_update_interval = new bx_param_num_c (BXP_VGA_UPDATE_INTERVAL, + "VGA Update Interval", + "Number of microseconds between VGA updates", + 1, BX_MAX_BIT32U, + 30000); + bx_options.Ovga_update_interval->set_handler (bx_param_handler); + bx_options.Ovga_update_interval->set_runtime_param (1); + bx_options.Ovga_update_interval->set_ask_format ("Type a new value for VGA update interval: [%d] "); + bx_options.Omouse_enabled = new bx_param_bool_c (BXP_MOUSE_ENABLED, + "Enable the mouse", + "Controls whether the mouse sends events to the guest. The hardware emulation is always enabled.", + 0); + bx_options.Omouse_enabled->set_handler (bx_param_handler); + bx_options.Omouse_enabled->set_runtime_param (1); + bx_options.Oips = new bx_param_num_c (BXP_IPS, + "Emulated instructions per second (IPS)", + "Emulated instructions per second, used to calibrate bochs emulated time with wall clock time.", + 1, BX_MAX_BIT32U, + 500000); + bx_options.Otext_snapshot_check = new bx_param_bool_c (BXP_TEXT_SNAPSHOT_CHECK, + "Enable panic for use in bochs testing", + "Enable panic when text on screen matches snapchk.txt.\nUseful for regression testing.\nIn win32, turns off CR/LF in snapshots and cuts.", + 0); + bx_options.Oprivate_colormap = new bx_param_bool_c (BXP_PRIVATE_COLORMAP, + "Use a private colormap", + "Request that the GUI create and use it's own non-shared colormap. This colormap will be used when in the bochs window. If not enabled, a shared colormap scheme may be used. Not implemented on all GUI's.", + 0); +#if BX_WITH_AMIGAOS + bx_options.Ofullscreen = new bx_param_bool_c (BXP_FULLSCREEN, + "Use full screen mode", + "When enabled, bochs occupies the whole screen instead of just a window.", + 0); + bx_options.Oscreenmode = new bx_param_string_c (BXP_SCREENMODE, + "Screen mode name", + "Screen mode name", + "", BX_PATHNAME_LEN); + bx_options.Oscreenmode->set_handler (bx_param_string_handler); +#endif + static char *config_interface_list[] = { + "textconfig", +#if BX_WITH_WX + "wx", +#endif + NULL + }; + bx_options.Osel_config = new bx_param_enum_c ( + BXP_SEL_CONFIG_INTERFACE, + "Configuration interface", + "Select configuration interface", + config_interface_list, + 0, + 0); + bx_options.Osel_config->set_by_name (BX_DEFAULT_CONFIG_INTERFACE); + bx_options.Osel_config->set_ask_format ("Choose which configuration interface to use: [%s] "); + // this is a list of gui libraries that are known to be available at + // compile time. The one that is listed first will be the default, + // which is used unless the user overrides it on the command line or + // in a configuration file. + static char *display_library_list[] = { +#if BX_WITH_X11 + "x", +#endif +#if BX_WITH_WIN32 + "win32", +#endif +#if BX_WITH_CARBON + "carbon", +#endif +#if BX_WITH_BEOS + "beos", +#endif +#if BX_WITH_MACOS + "macos", +#endif +#if BX_WITH_AMIGAOS + "amigaos", +#endif +#if BX_WITH_SDL + "sdl", +#endif +#if BX_WITH_SVGA + "svga", +#endif +#if BX_WITH_TERM + "term", +#endif +#if BX_WITH_RFB + "rfb", +#endif +#if BX_WITH_WX + "wx", +#endif +#if BX_WITH_NOGUI + "nogui", +#endif + NULL + }; + bx_options.Osel_displaylib = new bx_param_enum_c (BXP_SEL_DISPLAY_LIBRARY, + "VGA Display Library", + "Select VGA Display Library", + display_library_list, + 0, + 0); + bx_options.Osel_displaylib->set_by_name (BX_DEFAULT_DISPLAY_LIBRARY); + bx_options.Osel_displaylib->set_ask_format ("Choose which library to use for the Bochs display: [%s] "); + bx_param_c *interface_init_list[] = { + bx_options.Osel_config, + bx_options.Osel_displaylib, + bx_options.Ovga_update_interval, + bx_options.Omouse_enabled, + bx_options.Oips, + bx_options.Oprivate_colormap, +#if BX_WITH_AMIGAOS + bx_options.Ofullscreen, + bx_options.Oscreenmode, +#endif + NULL + }; + menu = new bx_list_c (BXP_MENU_INTERFACE, "Bochs Interface Menu", "intfmenu", interface_init_list); + menu->get_options ()->set (menu->SHOW_PARENT); + + // NE2K options + bx_options.ne2k.Opresent = new bx_param_bool_c (BXP_NE2K_PRESENT, + "Enable NE2K NIC emulation", + "Enables the NE2K NIC emulation", + 0); + bx_options.ne2k.Oioaddr = new bx_param_num_c (BXP_NE2K_IOADDR, + "NE2K I/O Address", + "I/O base address of the emulated NE2K device", + 0, 0xffff, + 0x240); + bx_options.ne2k.Oioaddr->set_base (16); + bx_options.ne2k.Oirq = new bx_param_num_c (BXP_NE2K_IRQ, + "NE2K Interrupt", + "IRQ used by the NE2K device", + 0, 15, + 9); + bx_options.ne2k.Oirq->set_options (bx_param_num_c::USE_SPIN_CONTROL); + bx_options.ne2k.Omacaddr = new bx_param_string_c (BXP_NE2K_MACADDR, + "MAC Address", + "MAC address of the NE2K device. Don't use an address of a machine on your net.", + "\xfe\xfd\xde\xad\xbe\xef", 6); + bx_options.ne2k.Omacaddr->get_options ()->set (bx_options.ne2k.Omacaddr->RAW_BYTES); + bx_options.ne2k.Omacaddr->set_separator (':'); + static char *eth_module_list[] = { + "null", +#if defined(ETH_LINUX) + "linux", +#endif +#if HAVE_ETHERTAP + "tap", +#endif +#if HAVE_TUNTAP + "tuntap", +#endif +#if defined(ETH_WIN32) + "win32", +#endif +#if defined(ETH_FBSD) + "fbsd", +#endif +#ifdef ETH_ARPBACK + "arpback", +#endif + NULL + }; + bx_options.ne2k.Oethmod = new bx_param_enum_c (BXP_NE2K_ETHMOD, + "Ethernet module", + "Module used for the connection to the real net.", + eth_module_list, + 0, + 0); + bx_options.ne2k.Oethmod->set_by_name ("null"); + bx_options.ne2k.Oethdev = new bx_param_string_c (BXP_NE2K_ETHDEV, + "Ethernet device", + "Device used for the connection to the real net. This is only valid if an ethernet module other than 'null' is used.", + "xl0", BX_PATHNAME_LEN); + bx_options.ne2k.Oscript = new bx_param_string_c (BXP_NE2K_SCRIPT, + "Device configuration script", + "Name of the script that is executed after Bochs initializes the network interface (optional).", + "none", BX_PATHNAME_LEN); +#if !BX_WITH_WX + bx_options.ne2k.Oscript->set_ask_format ("Enter new script name, or 'none': [%s] "); +#endif + bx_param_c *ne2k_init_list[] = { + bx_options.ne2k.Opresent, + bx_options.ne2k.Oioaddr, + bx_options.ne2k.Oirq, + bx_options.ne2k.Omacaddr, + bx_options.ne2k.Oethmod, + bx_options.ne2k.Oethdev, + bx_options.ne2k.Oscript, + NULL + }; + menu = new bx_list_c (BXP_NE2K, "NE2K Configuration", "", ne2k_init_list); + menu->get_options ()->set (menu->SHOW_PARENT); + bx_param_c **ne2k_dependent_list = &ne2k_init_list[1]; + bx_options.ne2k.Opresent->set_dependent_list ( + new bx_list_c (BXP_NULL, "", "", ne2k_dependent_list)); + bx_options.ne2k.Opresent->set_handler (bx_param_handler); + bx_options.ne2k.Opresent->set (0); + + // SB16 options + bx_options.sb16.Opresent = new bx_param_bool_c (BXP_SB16_PRESENT, + "Enable SB16 emulation", + "Enables the SB16 emulation", + 0); + bx_options.sb16.Omidifile = new bx_param_filename_c (BXP_SB16_MIDIFILE, + "MIDI file", + "The filename is where the MIDI data is sent. This can be device or just a file.", + "", BX_PATHNAME_LEN); + bx_options.sb16.Owavefile = new bx_param_filename_c (BXP_SB16_WAVEFILE, + "Wave file", + "This is the device/file where the wave output is stored", + "", BX_PATHNAME_LEN); + bx_options.sb16.Ologfile = new bx_param_filename_c (BXP_SB16_LOGFILE, + "Log file", + "The file to write the SB16 emulator messages to.", + "", BX_PATHNAME_LEN); + bx_options.sb16.Omidimode = new bx_param_num_c (BXP_SB16_MIDIMODE, + "Midi mode", + "Controls the MIDI output format.", + 0, 3, + 0); + bx_options.sb16.Owavemode = new bx_param_num_c (BXP_SB16_WAVEMODE, + "Wave mode", + "Controls the wave output format.", + 0, 3, + 0); + bx_options.sb16.Ologlevel = new bx_param_num_c (BXP_SB16_LOGLEVEL, + "Log mode", + "Controls how verbose the SB16 emulation is (0 = no log, 5 = all errors and infos).", + 0, 5, + 0); + bx_options.sb16.Odmatimer = new bx_param_num_c (BXP_SB16_DMATIMER, + "DMA timer", + "Microseconds per second for a DMA cycle.", + 0, BX_MAX_BIT32U, + 0); + +#if BX_WITH_WX + bx_options.sb16.Omidimode->set_options (bx_param_num_c::USE_SPIN_CONTROL); + bx_options.sb16.Owavemode->set_options (bx_param_num_c::USE_SPIN_CONTROL); + bx_options.sb16.Ologlevel->set_options (bx_param_num_c::USE_SPIN_CONTROL); +#endif + bx_param_c *sb16_init_list[] = { + bx_options.sb16.Opresent, + bx_options.sb16.Omidimode, + bx_options.sb16.Omidifile, + bx_options.sb16.Owavemode, + bx_options.sb16.Owavefile, + bx_options.sb16.Ologlevel, + bx_options.sb16.Ologfile, + bx_options.sb16.Odmatimer, + NULL + }; + menu = new bx_list_c (BXP_SB16, "SB16 Configuration", "", sb16_init_list); + menu->get_options ()->set (menu->SHOW_PARENT); + // sb16_dependent_list is a null-terminated list including all the + // sb16 fields except for the "present" field. These will all be enabled/ + // disabled according to the value of the present field. + bx_param_c **sb16_dependent_list = &sb16_init_list[1]; + bx_options.sb16.Opresent->set_dependent_list ( + new bx_list_c (BXP_NULL, "", "", sb16_dependent_list)); + + bx_options.log.Ofilename = new bx_param_filename_c (BXP_LOG_FILENAME, + "Log filename", + "Pathname of bochs log file", + "-", BX_PATHNAME_LEN); + bx_options.log.Ofilename->set_ask_format ("Enter log filename: [%s] "); + + bx_options.log.Oprefix = new bx_param_string_c (BXP_LOG_PREFIX, + "Log output prefix", + "Prefix prepended to log output", + "%t%e%d", BX_PATHNAME_LEN); + bx_options.log.Oprefix->set_ask_format ("Enter log prefix: [%s] "); + + bx_options.log.Odebugger_filename = new bx_param_filename_c (BXP_DEBUGGER_LOG_FILENAME, + "Debugger Log filename", + "Pathname of debugger log file", + "-", BX_PATHNAME_LEN); + bx_options.log.Odebugger_filename->set_ask_format ("Enter debugger log filename: [%s] "); + + // loader + bx_options.load32bitOSImage.OwhichOS = new bx_param_enum_c (BXP_LOAD32BITOS_WHICH, + "Which operating system?", + "Which OS to boot", + loader_os_names, +#ifdef BX_USE_VMX + Load32bitOSLinux, +#else + Load32bitOSNone, +#endif + Load32bitOSNone); + bx_options.load32bitOSImage.Opath = new bx_param_filename_c (BXP_LOAD32BITOS_PATH, + "Pathname of OS to load", + "Pathname of the 32-bit OS to load", + "", BX_PATHNAME_LEN); + bx_options.load32bitOSImage.Oiolog = new bx_param_filename_c (BXP_LOAD32BITOS_IOLOG, + "Pathname of I/O log file", + "I/O logfile used for initializing the hardware", + "", BX_PATHNAME_LEN); + bx_options.load32bitOSImage.Oinitrd = new bx_param_filename_c (BXP_LOAD32BITOS_INITRD, + "Pathname of initrd", + "Pathname of the initial ramdisk", + "", BX_PATHNAME_LEN); + bx_param_c *loader_init_list[] = { + bx_options.load32bitOSImage.OwhichOS, + bx_options.load32bitOSImage.Opath, + bx_options.load32bitOSImage.Oiolog, + bx_options.load32bitOSImage.Oinitrd, + NULL + }; + bx_options.load32bitOSImage.OwhichOS->set_format ("os=%s"); + bx_options.load32bitOSImage.Opath->set_format ("path=%s"); + bx_options.load32bitOSImage.Oiolog->set_format ("iolog=%s"); + bx_options.load32bitOSImage.Oinitrd->set_format ("initrd=%s"); + bx_options.load32bitOSImage.OwhichOS->set_ask_format ("Enter OS to load: [%s] "); + bx_options.load32bitOSImage.Opath->set_ask_format ("Enter pathname of OS: [%s]"); + bx_options.load32bitOSImage.Oiolog->set_ask_format ("Enter pathname of I/O log: [%s] "); + bx_options.load32bitOSImage.Oinitrd->set_ask_format ("Enter pathname of initrd: [%s] "); + menu = new bx_list_c (BXP_LOAD32BITOS, "32-bit OS Loader", "", loader_init_list); + menu->get_options ()->set (menu->SERIES_ASK); + bx_options.load32bitOSImage.OwhichOS->set_handler (bx_param_handler); +#ifdef BX_USE_VMX + bx_options.load32bitOSImage.OwhichOS->set (Load32bitOSLinux); +#else + bx_options.load32bitOSImage.OwhichOS->set (Load32bitOSNone); +#endif + + // clock + bx_options.clock.Otime0 = new bx_param_num_c (BXP_CLOCK_TIME0, + "clock:time0", + "Initial time for Bochs CMOS clock, used if you really want two runs to be identical", + 0, BX_MAX_BIT32U, + BX_CLOCK_TIME0_LOCAL); + bx_options.clock.Osync = new bx_param_enum_c (BXP_CLOCK_SYNC, + "clock:sync", + "Host to guest time synchronization method", + clock_sync_names, + BX_CLOCK_SYNC_NONE, + BX_CLOCK_SYNC_NONE); + bx_param_c *clock_init_list[] = { + bx_options.clock.Osync, + bx_options.clock.Otime0, + NULL + }; +#if !BX_WITH_WX + bx_options.clock.Osync->set_format ("sync=%s"); + bx_options.clock.Otime0->set_format ("initial time=%d"); +#endif + bx_options.clock.Otime0->set_ask_format ("Enter Initial CMOS time (1:localtime, 2:utc, other:time in seconds): [%d] "); + bx_options.clock.Osync->set_ask_format ("Enter Synchronisation method: [%s] "); + bx_options.clock.Otime0->set_label ("Initial CMOS time for Bochs\n(1:localtime, 2:utc, other:time in seconds)"); + bx_options.clock.Osync->set_label ("Synchronisation method"); + menu = new bx_list_c (BXP_CLOCK, "Clock parameters", "", clock_init_list); + menu->get_options ()->set (menu->SERIES_ASK); + + // other + bx_options.Okeyboard_serial_delay = new bx_param_num_c (BXP_KBD_SERIAL_DELAY, + "Keyboard serial delay", + "Approximate time in microseconds that it takes one character to be transfered from the keyboard to controller over the serial path.", + 1, BX_MAX_BIT32U, + 20000); + bx_options.Okeyboard_paste_delay = new bx_param_num_c (BXP_KBD_PASTE_DELAY, + "Keyboard paste delay", + "Approximate time in microseconds between attemps to paste characters to the keyboard controller.", + 1000, BX_MAX_BIT32U, + 100000); + bx_options.Okeyboard_paste_delay->set_handler (bx_param_handler); + bx_options.Okeyboard_paste_delay->set_runtime_param (1); + bx_options.Ofloppy_command_delay = new bx_param_num_c (BXP_FLOPPY_CMD_DELAY, + "Floppy command delay", + "Time in microseconds to wait before completing some floppy commands such as read/write/seek/etc, which normally have a delay associated. This used to be hardwired to 50,000 before.", + 1, BX_MAX_BIT32U, + 50000); + bx_options.Oi440FXSupport = new bx_param_bool_c (BXP_I440FX_SUPPORT, + "PCI i440FX Support", + "Controls whether to emulate the i440FX PCI chipset", + 0); + bx_options.cmos.OcmosImage = new bx_param_bool_c (BXP_CMOS_IMAGE, + "Use a CMOS image", + "Controls the usage of a CMOS image", + 0); + bx_options.cmos.Opath = new bx_param_filename_c (BXP_CMOS_PATH, + "Pathname of CMOS image", + "Pathname of CMOS image", + "", BX_PATHNAME_LEN); + deplist = new bx_list_c (BXP_NULL, 1); + deplist->add (bx_options.cmos.Opath); + bx_options.cmos.OcmosImage->set_dependent_list (deplist); + + // Keyboard mapping + bx_options.keyboard.OuseMapping = new bx_param_bool_c(BXP_KEYBOARD_USEMAPPING, + "Use keyboard mapping", + "Controls whether to use the keyboard mapping feature", + 0); + bx_options.keyboard.Okeymap = new bx_param_filename_c (BXP_KEYBOARD_MAP, + "Keymap filename", + "Pathname of the keymap file used", + "", BX_PATHNAME_LEN); + deplist = new bx_list_c (BXP_NULL, 1); + deplist->add (bx_options.keyboard.Okeymap); + bx_options.keyboard.OuseMapping->set_dependent_list (deplist); + + // Keyboard type + bx_options.Okeyboard_type = new bx_param_enum_c (BXP_KBD_TYPE, + "Keyboard type", + "Keyboard type reported by the 'identify keyboard' command", + keyboard_type_names, + BX_KBD_MF_TYPE, + BX_KBD_XT_TYPE); + bx_options.Okeyboard_type->set_ask_format ("Enter keyboard type: [%s] "); + + // Userbutton shortcut + bx_options.Ouser_shortcut = new bx_param_string_c (BXP_USER_SHORTCUT, + "Userbutton shortcut", + "Defines the keyboard shortcut to be sent when you press the 'user' button in the headerbar.", + "none", 16); + + // GDB stub + bx_options.gdbstub.port = 1234; + bx_options.gdbstub.text_base = 0; + bx_options.gdbstub.data_base = 0; + bx_options.gdbstub.bss_base = 0; + + bx_param_c *keyboard_init_list[] = { + bx_options.Okeyboard_serial_delay, + bx_options.Okeyboard_paste_delay, + bx_options.keyboard.OuseMapping, + bx_options.keyboard.Okeymap, + bx_options.Okeyboard_type, + bx_options.Ouser_shortcut, + NULL + }; + menu = new bx_list_c (BXP_MENU_KEYBOARD, "Configure Keyboard", "", keyboard_init_list); + menu->get_options ()->set (menu->SHOW_PARENT); + + bx_param_c *other_init_list[] = { + bx_options.Ofloppy_command_delay, + bx_options.Oi440FXSupport, + bx_options.cmos.OcmosImage, + bx_options.cmos.Opath, + SIM->get_param (BXP_CLOCK), + SIM->get_param (BXP_LOAD32BITOS), + NULL + }; + menu = new bx_list_c (BXP_MENU_MISC, "Configure Everything Else", "", other_init_list); + menu->get_options ()->set (menu->SHOW_PARENT); + +#if BX_WITH_WX + bx_param_c *other_init_list2[] = { +// bx_options.Osel_config, +// bx_options.Osel_displaylib, + bx_options.Ovga_update_interval, + bx_options.log.Oprefix, + bx_options.Omouse_enabled, + bx_options.OfloppySigCheck, + bx_options.Ofloppy_command_delay, + bx_options.OnewHardDriveSupport, + bx_options.Oprivate_colormap, +#if BX_WITH_AMIGAOS + bx_options.Ofullscreen, + bx_options.Oscreenmode, +#endif + bx_options.Oi440FXSupport, + bx_options.cmos.OcmosImage, + bx_options.cmos.Opath, + NULL + }; + menu = new bx_list_c (BXP_MENU_MISC_2, "Other options", "", other_init_list2); +#endif +} + +void bx_reset_options () +{ + // drives + bx_options.floppya.Opath->reset(); + bx_options.floppya.Odevtype->reset(); + bx_options.floppya.Otype->reset(); + bx_options.floppya.Ostatus->reset(); + bx_options.floppyb.Opath->reset(); + bx_options.floppyb.Odevtype->reset(); + bx_options.floppyb.Otype->reset(); + bx_options.floppyb.Ostatus->reset(); + + for (Bit8u channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) { + bx_options.ata[channel].Opresent->reset(); + bx_options.ata[channel].Oioaddr1->reset(); + bx_options.ata[channel].Oioaddr2->reset(); + bx_options.ata[channel].Oirq->reset(); + + for (Bit8u slave=0; slave<2; slave++) { + bx_options.atadevice[channel][slave].Opresent->reset(); + bx_options.atadevice[channel][slave].Otype->reset(); + bx_options.atadevice[channel][slave].Omode->reset(); + bx_options.atadevice[channel][slave].Opath->reset(); + bx_options.atadevice[channel][slave].Ocylinders->reset(); + bx_options.atadevice[channel][slave].Oheads->reset(); + bx_options.atadevice[channel][slave].Ospt->reset(); + bx_options.atadevice[channel][slave].Ostatus->reset(); + bx_options.atadevice[channel][slave].Omodel->reset(); + bx_options.atadevice[channel][slave].Obiosdetect->reset(); + bx_options.atadevice[channel][slave].Otranslation->reset(); + } + } + bx_options.OnewHardDriveSupport->reset(); + + // boot & memory + bx_options.Obootdrive->reset(); + bx_options.OfloppySigCheck->reset(); + bx_options.memory.Osize->reset(); + + // standard ports + bx_options.com[0].Oenabled->reset(); + bx_options.com[0].Odev->reset(); + bx_options.par[0].Oenabled->reset(); + bx_options.par[0].Ooutfile->reset(); + + // rom images + bx_options.rom.Opath->reset(); + bx_options.rom.Oaddress->reset(); + bx_options.optrom[0].Opath->reset(); + bx_options.optrom[0].Oaddress->reset(); + bx_options.optrom[1].Opath->reset(); + bx_options.optrom[1].Oaddress->reset(); + bx_options.optrom[2].Opath->reset(); + bx_options.optrom[2].Oaddress->reset(); + bx_options.optrom[3].Opath->reset(); + bx_options.optrom[3].Oaddress->reset(); + bx_options.vgarom.Opath->reset(); + + // interface + bx_options.Ovga_update_interval->reset(); + bx_options.Omouse_enabled->reset(); + bx_options.Oips->reset(); + bx_options.Oprivate_colormap->reset(); +#if BX_WITH_AMIGAOS + bx_options.Ofullscreen->reset(); + bx_options.Oscreenmode->reset(); +#endif + + // ne2k + bx_options.ne2k.Opresent->reset(); + bx_options.ne2k.Oioaddr->reset(); + bx_options.ne2k.Oirq->reset(); + bx_options.ne2k.Omacaddr->reset(); + bx_options.ne2k.Oethmod->reset(); + bx_options.ne2k.Oethdev->reset(); + bx_options.ne2k.Oscript->reset(); + + // SB16 + bx_options.sb16.Opresent->reset(); + bx_options.sb16.Omidifile->reset(); + bx_options.sb16.Owavefile->reset(); + bx_options.sb16.Ologfile->reset(); + bx_options.sb16.Omidimode->reset(); + bx_options.sb16.Owavemode->reset(); + bx_options.sb16.Ologlevel->reset(); + bx_options.sb16.Odmatimer->reset(); + + // logfile + bx_options.log.Ofilename->reset(); + bx_options.log.Oprefix->reset(); + bx_options.log.Odebugger_filename->reset(); + + // loader + bx_options.load32bitOSImage.OwhichOS->reset(); + bx_options.load32bitOSImage.Opath->reset(); + bx_options.load32bitOSImage.Oiolog->reset(); + bx_options.load32bitOSImage.Oinitrd->reset(); + + // keyboard + bx_options.Okeyboard_serial_delay->reset(); + bx_options.Okeyboard_paste_delay->reset(); + bx_options.keyboard.OuseMapping->reset(); + bx_options.keyboard.Okeymap->reset(); + bx_options.Okeyboard_type->reset(); + bx_options.Ouser_shortcut->reset(); + + // Clock + bx_options.clock.Otime0->reset(); + bx_options.clock.Osync->reset(); + + // other + bx_options.Ofloppy_command_delay->reset(); + bx_options.Oi440FXSupport->reset(); + bx_options.cmos.OcmosImage->reset(); + bx_options.cmos.Opath->reset(); + bx_options.Otext_snapshot_check->reset(); +} + +void bx_print_header () +{ + fprintf (stderr, "%s\n", divider); + char buffer[128]; + sprintf (buffer, "Bochs x86 Emulator %s\n", VER_STRING); + bx_center_print (stderr, buffer, 72); + if (REL_STRING[0]) { + sprintf (buffer, "%s\n", REL_STRING); + bx_center_print (stderr, buffer, 72); + } + fprintf (stderr, "%s\n", divider); +} + +#if BX_WITH_CARBON +/* Original code by Darrell Walisser - dwaliss1@purdue.edu */ + +static void setupWorkingDirectory (char *path) +{ + char parentdir[MAXPATHLEN]; + char *c; + + strncpy ( parentdir, path, MAXPATHLEN ); + c = (char*) parentdir; + + while (*c != '\0') /* go to end */ + c++; + + while (*c != '/') /* back up to parent */ + c--; + + *c = '\0'; /* cut off last part (binary name) */ + + /* chdir to the binary app's parent */ + int n; + n = chdir (parentdir); + if (n) BX_PANIC (("failed to change dir to parent")); + /* chdir to the .app's parent */ + n = chdir ("../../../"); + if (n) BX_PANIC (("failed to change to ../../..")); +} + +/* Panic button to display fatal errors. + Completely self contained, can't rely on carbon.cc being available */ +static void carbonFatalDialog(const char *error, const char *exposition) +{ + DialogRef alertDialog; + CFStringRef cfError; + CFStringRef cfExposition; + DialogItemIndex index; + AlertStdCFStringAlertParamRec alertParam = {0}; + fprintf(stderr, "Entering carbonFatalDialog: %s\n", error); + + // Init libraries + InitCursor(); + // Assemble dialog + cfError = CFStringCreateWithCString(NULL, error, kCFStringEncodingASCII); + if(exposition != NULL) + { + cfExposition = CFStringCreateWithCString(NULL, exposition, kCFStringEncodingASCII); + } + else { cfExposition = NULL; } + alertParam.version = kStdCFStringAlertVersionOne; + alertParam.defaultText = CFSTR("Quit"); + alertParam.position = kWindowDefaultPosition; + alertParam.defaultButton = kAlertStdAlertOKButton; + // Display Dialog + CreateStandardAlert( + kAlertStopAlert, + cfError, + cfExposition, /* can be NULL */ + &alertParam, /* can be NULL */ + &alertDialog); + RunStandardAlert( alertDialog, NULL, &index); + // Cleanup + CFRelease( cfError ); + if( cfExposition != NULL ) { CFRelease( cfExposition ); } +} +#endif + +int bxmain () { +#ifdef HAVE_LOCALE_H + // Initialize locale (for isprint() and other functions) + setlocale (LC_ALL, ""); +#endif + bx_user_quit = 0; + bx_init_siminterface (); // create the SIM object + + static jmp_buf context; + if (setjmp (context) == 0) { + SIM->set_quit_context (&context); + if (bx_init_main (bx_startup_flags.argc, bx_startup_flags.argv) < 0) + return 0; + // read a param to decide which config interface to start. + // If one exists, start it. If not, just begin. + bx_param_enum_c *ci_param = SIM->get_param_enum (BXP_SEL_CONFIG_INTERFACE); + char *ci_name = ci_param->get_choice (ci_param->get ()); + if (!strcmp(ci_name, "textconfig")) { + init_text_config_interface (); // in textconfig.h + } +#if BX_WITH_WX + else if (!strcmp(ci_name, "wx")) { + PLUG_load_plugin(wx, PLUGTYPE_CORE); + } +#endif + else { + BX_PANIC (("unsupported configuration interface '%s'", ci_name)); + } + int status = SIM->configuration_interface (ci_name, CI_START); + if (status == CI_ERR_NO_TEXT_CONSOLE) + BX_PANIC (("Bochs needed the text console, but it was not usable")); + // user quit the config interface, so just quit + } else { + // quit via longjmp + } +} + +// normal main function, presently in for all cases except for +// wxWindows under win32. +int main (int argc, char *argv[]) +{ + daemon(0, 0); + bx_startup_flags.argc = argc; + bx_startup_flags.argv = argv; +#if BX_WITH_SDL && defined(WIN32) + // if SDL/win32, try to create a console window. + RedirectIOToConsole (); +#endif + return bxmain (); +} + +void +print_usage () +{ + fprintf(stderr, + "Usage: bochs [flags] [bochsrc options]\n\n" + " -n no configuration file\n" + " -f configfile specify configuration file\n" + " -q quick start (skip configuration interface)\n" + " --help display this help and exit\n\n" + "For information on Bochs configuration file arguments, see the\n" +#if (!defined(WIN32)) && !BX_WITH_MACOS + "bochsrc section in the user documentation or the man page of bochsrc.\n"); +#else + "bochsrc section in the user documentation.\n"); +#endif +} + +#ifdef BX_USE_VMX +int domid = -1; +unsigned long megabytes = 0; +#endif +int +bx_init_main (int argc, char *argv[]) +{ + // To deal with initialization order problems inherent in C++, use the macros + // SAFE_GET_IOFUNC and SAFE_GET_GENLOG to retrieve "io" and "genlog" in all + // constructors or functions called by constructors. The macros test for + // NULL and create the object if necessary, then return it. Ensure that io + // and genlog get created, by making one reference to each macro right here. + // All other code can reference io and genlog directly. Because these + // objects are required for logging, and logging is so fundamental to + // knowing what the program is doing, they are never free()d. + SAFE_GET_IOFUNC(); // never freed + SAFE_GET_GENLOG(); // never freed + + // initalization must be done early because some destructors expect + // the bx_options to exist by the time they are called. + bx_init_bx_dbg (); + bx_init_options (); + + bx_print_header (); + +#ifdef BX_USE_VMX + xc_handle = xc_interface_open(); + SIM->get_param_enum(BXP_BOCHS_START)->set (BX_QUICK_START); +#else + SIM->get_param_enum(BXP_BOCHS_START)->set (BX_RUN_START); +#endif + + // interpret the args that start with -, like -q, -f, etc. + int arg = 1, load_rcfile=1; + while (arg < argc) { + // parse next arg + if (!strcmp ("--help", argv[arg]) || !strncmp ("-h", argv[arg], 2)) { + print_usage(); + SIM->quit_sim (0); + } + else if (!strcmp ("-n", argv[arg])) { + load_rcfile = 0; + } + else if (!strcmp ("-q", argv[arg])) { + SIM->get_param_enum(BXP_BOCHS_START)->set (BX_QUICK_START); + } + else if (!strcmp ("-f", argv[arg])) { + if (++arg >= argc) BX_PANIC(("-f must be followed by a filename")); + else bochsrc_filename = argv[arg]; + } + else if (!strcmp ("-qf", argv[arg])) { + SIM->get_param_enum(BXP_BOCHS_START)->set (BX_QUICK_START); + if (++arg >= argc) BX_PANIC(("-qf must be followed by a filename")); + else bochsrc_filename = argv[arg]; + } +#ifdef BX_USE_VMX + else if (!strcmp ("-p", argv[arg])) { + //get the polling port + extern int ioreq_port; + if (++arg >= argc) BX_PANIC(("-p must be followed by a polling port")); + else sscanf(argv[arg], "%d", &ioreq_port); + } + else if (!strcmp ("-d", argv[arg])) { + //get the domain id + if (++arg >= argc) BX_PANIC(("-d must be followed by domainid")); + else sscanf(argv[arg], "%d", &domid); + } + else if (!strcmp ("-m", argv[arg])) { + //get the maxmem + if (++arg >= argc) + BX_PANIC(("-m must be followed by maxmem in megabytes")); + else sscanf(argv[arg], "%d", &megabytes); + } + +#endif + else if (argv[arg][0] == '-') { + print_usage(); + BX_PANIC (("command line arg '%s' was not understood", argv[arg])); + } + else { + // the arg did not start with -, so stop interpreting flags + break; + } + arg++; + } + + int norcfile = 1; + + if (load_rcfile) { + /* parse configuration file and command line arguments */ +#ifdef WIN32 + if (bochsrc_filename != NULL) { + lstrcpy(bx_startup_flags.initial_dir, bochsrc_filename); + } else { + bx_startup_flags.initial_dir[0] = 0; + } +#endif + if (bochsrc_filename == NULL) bochsrc_filename = bx_find_bochsrc (); + if (bochsrc_filename) + norcfile = bx_read_configuration (bochsrc_filename); + } + + // parse the rest of the command line. This is done after reading the + // configuration file so that the command line arguments can override + // the settings from the file. + if (bx_parse_cmdline (arg, argc, argv)) { + BX_PANIC(("There were errors while parsing the command line")); + return -1; + } + // initialize plugin system. This must happen before we attempt to + // load any modules. + plugin_startup(); + return 0; +} + +bx_bool load_and_init_display_lib () { + if (bx_gui != NULL) { + // bx_gui has already been filled in. This happens when you start + // the simulation for the second time. + // Also, if you load wxWindows as the configuration interface. Its + // plugin_init will install wxWindows as the bx_gui. + return true; + } + BX_ASSERT (bx_gui == NULL); + bx_param_enum_c *ci_param = SIM->get_param_enum (BXP_SEL_CONFIG_INTERFACE); + char *ci_name = ci_param->get_choice (ci_param->get ()); + bx_param_enum_c *gui_param = SIM->get_param_enum(BXP_SEL_DISPLAY_LIBRARY); + char *gui_name = gui_param->get_choice (gui_param->get ()); + if (!strcmp(ci_name, "wx")) { + BX_ERROR(("change of the config interface to wx not implemented yet")); + } + if (!strcmp (gui_name, "wx")) { + // they must not have used wx as the configuration interface, or bx_gui + // would already be initialized. Sorry, it doesn't work that way. + BX_ERROR (("wxWindows was not used as the configuration interface, so it cannot be used as the display library")); + // choose another, hopefully different! + gui_param->set (0); + gui_name = gui_param->get_choice (gui_param->get ()); + if (!strcmp (gui_name, "wx")) { + BX_PANIC (("no alternative display libraries are available")); + return false; + } + BX_ERROR (("changing display library to '%s' instead", gui_name)); + } +#if BX_WITH_NOGUI + if (!strcmp (gui_name, "nogui")) + PLUG_load_plugin (nogui, PLUGTYPE_OPTIONAL); +#endif +#if BX_WITH_RFB + if (!strcmp (gui_name, "rfb")) + PLUG_load_plugin (rfb, PLUGTYPE_OPTIONAL); +#endif +#if BX_WITH_X11 + if (!strcmp (gui_name, "x")) + PLUG_load_plugin (x, PLUGTYPE_OPTIONAL); +#endif + +#if BX_GUI_SIGHANDLER + // set the flag for guis requiring a GUI sighandler. + // useful when guis are compiled as plugins + // only term for now + if (!strcmp (gui_name, "term")) { + bx_gui_sighandler = 1; + } +#endif + + BX_ASSERT (bx_gui != NULL); + return true; +} + +int +bx_begin_simulation (int argc, char *argv[]) +{ + // deal with gui selection + if (!load_and_init_display_lib ()) { + BX_PANIC (("no gui module was loaded")); + return 0; + } +#if BX_GDBSTUB + // If using gdbstub, it will take control and call + // bx_init_hardware() and cpu_loop() + bx_gdbstub_init (argc, argv); +#elif BX_DEBUGGER + // If using the debugger, it will take control and call + // bx_init_hardware() and cpu_loop() + bx_dbg_main(argc, argv); +#else + + bx_init_hardware(); + + if (bx_options.load32bitOSImage.OwhichOS->get ()) { + void bx_load32bitOSimagehack(void); + bx_load32bitOSimagehack(); + } + + SIM->set_init_done (1); + + // update headerbar buttons since drive status can change during init + bx_gui->update_drive_status_buttons (); + + // The set handler for mouse_enabled does not actually update the gui + // until init_done is set. This forces the set handler to be called, + // which sets up the mouse enabled GUI-specific stuff correctly. + // Not a great solution but it works. BBD + bx_options.Omouse_enabled->set (bx_options.Omouse_enabled->get ()); + + if (BX_SMP_PROCESSORS == 1) { + // only one processor, run as fast as possible by not messing with + // quantums and loops. + BX_CPU(0)->cpu_loop(1); + // for one processor, the only reason for cpu_loop to return is + // that kill_bochs_request was set by the GUI interface. + } else { + // SMP simulation: do a few instructions on each processor, then switch + // to another. Increasing quantum speeds up overall performance, but + // reduces granularity of synchronization between processors. + int processor = 0; + int quantum = 5; + while (1) { + // do some instructions in each processor + BX_CPU(processor)->cpu_loop(quantum); + processor = (processor+1) % BX_SMP_PROCESSORS; + if (BX_CPU(0)->kill_bochs_request) + break; + if (processor == 0) + BX_TICKN(quantum); + } + } +#endif + BX_INFO (("cpu loop quit, shutting down simulator")); + bx_atexit (); + return(0); +} + + +int +bx_read_configuration (char *rcfile) +{ + // parse rcfile first, then parse arguments in order. + BX_INFO (("reading configuration from %s", rcfile)); + if (parse_bochsrc(rcfile) < 0) { + BX_PANIC (("reading from %s failed", rcfile)); + return -1; + } + // update log actions + for (int level=0; level<N_LOGLEV; level++) { + int action = SIM->get_default_log_action (level); + io->set_log_action (level, action); + } + return 0; +} + +int bx_parse_cmdline (int arg, int argc, char *argv[]) +{ + //if (arg < argc) BX_INFO (("parsing command line arguments")); + + while (arg < argc) { + BX_INFO (("parsing arg %d, %s", arg, argv[arg])); + parse_line_unformatted("cmdline args", argv[arg]); + arg++; + } + // update log actions + for (int level=0; level<N_LOGLEV; level++) { + int action = SIM->get_default_log_action (level); + io->set_log_action (level, action); + } + return 0; +} + + int +bx_init_hardware() +{ + // all configuration has been read, now initialize everything. + + if (SIM->get_param_enum(BXP_BOCHS_START)->get ()==BX_QUICK_START) { + for (int level=0; level<N_LOGLEV; level++) { + int action = SIM->get_default_log_action (level); +#if !BX_USE_CONFIG_INTERFACE + if (action == ACT_ASK) action = ACT_FATAL; +#endif + io->set_log_action (level, action); + } + } + + bx_pc_system.init_ips(bx_options.Oips->get ()); + + if(bx_options.log.Ofilename->getptr()[0]!='-') { + BX_INFO (("using log file %s", bx_options.log.Ofilename->getptr ())); + io->init_log(bx_options.log.Ofilename->getptr ()); + } + + io->set_log_prefix(bx_options.log.Oprefix->getptr()); + + // Output to the log file the cpu settings + // This will by handy for bug reports + BX_INFO(("Bochs x86 Emulator %s", VER_STRING)); + BX_INFO((" %s", REL_STRING)); + BX_INFO(("System configuration")); + BX_INFO((" processors: %d",BX_SMP_PROCESSORS)); + BX_INFO((" A20 line support: %s",BX_SUPPORT_A20?"yes":"no")); + BX_INFO((" APIC support: %s",BX_SUPPORT_APIC?"yes":"no")); + +#ifndef BX_USE_VMX + BX_INFO(("CPU configuration")); + BX_INFO((" level: %d",BX_CPU_LEVEL)); + BX_INFO((" fpu support: %s",BX_SUPPORT_FPU?"yes":"no")); + BX_INFO((" paging support: %s, tlb enabled: %s",BX_SUPPORT_PAGING?"yes":"no",BX_USE_TLB?"yes":"no")); + BX_INFO((" mmx support: %s",BX_SUPPORT_MMX?"yes":"no")); + BX_INFO((" sse support: %s",BX_SUPPORT_SSE==2?"2":BX_SUPPORT_SSE==1?"1":"no")); + BX_INFO((" v8086 mode support: %s",BX_SUPPORT_V8086_MODE?"yes":"no")); + BX_INFO((" 3dnow! support: %s",BX_SUPPORT_3DNOW?"yes":"no")); + BX_INFO((" PAE support: %s",BX_SupportPAE?"yes":"no")); + BX_INFO((" PGE support: %s",BX_SupportGlobalPages?"yes":"no")); + BX_INFO((" PSE support: %s",BX_SUPPORT_4MEG_PAGES?"yes":"no")); + BX_INFO((" x86-64 support: %s",BX_SUPPORT_X86_64?"yes":"no")); + BX_INFO((" SEP support: %s",BX_SUPPORT_SEP?"yes":"no")); + BX_INFO(("Optimization configuration")); + BX_INFO((" Guest2HostTLB support: %s",BX_SupportGuest2HostTLB?"yes":"no")); + BX_INFO((" RepeatSpeedups support: %s",BX_SupportRepeatSpeedups?"yes":"no")); + BX_INFO((" Icache support: %s",BX_SupportICache?"yes":"no")); + BX_INFO((" Host Asm support: %s",BX_SupportHostAsms?"yes":"no")); +#endif /* BX_USE_VMX */ + + // set up memory and CPU objects +#if BX_SUPPORT_APIC + bx_generic_apic_c::reset_all_ids (); +#endif + +#ifndef BX_USE_VMX + // Check if there is a romimage + if (strcmp(bx_options.rom.Opath->getptr (),"") == 0) { + BX_ERROR(("No romimage to load. Is your bochsrc file loaded/valid ?")); + } + +#if BX_SMP_PROCESSORS==1 + BX_MEM(0)->init_memory(bx_options.memory.Osize->get () * 1024*1024); + + // First load the optional ROM images + if (strcmp(bx_options.optrom[0].Opath->getptr (),"") !=0 ) + BX_MEM(0)->load_ROM(bx_options.optrom[0].Opath->getptr (), bx_options.optrom[0].Oaddress->get (), 2); + if (strcmp(bx_options.optrom[1].Opath->getptr (),"") !=0 ) + BX_MEM(0)->load_ROM(bx_options.optrom[1].Opath->getptr (), bx_options.optrom[1].Oaddress->get (), 2); + if (strcmp(bx_options.optrom[2].Opath->getptr (),"") !=0 ) + BX_MEM(0)->load_ROM(bx_options.optrom[2].Opath->getptr (), bx_options.optrom[2].Oaddress->get (), 2); + if (strcmp(bx_options.optrom[3].Opath->getptr (),"") !=0 ) + BX_MEM(0)->load_ROM(bx_options.optrom[3].Opath->getptr (), bx_options.optrom[3].Oaddress->get (), 2); + + // Then Load the BIOS and VGABIOS + BX_MEM(0)->load_ROM(bx_options.rom.Opath->getptr (), bx_options.rom.Oaddress->get (), 0); + BX_MEM(0)->load_ROM(bx_options.vgarom.Opath->getptr (), 0xc0000, 1); + + BX_CPU(0)->init (BX_MEM(0)); + BX_CPU(0)->set_cpu_id(0); +#if BX_SUPPORT_APIC + BX_CPU(0)->local_apic.set_id (0); +#endif + BX_INSTR_INIT(0); + BX_CPU(0)->reset(BX_RESET_HARDWARE); +#else + // SMP initialization + bx_mem_array[0] = new BX_MEM_C (); + bx_mem_array[0]->init_memory(bx_options.memory.Osize->get () * 1024*1024); + + // First load the optional ROM images + if (strcmp(bx_options.optrom[0].Opath->getptr (),"") !=0 ) + bx_mem_array[0]->load_ROM(bx_options.optrom[0].Opath->getptr (), bx_options.optrom[0].Oaddress->get (), 2); + if (strcmp(bx_options.optrom[1].Opath->getptr (),"") !=0 ) + bx_mem_array[0]->load_ROM(bx_options.optrom[1].Opath->getptr (), bx_options.optrom[1].Oaddress->get (), 2); + if (strcmp(bx_options.optrom[2].Opath->getptr (),"") !=0 ) + bx_mem_array[0]->load_ROM(bx_options.optrom[2].Opath->getptr (), bx_options.optrom[2].Oaddress->get (), 2); + if (strcmp(bx_options.optrom[3].Opath->getptr (),"") !=0 ) + bx_mem_array[0]->load_ROM(bx_options.optrom[3].Opath->getptr (), bx_options.optrom[3].Oaddress->get (), 2); + + // Then Load the BIOS and VGABIOS + bx_mem_array[0]->load_ROM(bx_options.rom.Opath->getptr (), bx_options.rom.Oaddress->get (), 0); + bx_mem_array[0]->load_ROM(bx_options.vgarom.Opath->getptr (), 0xc0000, 1); + + for (int i=0; i<BX_SMP_PROCESSORS; i++) { + BX_CPU(i) = new BX_CPU_C; + BX_CPU(i)->init (bx_mem_array[0]); + // assign apic ID from the index of this loop + // if !BX_SUPPORT_APIC, this will not compile. + BX_CPU(i)->set_cpu_id(i); + BX_CPU(i)->local_apic.set_id (i); + BX_INSTR_INIT(i); + BX_CPU(i)->reset(BX_RESET_HARDWARE); + } +#endif +#else + // Assume UP for now for VMX + bx_mem.init_memory(megabytes * 1024 * 1024); + bx_cpu.init(&bx_mem); +#endif // BX_USE_VMX + +#if BX_DEBUGGER == 0 + DEV_init_devices(); + DEV_reset_devices(BX_RESET_HARDWARE); + bx_gui->init_signal_handlers (); + bx_pc_system.start_timers(); +#endif + BX_DEBUG(("bx_init_hardware is setting signal handlers")); +// if not using debugger, then we can take control of SIGINT. +#if !BX_DEBUGGER + signal(SIGINT, bx_signal_handler); +#endif + +#if BX_SHOW_IPS +#ifndef __MINGW32__ + signal(SIGALRM, bx_signal_handler); +#endif + alarm( 1 ); +#endif + + return(0); +} + + + + void +bx_init_bx_dbg (void) +{ + bx_dbg.floppy = 0; + bx_dbg.keyboard = 0; + bx_dbg.video = 0; + bx_dbg.disk = 0; + bx_dbg.pit = 0; + bx_dbg.pic = 0; + bx_dbg.bios = 0; + bx_dbg.cmos = 0; + bx_dbg.a20 = 0; + bx_dbg.interrupts = 0; + bx_dbg.exceptions = 0; + bx_dbg.unsupported = 0; + bx_dbg.temp = 0; + bx_dbg.reset = 0; + bx_dbg.mouse = 0; + bx_dbg.io = 0; + bx_dbg.debugger = 0; + bx_dbg.xms = 0; + bx_dbg.v8086 = 0; + bx_dbg.paging = 0; + bx_dbg.creg = 0; + bx_dbg.dreg = 0; + bx_dbg.dma = 0; + bx_dbg.unsupported_io = 0; + bx_dbg.record_io = 0; + bx_dbg.serial = 0; + bx_dbg.cdrom = 0; +#ifdef MAGIC_BREAKPOINT + bx_dbg.magic_break_enabled = 0; +#endif + +} + + +int +bx_atexit(void) +{ + static bx_bool been_here = 0; + if (been_here) return 1; // protect from reentry + been_here = 1; + + // in case we ended up in simulation mode, change back to config mode + // so that the user can see any messages left behind on the console. + SIM->set_display_mode (DISP_MODE_CONFIG); + +#if BX_PROVIDE_DEVICE_MODELS==1 + bx_pc_system.exit(); +#endif + +#if BX_DEBUGGER == 0 + if (SIM && SIM->get_init_done ()) { + for (int cpu=0; cpu<BX_SMP_PROCESSORS; cpu++) + if (BX_CPU(cpu)) BX_CPU(cpu)->atexit(); + } +#endif + +#if BX_PCI_SUPPORT + if (bx_options.Oi440FXSupport->get ()) { + bx_devices.pluginPciBridge->print_i440fx_state(); + } +#endif + + // restore signal handling to defaults +#if !BX_DEBUGGER + BX_INFO (("restoring default signal behavior")); + signal(SIGINT, SIG_DFL); +#endif + +#if BX_SHOW_IPS +#ifndef __MINGW32__ + signal(SIGALRM, SIG_DFL); +#endif +#endif + return 0; +} + +#if BX_PROVIDE_MAIN + +char * +bx_find_bochsrc () +{ + FILE *fd = NULL; + char rcfile[512]; + Bit32u retry = 0, found = 0; + // try several possibilities for the bochsrc before giving up + while (!found) { + rcfile[0] = 0; + switch (retry++) { + case 0: strcpy (rcfile, ".bochsrc"); break; + case 1: strcpy (rcfile, "bochsrc"); break; + case 2: strcpy (rcfile, "bochsrc.txt"); break; +#ifdef WIN32 + case 3: strcpy (rcfile, "bochsrc.bxrc"); break; +#elif !BX_WITH_MACOS + // only try this on unix + case 3: + { + char *ptr = getenv("HOME"); + if (ptr) snprintf (rcfile, sizeof(rcfile), "%s/.bochsrc", ptr); + } + break; + case 4: strcpy (rcfile, "/etc/bochsrc"); break; +#endif + default: + return NULL; + } + if (rcfile[0]) { + BX_DEBUG (("looking for configuration in %s", rcfile)); + fd = fopen(rcfile, "r"); + if (fd) found = 1; + } + } + assert (fd != NULL && rcfile[0] != 0); + fclose (fd); + return strdup (rcfile); +} + + static int +parse_bochsrc(char *rcfile) +{ + FILE *fd = NULL; + char *ret; + char line[512]; + + // try several possibilities for the bochsrc before giving up + + bochsrc_include_count++; + + fd = fopen (rcfile, "r"); + if (fd == NULL) return -1; + + int retval = 0; + do { + ret = fgets(line, sizeof(line)-1, fd); + line[sizeof(line) - 1] = '\0'; + int len = strlen(line); + if (len>0) + line[len-1] = '\0'; + if ((ret != NULL) && strlen(line)) { + if (parse_line_unformatted(rcfile, line) < 0) { + retval = -1; + break; // quit parsing after first error + } + } + } while (!feof(fd)); + fclose(fd); + bochsrc_include_count--; + return retval; +} + + static Bit32s +parse_line_unformatted(char *context, char *line) +{ +#define MAX_PARAMS_LEN 40 + char *ptr; + unsigned i, string_i; + char string[512]; + char *params[MAX_PARAMS_LEN]; + int num_params; + bx_bool inquotes = 0; + bx_bool comment = 0; + + memset(params, 0, sizeof(params)); + if (line == NULL) return 0; + + // if passed nothing but whitespace, just return + for (i=0; i<strlen(line); i++) { + if (!isspace(line[i])) break; + } + if (i>=strlen(line)) + return 0; + + num_params = 0; + + if (!strncmp(line, "#include", 8)) + ptr = strtok(line, " "); + else + ptr = strtok(line, ":"); + while ((ptr) && (!comment)) { + string_i = 0; + for (i=0; i<strlen(ptr); i++) { + if (ptr[i] == '"') + inquotes = !inquotes; + else if ((ptr[i] == '#') && (strncmp(line+i, "#include", 8)) && !inquotes) { + comment = 1; + break; + } else { +#if BX_HAVE_GETENV + // substitute environment variables. + if (ptr[i] == '$') { + char varname[512]; + char *pv = varname; + char *value; + *pv = 0; + i++; + while (isalpha(ptr[i]) || ptr[i]=='_') { + *pv = ptr[i]; pv++; i++; + } + *pv = 0; + if (strlen(varname)<1 || !(value = getenv(varname))) { + BX_PANIC (("could not look up environment variable '%s'", varname)); + } else { + // append value to the string + for (pv=value; *pv; pv++) + string[string_i++] = *pv; + } + } +#endif + if (!isspace(ptr[i]) || inquotes) { + string[string_i++] = ptr[i]; + } + } + } + string[string_i] = '\0'; + if (string_i == 0) break; + if ( params[num_params] != NULL ) + { + free(params[num_params]); + params[num_params] = NULL; + } + if ( num_params < MAX_PARAMS_LEN ) + { + params[num_params++] = strdup (string); + ptr = strtok(NULL, ","); + } + else + { + BX_PANIC (("too many parameters, max is %d\n", MAX_PARAMS_LEN)); + } + } + Bit32s retval = parse_line_formatted(context, num_params, ¶ms[0]); + for (i=0; i < MAX_PARAMS_LEN; i++) + { + if ( params[i] != NULL ) + { + free(params[i]); + params[i] = NULL; + } + } + return retval; +} + +// These macros are called for all parse errors, so that we can easily +// change the behavior of all occurrences. +#define PARSE_ERR(x) \ + do { BX_PANIC(x); return -1; } while (0) +#define PARSE_WARN(x) \ + BX_ERROR(x) + + static Bit32s +parse_line_formatted(char *context, int num_params, char *params[]) +{ + int i; + + if (num_params < 1) return 0; + if (num_params < 2) { + PARSE_ERR(("%s: a bochsrc option needs at least one parameter", context)); + } + + if (!strcmp(params[0], "#include")) { + if (num_params != 2) { + PARSE_ERR(("%s: ignoring malformed #include directive.", context)); + } + if (!strcmp(params[1], context)) { + PARSE_ERR(("%s: cannot include this file again.", context)); + } + if (bochsrc_include_count == 2) { + PARSE_ERR(("%s: include directive in an included file not supported yet.", context)); + } + bx_read_configuration(params[1]); + } + else if (!strcmp(params[0], "floppya")) { + for (i=1; i<num_params; i++) { + if (!strncmp(params[i], "2_88=", 5)) { + bx_options.floppya.Opath->set (¶ms[i][5]); + bx_options.floppya.Otype->set (BX_FLOPPY_2_88); + } + else if (!strncmp(params[i], "1_44=", 5)) { + bx_options.floppya.Opath->set (¶ms[i][5]); + bx_options.floppya.Otype->set (BX_FLOPPY_1_44); + } + else if (!strncmp(params[i], "1_2=", 4)) { + bx_options.floppya.Opath->set (¶ms[i][4]); + bx_options.floppya.Otype->set (BX_FLOPPY_1_2); + } + else if (!strncmp(params[i], "720k=", 5)) { + bx_options.floppya.Opath->set (¶ms[i][5]); + bx_options.floppya.Otype->set (BX_FLOPPY_720K); + } + else if (!strncmp(params[i], "360k=", 5)) { + bx_options.floppya.Opath->set (¶ms[i][5]); + bx_options.floppya.Otype->set (BX_FLOPPY_360K); + } + // use CMOS reserved types? + else if (!strncmp(params[i], "160k=", 5)) { + bx_options.floppya.Opath->set (¶ms[i][5]); + bx_options.floppya.Otype->set (BX_FLOPPY_160K); + } + else if (!strncmp(params[i], "180k=", 5)) { + bx_options.floppya.Opath->set (¶ms[i][5]); + bx_options.floppya.Otype->set (BX_FLOPPY_180K); + } + else if (!strncmp(params[i], "320k=", 5)) { + bx_options.floppya.Opath->set (¶ms[i][5]); + bx_options.floppya.Otype->set (BX_FLOPPY_320K); + } + else if (!strncmp(params[i], "status=ejected", 14)) { + bx_options.floppya.Ostatus->set (BX_EJECTED); + } + else if (!strncmp(params[i], "status=inserted", 15)) { + bx_options.floppya.Ostatus->set (BX_INSERTED); + } + else { + PARSE_ERR(("%s: floppya attribute '%s' not understood.", context, + params[i])); + } + } + } + else if (!strcmp(params[0], "gdbstub_port")) + { + if (num_params != 2) + { + fprintf(stderr, ".bochsrc: gdbstub_port directive: wrong # args.\n"); + exit(1); + } + bx_options.gdbstub.port = atoi(params[1]); + } + else if (!strcmp(params[0], "gdbstub_text_base")) + { + if (num_params != 2) + { + fprintf(stderr, ".bochsrc: gdbstub_text_base directive: wrong # args.\n"); + exit(1); + } + bx_options.gdbstub.text_base = atoi(params[1]); + } + else if (!strcmp(params[0], "gdbstub_data_base")) + { + if (num_params != 2) + { + fprintf(stderr, ".bochsrc: gdbstub_data_base directive: wrong # args.\n"); + exit(1); + } + bx_options.gdbstub.data_base = atoi(params[1]); + } + else if (!strcmp(params[0], "gdbstub_bss_base")) + { + if (num_params != 2) + { + fprintf(stderr, ".bochsrc: gdbstub_bss_base directive: wrong # args.\n"); + exit(1); + } + bx_options.gdbstub.bss_base = atoi(params[1]); + } + + else if (!strcmp(params[0], "floppyb")) { + for (i=1; i<num_params; i++) { + if (!strncmp(params[i], "2_88=", 5)) { + bx_options.floppyb.Opath->set (¶ms[i][5]); + bx_options.floppyb.Otype->set (BX_FLOPPY_2_88); + } + else if (!strncmp(params[i], "1_44=", 5)) { + bx_options.floppyb.Opath->set (¶ms[i][5]); + bx_options.floppyb.Otype->set (BX_FLOPPY_1_44); + } + else if (!strncmp(params[i], "1_2=", 4)) { + bx_options.floppyb.Opath->set (¶ms[i][4]); + bx_options.floppyb.Otype->set (BX_FLOPPY_1_2); + } + else if (!strncmp(params[i], "720k=", 5)) { + bx_options.floppyb.Opath->set (¶ms[i][5]); + bx_options.floppyb.Otype->set (BX_FLOPPY_720K); + } + else if (!strncmp(params[i], "360k=", 5)) { + bx_options.floppyb.Opath->set (¶ms[i][5]); + bx_options.floppyb.Otype->set (BX_FLOPPY_360K); + } + // use CMOS reserved types? + else if (!strncmp(params[i], "160k=", 5)) { + bx_options.floppyb.Opath->set (¶ms[i][5]); + bx_options.floppyb.Otype->set (BX_FLOPPY_160K); + } + else if (!strncmp(params[i], "180k=", 5)) { + bx_options.floppyb.Opath->set (¶ms[i][5]); + bx_options.floppyb.Otype->set (BX_FLOPPY_180K); + } + else if (!strncmp(params[i], "320k=", 5)) { + bx_options.floppyb.Opath->set (¶ms[i][5]); + bx_options.floppyb.Otype->set (BX_FLOPPY_320K); + } + else if (!strncmp(params[i], "status=ejected", 14)) { + bx_options.floppyb.Ostatus->set (BX_EJECTED); + } + else if (!strncmp(params[i], "status=inserted", 15)) { + bx_options.floppyb.Ostatus->set (BX_INSERTED); + } + else { + PARSE_ERR(("%s: floppyb attribute '%s' not understood.", context, + params[i])); + } + } + } + + else if ((!strncmp(params[0], "ata", 3)) && (strlen(params[0]) == 4)) { + Bit8u channel = params[0][3]; + + if ((channel < '0') || (channel > '9')) { + PARSE_ERR(("%s: ataX directive malformed.", context)); + } + channel-='0'; + if (channel >= BX_MAX_ATA_CHANNEL) { + PARSE_ERR(("%s: ataX directive malformed.", context)); + } + + if ((num_params < 2) || (num_params > 5)) { + PARSE_ERR(("%s: ataX directive malformed.", context)); + } + + if (strncmp(params[1], "enabled=", 8)) { + PARSE_ERR(("%s: ataX directive malformed.", context)); + } + else { + bx_options.ata[channel].Opresent->set (atol(¶ms[1][8])); + } + + if (num_params > 2) { + if (strncmp(params[2], "ioaddr1=", 8)) { + PARSE_ERR(("%s: ataX directive malformed.", context)); + } + else { + if ( (params[2][8] == '0') && (params[2][9] == 'x') ) + bx_options.ata[channel].Oioaddr1->set (strtoul (¶ms[2][8], NULL, 16)); + else + bx_options.ata[channel].Oioaddr1->set (strtoul (¶ms[2][8], NULL, 10)); + } + } + + if (num_params > 3) { + if (strncmp(params[3], "ioaddr2=", 8)) { + PARSE_ERR(("%s: ataX directive malformed.", context)); + } + else { + if ( (params[3][8] == '0') && (params[3][9] == 'x') ) + bx_options.ata[channel].Oioaddr2->set (strtoul (¶ms[3][8], NULL, 16)); + else + bx_options.ata[channel].Oioaddr2->set (strtoul (¶ms[3][8], NULL, 10)); + } + } + + if (num_params > 4) { + if (strncmp(params[4], "irq=", 4)) { + PARSE_ERR(("%s: ataX directive malformed.", context)); + } + else { + bx_options.ata[channel].Oirq->set (atol(¶ms[4][4])); + } + } + } + + // ataX-master, ataX-slave + else if ((!strncmp(params[0], "ata", 3)) && (strlen(params[0]) > 4)) { + Bit8u channel = params[0][3], slave = 0; + + if ((channel < '0') || (channel > '9')) { + PARSE_ERR(("%s: ataX-master/slave directive malformed.", context)); + } + channel-='0'; + if (channel >= BX_MAX_ATA_CHANNEL) { + PARSE_ERR(("%s: ataX-master/slave directive malformed.", context)); + } + + if ((strcmp(¶ms[0][4], "-slave")) && + (strcmp(¶ms[0][4], "-master"))) { + PARSE_ERR(("%s: ataX-master/slave directive malformed.", context)); + } + + if (!strcmp(¶ms[0][4], "-slave")) { + slave = 1; + } + + // This was originally meant to warn users about both diskc + // and ata0-master defined, but it also prevent users to + // override settings on the command line + // (see [ 661010 ] cannot override ata-settings from cmdline) + // if (bx_options.atadevice[channel][slave].Opresent->get()) { + // BX_INFO(("%s: %s device of ata channel %d already defined.", context, slave?"slave":"master",channel)); + // } + + for (i=1; i<num_params; i++) { + if (!strcmp(params[i], "type=disk")) { + bx_options.atadevice[channel][slave].Otype->set (BX_ATA_DEVICE_DISK); + } + else if (!strcmp(params[i], "type=cdrom")) { + bx_options.atadevice[channel][slave].Otype->set (BX_ATA_DEVICE_CDROM); + } + else if (!strcmp(params[i], "mode=flat")) { + bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_FLAT); + } + else if (!strcmp(params[i], "mode=concat")) { + bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_CONCAT); + } + else if (!strcmp(params[i], "mode=external")) { + bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_EXTDISKSIM); + } + else if (!strcmp(params[i], "mode=dll")) { + bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_DLL_HD); + } + else if (!strcmp(params[i], "mode=sparse")) { + bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_SPARSE); + } + else if (!strcmp(params[i], "mode=vmware3")) { + bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_VMWARE3); + } +// else if (!strcmp(params[i], "mode=split")) { +// bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_SPLIT); +// } + else if (!strcmp(params[i], "mode=undoable")) { + bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_UNDOABLE); + } + else if (!strcmp(params[i], "mode=growing")) { + bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_GROWING); + } + else if (!strcmp(params[i], "mode=volatile")) { + bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_VOLATILE); + } +// else if (!strcmp(params[i], "mode=z-undoable")) { +// bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_Z_UNDOABLE); +// } +// else if (!strcmp(params[i], "mode=z-volatile")) { +// bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_Z_VOLATILE); +// } + else if (!strncmp(params[i], "path=", 5)) { + bx_options.atadevice[channel][slave].Opath->set (¶ms[i][5]); + } + else if (!strncmp(params[i], "cylinders=", 10)) { + bx_options.atadevice[channel][slave].Ocylinders->set (atol(¶ms[i][10])); + } + else if (!strncmp(params[i], "heads=", 6)) { + bx_options.atadevice[channel][slave].Oheads->set (atol(¶ms[i][6])); + } + else if (!strncmp(params[i], "spt=", 4)) { + bx_options.atadevice[channel][slave].Ospt->set (atol(¶ms[i][4])); + } + else if (!strncmp(params[i], "model=", 6)) { + bx_options.atadevice[channel][slave].Omodel->set(¶ms[i][6]); + } + else if (!strcmp(params[i], "biosdetect=none")) { + bx_options.atadevice[channel][slave].Obiosdetect->set(BX_ATA_BIOSDETECT_NONE); + } + else if (!strcmp(params[i], "biosdetect=cmos")) { + bx_options.atadevice[channel][slave].Obiosdetect->set(BX_ATA_BIOSDETECT_CMOS); + } + else if (!strcmp(params[i], "biosdetect=auto")) { + bx_options.atadevice[channel][slave].Obiosdetect->set(BX_ATA_BIOSDETECT_AUTO); + } + else if (!strcmp(params[i], "translation=none")) { + bx_options.atadevice[channel][slave].Otranslation->set(BX_ATA_TRANSLATION_NONE); + } + else if (!strcmp(params[i], "translation=lba")) { + bx_options.atadevice[channel][slave].Otranslation->set(BX_ATA_TRANSLATION_LBA); + } + else if (!strcmp(params[i], "translation=large")) { + bx_options.atadevice[channel][slave].Otranslation->set(BX_ATA_TRANSLATION_LARGE); + } + else if (!strcmp(params[i], "translation=echs")) { // synonym of large + bx_options.atadevice[channel][slave].Otranslation->set(BX_ATA_TRANSLATION_LARGE); + } + else if (!strcmp(params[i], "translation=rechs")) { + bx_options.atadevice[channel][slave].Otranslation->set(BX_ATA_TRANSLATION_RECHS); + } + else if (!strcmp(params[i], "translation=auto")) { + bx_options.atadevice[channel][slave].Otranslation->set(BX_ATA_TRANSLATION_AUTO); + } + else if (!strcmp(params[i], "status=ejected")) { + bx_options.atadevice[channel][slave].Ostatus->set(BX_EJECTED); + } + else if (!strcmp(params[i], "status=inserted")) { + bx_options.atadevice[channel][slave].Ostatus->set(BX_INSERTED); + } + else if (!strncmp(params[i], "journal=", 8)) { + bx_options.atadevice[channel][slave].Ojournal->set(¶ms[i][8]); + } + else { + PARSE_ERR(("%s: ataX-master/slave directive malformed.", context)); + } + } + + // Enables the ata device + bx_options.atadevice[channel][slave].Opresent->set(1); + + // if enabled, check if device ok + if (bx_options.atadevice[channel][slave].Opresent->get() == 1) { + if (bx_options.atadevice[channel][slave].Otype->get() == BX_ATA_DEVICE_DISK) { + if (strlen(bx_options.atadevice[channel][slave].Opath->getptr()) ==0) + PARSE_WARN(("%s: ataX-master/slave has empty path", context)); + if ((bx_options.atadevice[channel][slave].Ocylinders->get() == 0) || + (bx_options.atadevice[channel][slave].Oheads->get() ==0 ) || + (bx_options.atadevice[channel][slave].Ospt->get() == 0)) { + PARSE_WARN(("%s: ataX-master/slave cannot have zero cylinders, heads, or sectors/track", context)); + } + } + else if (bx_options.atadevice[channel][slave].Otype->get() == BX_ATA_DEVICE_CDROM) { + if (strlen(bx_options.atadevice[channel][slave].Opath->getptr()) == 0) { + PARSE_WARN(("%s: ataX-master/slave has empty path", context)); + } + } + else { + PARSE_WARN(("%s: ataX-master/slave: type should be specified", context)); + } + } + } + + // Legacy disk options emulation + else if (!strcmp(params[0], "diskc")) { // DEPRECATED + BX_INFO(("WARNING: diskc directive is deprecated, use ata0-master: instead")); + if (bx_options.atadevice[0][0].Opresent->get()) { + PARSE_ERR(("%s: master device of ata channel 0 already defined.", context)); + } + if (num_params != 5) { + PARSE_ERR(("%s: diskc directive malformed.", context)); + } + if (strncmp(params[1], "file=", 5) || + strncmp(params[2], "cyl=", 4) || + strncmp(params[3], "heads=", 6) || + strncmp(params[4], "spt=", 4)) { + PARSE_ERR(("%s: diskc directive malformed.", context)); + } + bx_options.ata[0].Opresent->set(1); + bx_options.atadevice[0][0].Otype->set (BX_ATA_DEVICE_DISK); + bx_options.atadevice[0][0].Opath->set (¶ms[1][5]); + bx_options.atadevice[0][0].Ocylinders->set (atol(¶ms[2][4])); + bx_options.atadevice[0][0].Oheads->set (atol(¶ms[3][6])); + bx_options.atadevice[0][0].Ospt->set (atol(¶ms[4][4])); + bx_options.atadevice[0][0].Opresent->set (1); + } + else if (!strcmp(params[0], "diskd")) { // DEPRECATED + BX_INFO(("WARNING: diskd directive is deprecated, use ata0-slave: instead")); + if (bx_options.atadevice[0][1].Opresent->get()) { + PARSE_ERR(("%s: slave device of ata channel 0 already defined.", context)); + } + if (num_params != 5) { + PARSE_ERR(("%s: diskd directive malformed.", context)); + } + if (strncmp(params[1], "file=", 5) || + strncmp(params[2], "cyl=", 4) || + strncmp(params[3], "heads=", 6) || + strncmp(params[4], "spt=", 4)) { + PARSE_ERR(("%s: diskd directive malformed.", context)); + } + bx_options.ata[0].Opresent->set(1); + bx_options.atadevice[0][1].Otype->set (BX_ATA_DEVICE_DISK); + bx_options.atadevice[0][1].Opath->set (¶ms[1][5]); + bx_options.atadevice[0][1].Ocylinders->set (atol( ¶ms[2][4])); + bx_options.atadevice[0][1].Oheads->set (atol( ¶ms[3][6])); + bx_options.atadevice[0][1].Ospt->set (atol( ¶ms[4][4])); + bx_options.atadevice[0][1].Opresent->set (1); + } + else if (!strcmp(params[0], "cdromd")) { // DEPRECATED + BX_INFO(("WARNING: cdromd directive is deprecated, use ata0-slave: instead")); + if (bx_options.atadevice[0][1].Opresent->get()) { + PARSE_ERR(("%s: slave device of ata channel 0 already defined.", context)); + } + if (num_params != 3) { + PARSE_ERR(("%s: cdromd directive malformed.", context)); + } + if (strncmp(params[1], "dev=", 4) || strncmp(params[2], "status=", 7)) { + PARSE_ERR(("%s: cdromd directive malformed.", context)); + } + bx_options.ata[0].Opresent->set(1); + bx_options.atadevice[0][1].Otype->set (BX_ATA_DEVICE_CDROM); + bx_options.atadevice[0][1].Opath->set (¶ms[1][4]); + if (!strcmp(params[2], "status=inserted")) + bx_options.atadevice[0][1].Ostatus->set (BX_INSERTED); + else if (!strcmp(params[2], "status=ejected")) + bx_options.atadevice[0][1].Ostatus->set (BX_EJECTED); + else { + PARSE_ERR(("%s: cdromd directive malformed.", context)); + } + bx_options.atadevice[0][1].Opresent->set (1); + } + + else if (!strcmp(params[0], "boot")) { + if (!strcmp(params[1], "a")) { + bx_options.Obootdrive->set (BX_BOOT_FLOPPYA); + } else if (!strcmp(params[1], "floppy")) { + bx_options.Obootdrive->set (BX_BOOT_FLOPPYA); + } else if (!strcmp(params[1], "c")) { + bx_options.Obootdrive->set (BX_BOOT_DISKC); + } else if (!strcmp(params[1], "disk")) { + bx_options.Obootdrive->set (BX_BOOT_DISKC); + } else if (!strcmp(params[1], "cdrom")) { + bx_options.Obootdrive->set (BX_BOOT_CDROM); + } else { + PARSE_ERR(("%s: boot directive with unknown boot device '%s'. use 'floppy', 'disk' or 'cdrom'.", context, params[1])); + } + } + + else if (!strcmp(params[0], "com1")) { + for (i=1; i<num_params; i++) { + if (!strncmp(params[i], "enabled=", 8)) { + bx_options.com[0].Oenabled->set (atol(¶ms[i][8])); + } + else if (!strncmp(params[i], "dev=", 4)) { + bx_options.com[0].Odev->set (¶ms[i][4]); + bx_options.com[0].Oenabled->set (1); + } + else { + PARSE_ERR(("%s: unknown parameter for com1 ignored.", context)); + } + } + } +#if 0 + else if (!strcmp(params[0], "com2")) { + for (i=1; i<num_params; i++) { + if (!strncmp(params[i], "enabled=", 8)) { + bx_options.com[1].Oenabled->set (atol(¶ms[i][8])); + } + else if (!strncmp(params[i], "dev=", 4)) { + bx_options.com[1].Odev->set (¶ms[i][4]); + bx_options.com[1].Oenabled->set (1); + } + else { + PARSE_ERR(("%s: unknown parameter for com2 ignored.", context)); + } + } + } + else if (!strcmp(params[0], "com3")) { + for (i=1; i<num_params; i++) { + if (!strncmp(params[i], "enabled=", 8)) { + bx_options.com[2].Oenabled->set (atol(¶ms[i][8])); + } + else if (!strncmp(params[i], "dev=", 4)) { + bx_options.com[2].Odev->set (¶ms[i][4]); + bx_options.com[2].Oenabled->set (1); + } + else { + PARSE_ERR(("%s: unknown parameter for com3 ignored.", context)); + } + } + } + else if (!strcmp(params[0], "com4")) { + for (i=1; i<num_params; i++) { + if (!strncmp(params[i], "enabled=", 8)) { + bx_options.com[3].Oenabled->set (atol(¶ms[i][8])); + } + else if (!strncmp(params[i], "dev=", 4)) { + bx_options.com[3].Odev->set (¶ms[i][4]); + bx_options.com[3].Oenabled->set (1); + } + else { + PARSE_ERR(("%s: unknown parameter for com4 ignored.", context)); + } + } + } +#endif + else if (!strcmp(params[0], "usb1")) { + for (i=1; i<num_params; i++) { + if (!strncmp(params[i], "enabled=", 8)) { + bx_options.usb[0].Oenabled->set (atol(¶ms[i][8])); + } + else if (!strncmp(params[i], "ioaddr=", 7)) { + if ( (params[i][7] == '0') && (params[i][8] == 'x') ) + bx_options.usb[0].Oioaddr->set (strtoul (¶ms[i][7], NULL, 16)); + else + bx_options.usb[0].Oioaddr->set (strtoul (¶ms[i][7], NULL, 10)); + bx_options.usb[0].Oenabled->set (1); + } + else if (!strncmp(params[i], "irq=", 4)) { + bx_options.usb[0].Oirq->set (atol(¶ms[i][4])); + } + else { + PARSE_ERR(("%s: unknown parameter for usb1 ignored.", context)); + } + } + } + else if (!strcmp(params[0], "floppy_bootsig_check")) { + if (num_params != 2) { + PARSE_ERR(("%s: floppy_bootsig_check directive malformed.", context)); + } + if (strncmp(params[1], "disabled=", 9)) { + PARSE_ERR(("%s: floppy_bootsig_check directive malformed.", context)); + } + if (params[1][9] == '0') + bx_options.OfloppySigCheck->set (0); + else if (params[1][9] == '1') + bx_options.OfloppySigCheck->set (1); + else { + PARSE_ERR(("%s: floppy_bootsig_check directive malformed.", context)); + } + } + else if (!strcmp(params[0], "log")) { + if (num_params != 2) { + PARSE_ERR(("%s: log directive has wrong # args.", context)); + } + bx_options.log.Ofilename->set (params[1]); + } + else if (!strcmp(params[0], "logprefix")) { + if (num_params != 2) { + PARSE_ERR(("%s: logprefix directive has wrong # args.", context)); + } + bx_options.log.Oprefix->set (params[1]); + } + else if (!strcmp(params[0], "debugger_log")) { + if (num_params != 2) { + PARSE_ERR(("%s: debugger_log directive has wrong # args.", context)); + } + bx_options.log.Odebugger_filename->set (params[1]); + } + else if (!strcmp(params[0], "panic")) { + if (num_params != 2) { + PARSE_ERR(("%s: panic directive malformed.", context)); + } + if (strncmp(params[1], "action=", 7)) { + PARSE_ERR(("%s: panic directive malformed.", context)); + } + char *action = 7 + params[1]; + if (!strcmp(action, "fatal")) + SIM->set_default_log_action (LOGLEV_PANIC, ACT_FATAL); + else if (!strcmp (action, "report")) + SIM->set_default_log_action (LOGLEV_PANIC, ACT_REPORT); + else if (!strcmp (action, "ignore")) + SIM->set_default_log_action (LOGLEV_PANIC, ACT_IGNORE); + else if (!strcmp (action, "ask")) + SIM->set_default_log_action (LOGLEV_PANIC, ACT_ASK); + else { + PARSE_ERR(("%s: panic directive malformed.", context)); + } + } + else if (!strcmp(params[0], "pass")) { + if (num_params != 2) { + PARSE_ERR(("%s: pass directive malformed.", context)); + } + if (strncmp(params[1], "action=", 7)) { + PARSE_ERR(("%s: pass directive malformed.", context)); + } + char *action = 7 + params[1]; + if (!strcmp(action, "fatal")) + SIM->set_default_log_action (LOGLEV_PASS, ACT_FATAL); + else if (!strcmp (action, "report")) + SIM->set_default_log_action (LOGLEV_PASS, ACT_REPORT); + else if (!strcmp (action, "ignore")) + SIM->set_default_log_action (LOGLEV_PASS, ACT_IGNORE); + else if (!strcmp (action, "ask")) + SIM->set_default_log_action (LOGLEV_PASS, ACT_ASK); + else { + PARSE_ERR(("%s: pass directive malformed.", context)); + } + } + else if (!strcmp(params[0], "error")) { + if (num_params != 2) { + PARSE_ERR(("%s: error directive malformed.", context)); + } + if (strncmp(params[1], "action=", 7)) { + PARSE_ERR(("%s: error directive malformed.", context)); + } + char *action = 7 + params[1]; + if (!strcmp(action, "fatal")) + SIM->set_default_log_action (LOGLEV_ERROR, ACT_FATAL); + else if (!strcmp (action, "report")) + SIM->set_default_log_action (LOGLEV_ERROR, ACT_REPORT); + else if (!strcmp (action, "ignore")) + SIM->set_default_log_action (LOGLEV_ERROR, ACT_IGNORE); + else if (!strcmp (action, "ask")) + SIM->set_default_log_action (LOGLEV_ERROR, ACT_ASK); + else { + PARSE_ERR(("%s: error directive malformed.", context)); + } + } + else if (!strcmp(params[0], "info")) { + if (num_params != 2) { + PARSE_ERR(("%s: info directive malformed.", context)); + } + if (strncmp(params[1], "action=", 7)) { + PARSE_ERR(("%s: info directive malformed.", context)); + } + char *action = 7 + params[1]; + if (!strcmp(action, "fatal")) + SIM->set_default_log_action (LOGLEV_INFO, ACT_FATAL); + else if (!strcmp (action, "report")) + SIM->set_default_log_action (LOGLEV_INFO, ACT_REPORT); + else if (!strcmp (action, "ignore")) + SIM->set_default_log_action (LOGLEV_INFO, ACT_IGNORE); + else if (!strcmp (action, "ask")) + SIM->set_default_log_action (LOGLEV_INFO, ACT_ASK); + else { + PARSE_ERR(("%s: info directive malformed.", context)); + } + } + else if (!strcmp(params[0], "debug")) { + if (num_params != 2) { + PARSE_ERR(("%s: debug directive malformed.", context)); + } + if (strncmp(params[1], "action=", 7)) { + PARSE_ERR(("%s: debug directive malformed.", context)); + } + char *action = 7 + params[1]; + if (!strcmp(action, "fatal")) + SIM->set_default_log_action (LOGLEV_DEBUG, ACT_FATAL); + else if (!strcmp (action, "report")) + SIM->set_default_log_action (LOGLEV_DEBUG, ACT_REPORT); + else if (!strcmp (action, "ignore")) + SIM->set_default_log_action (LOGLEV_DEBUG, ACT_IGNORE); + else if (!strcmp (action, "ask")) + SIM->set_default_log_action (LOGLEV_DEBUG, ACT_ASK); + else { + PARSE_ERR(("%s: debug directive malformed.", context)); + } + } + else if (!strcmp(params[0], "romimage")) { + if (num_params != 3) { + PARSE_ERR(("%s: romimage directive: wrong # args.", context)); + } + if (strncmp(params[1], "file=", 5)) { + PARSE_ERR(("%s: romimage directive malformed.", context)); + } + if (strncmp(params[2], "address=", 8)) { + PARSE_ERR(("%s: romimage directive malformed.", context)); + } + bx_options.rom.Opath->set (¶ms[1][5]); + if ( (params[2][8] == '0') && (params[2][9] == 'x') ) + bx_options.rom.Oaddress->set (strtoul (¶ms[2][8], NULL, 16)); + else + bx_options.rom.Oaddress->set (strtoul (¶ms[2][8], NULL, 10)); + } + else if (!strcmp(params[0], "optromimage1")) { + if (num_params != 3) { + PARSE_ERR(("%s: optromimage1 directive: wrong # args.", context)); + } + if (strncmp(params[1], "file=", 5)) { + PARSE_ERR(("%s: optromimage1 directive malformed.", context)); + } + if (strncmp(params[2], "address=", 8)) { + PARSE_ERR(("%s: optromimage2 directive malformed.", context)); + } + bx_options.optrom[0].Opath->set (¶ms[1][5]); + if ( (params[2][8] == '0') && (params[2][9] == 'x') ) + bx_options.optrom[0].Oaddress->set (strtoul (¶ms[2][8], NULL, 16)); + else + bx_options.optrom[0].Oaddress->set (strtoul (¶ms[2][8], NULL, 10)); + } + else if (!strcmp(params[0], "optromimage2")) { + if (num_params != 3) { + PARSE_ERR(("%s: optromimage2 directive: wrong # args.", context)); + } + if (strncmp(params[1], "file=", 5)) { + PARSE_ERR(("%s: optromimage2 directive malformed.", context)); + } + if (strncmp(params[2], "address=", 8)) { + PARSE_ERR(("%s: optromimage2 directive malformed.", context)); + } + bx_options.optrom[1].Opath->set (¶ms[1][5]); + if ( (params[2][8] == '0') && (params[2][9] == 'x') ) + bx_options.optrom[1].Oaddress->set (strtoul (¶ms[2][8], NULL, 16)); + else + bx_options.optrom[1].Oaddress->set (strtoul (¶ms[2][8], NULL, 10)); + } + else if (!strcmp(params[0], "optromimage3")) { + if (num_params != 3) { + PARSE_ERR(("%s: optromimage3 directive: wrong # args.", context)); + } + if (strncmp(params[1], "file=", 5)) { + PARSE_ERR(("%s: optromimage3 directive malformed.", context)); + } + if (strncmp(params[2], "address=", 8)) { + PARSE_ERR(("%s: optromimage2 directive malformed.", context)); + } + bx_options.optrom[2].Opath->set (¶ms[1][5]); + if ( (params[2][8] == '0') && (params[2][9] == 'x') ) + bx_options.optrom[2].Oaddress->set (strtoul (¶ms[2][8], NULL, 16)); + else + bx_options.optrom[2].Oaddress->set (strtoul (¶ms[2][8], NULL, 10)); + } + else if (!strcmp(params[0], "optromimage4")) { + if (num_params != 3) { + PARSE_ERR(("%s: optromimage4 directive: wrong # args.", context)); + } + if (strncmp(params[1], "file=", 5)) { + PARSE_ERR(("%s: optromimage4 directive malformed.", context)); + } + if (strncmp(params[2], "address=", 8)) { + PARSE_ERR(("%s: optromimage2 directive malformed.", context)); + } + bx_options.optrom[3].Opath->set (¶ms[1][5]); + if ( (params[2][8] == '0') && (params[2][9] == 'x') ) + bx_options.optrom[3].Oaddress->set (strtoul (¶ms[2][8], NULL, 16)); + else + bx_options.optrom[3].Oaddress->set (strtoul (¶ms[2][8], NULL, 10)); + } + else if (!strcmp(params[0], "vgaromimage")) { + if (num_params != 2) { + PARSE_ERR(("%s: vgaromimage directive: wrong # args.", context)); + } + bx_options.vgarom.Opath->set (params[1]); + } + else if (!strcmp(params[0], "vga_update_interval")) { + if (num_params != 2) { + PARSE_ERR(("%s: vga_update_interval directive: wrong # args.", context)); + } + bx_options.Ovga_update_interval->set (atol(params[1])); + if (bx_options.Ovga_update_interval->get () < 50000) { + BX_INFO(("%s: vga_update_interval seems awfully small!", context)); + } + } + else if (!strcmp(params[0], "keyboard_serial_delay")) { + if (num_params != 2) { + PARSE_ERR(("%s: keyboard_serial_delay directive: wrong # args.", context)); + } + bx_options.Okeyboard_serial_delay->set (atol(params[1])); + if (bx_options.Okeyboard_serial_delay->get () < 5) { + PARSE_ERR (("%s: keyboard_serial_delay not big enough!", context)); + } + } + else if (!strcmp(params[0], "keyboard_paste_delay")) { + if (num_params != 2) { + PARSE_ERR(("%s: keyboard_paste_delay directive: wrong # args.", context)); + } + bx_options.Okeyboard_paste_delay->set (atol(params[1])); + if (bx_options.Okeyboard_paste_delay->get () < 1000) { + PARSE_ERR (("%s: keyboard_paste_delay not big enough!", context)); + } + } + else if (!strcmp(params[0], "megs")) { + if (num_params != 2) { + PARSE_ERR(("%s: megs directive: wrong # args.", context)); + } + bx_options.memory.Osize->set (atol(params[1])); + } + else if (!strcmp(params[0], "floppy_command_delay")) { + if (num_params != 2) { + PARSE_ERR(("%s: floppy_command_delay directive: wrong # args.", context)); + } + bx_options.Ofloppy_command_delay->set (atol(params[1])); + if (bx_options.Ofloppy_command_delay->get () < 100) { + PARSE_ERR(("%s: floppy_command_delay not big enough!", context)); + } + } + else if (!strcmp(params[0], "ips")) { + if (num_params != 2) { + PARSE_ERR(("%s: ips directive: wrong # args.", context)); + } + bx_options.Oips->set (atol(params[1])); + if (bx_options.Oips->get () < BX_MIN_IPS) { + BX_ERROR(("%s: WARNING: ips is AWFULLY low!", context)); + } + } + else if (!strcmp(params[0], "pit")) { // Deprecated + if (num_params != 2) { + PARSE_ERR(("%s: pit directive: wrong # args.", context)); + } + BX_INFO(("WARNING: pit directive is deprecated, use clock: instead")); + if (!strncmp(params[1], "realtime=", 9)) { + switch (params[1][9]) { + case '0': + BX_INFO(("WARNING: not disabling realtime pit")); + break; + case '1': bx_options.clock.Osync->set (BX_CLOCK_SYNC_REALTIME); break; + default: PARSE_ERR(("%s: pit expected realtime=[0|1] arg", context)); + } + } + else PARSE_ERR(("%s: pit expected realtime=[0|1] arg", context)); + } + else if (!strcmp(params[0], "max_ips")) { + if (num_params != 2) { + PARSE_ERR(("%s: max_ips directive: wrong # args.", context)); + } + BX_INFO(("WARNING: max_ips not implemented")); + } + else if (!strcmp(params[0], "text_snapshot_check")) { + if (num_params != 2) { + PARSE_ERR(("%s: text_snapshot_check directive: wrong # args.", context)); + } + if (!strncmp(params[1], "enable", 6)) { + bx_options.Otext_snapshot_check->set (1); + } + else if (!strncmp(params[1], "disable", 7)) { + bx_options.Otext_snapshot_check->set (0); + } + else bx_options.Otext_snapshot_check->set (!!(atol(params[1]))); + } + else if (!strcmp(params[0], "mouse")) { + if (num_params != 2) { + PARSE_ERR(("%s: mouse directive malformed.", context)); + } + if (strncmp(params[1], "enabled=", 8)) { + PARSE_ERR(("%s: mouse directive malformed.", context)); + } + if (params[1][8] == '0' || params[1][8] == '1') + bx_options.Omouse_enabled->set (params[1][8] - '0'); + else + PARSE_ERR(("%s: mouse directive malformed.", context)); + } + else if (!strcmp(params[0], "private_colormap")) { + if (num_params != 2) { + PARSE_ERR(("%s: private_colormap directive malformed.", context)); + } + if (strncmp(params[1], "enabled=", 8)) { + PARSE_ERR(("%s: private_colormap directive malformed.", context)); + } + if (params[1][8] == '0' || params[1][8] == '1') + bx_options.Oprivate_colormap->set (params[1][8] - '0'); + else { + PARSE_ERR(("%s: private_colormap directive malformed.", context)); + } + } + else if (!strcmp(params[0], "fullscreen")) { +#if BX_WITH_AMIGAOS + if (num_params != 2) { + PARSE_ERR(("%s: fullscreen directive malformed.", context)); + } + if (strncmp(params[1], "enabled=", 8)) { + PARSE_ERR(("%s: fullscreen directive malformed.", context)); + } + if (params[1][8] == '0' || params[1][8] == '1') { + bx_options.Ofullscreen->set (params[1][8] - '0'); + } else { + PARSE_ERR(("%s: fullscreen directive malformed.", context)); + } +#endif + } + else if (!strcmp(params[0], "screenmode")) { +#if BX_WITH_AMIGAOS + if (num_params != 2) { + PARSE_ERR(("%s: screenmode directive malformed.", context)); + } + if (strncmp(params[1], "name=", 5)) { + PARSE_ERR(("%s: screenmode directive malformed.", context)); + } + bx_options.Oscreenmode->set (strdup(¶ms[1][5])); +#endif + } + + else if (!strcmp(params[0], "sb16")) { + for (i=1; i<num_params; i++) { + if (!strncmp(params[i], "midi=", 5)) { + bx_options.sb16.Omidifile->set (strdup(¶ms[i][5])); + } + else if (!strncmp(params[i], "midimode=", 9)) { + bx_options.sb16.Omidimode->set (atol(¶ms[i][9])); + } + else if (!strncmp(params[i], "wave=", 5)) { + bx_options.sb16.Owavefile->set (strdup(¶ms[i][5])); + } + else if (!strncmp(params[i], "wavemode=", 9)) { + bx_options.sb16.Owavemode->set (atol(¶ms[i][9])); + } + else if (!strncmp(params[i], "log=", 4)) { + bx_options.sb16.Ologfile->set (strdup(¶ms[i][4])); + } + else if (!strncmp(params[i], "loglevel=", 9)) { + bx_options.sb16.Ologlevel->set (atol(¶ms[i][9])); + } + else if (!strncmp(params[i], "dmatimer=", 9)) { + bx_options.sb16.Odmatimer->set (atol(¶ms[i][9])); + } + } + if (bx_options.sb16.Odmatimer->get () > 0) + bx_options.sb16.Opresent->set (1); + } + + else if (!strcmp(params[0], "parport1")) { + for (i=1; i<num_params; i++) { + if (!strncmp(params[i], "enabled=", 8)) { + bx_options.par[0].Oenabled->set (atol(¶ms[i][8])); + } + else if (!strncmp(params[i], "file=", 5)) { + bx_options.par[0].Ooutfile->set (strdup(¶ms[i][5])); + bx_options.par[0].Oenabled->set (1); + } + else { + BX_ERROR(("%s: unknown parameter for parport1 ignored.", context)); + } + } + } + +#if 0 + else if (!strcmp(params[0], "parport2")) { + for (i=1; i<num_params; i++) { + if (!strncmp(params[i], "enabled=", 8)) { + bx_options.par[1].Oenabled->set (atol(¶ms[i][8])); + } + else if (!strncmp(params[i], "file=", 5)) { + bx_options.par[1].Ooutfile->set (strdup(¶ms[i][5])); + bx_options.par[1].Oenabled->set (1); + } + else { + BX_ERROR(("%s: unknown parameter for parport2 ignored.", context)); + } + } + } +#endif + + else if (!strcmp(params[0], "i440fxsupport")) { + if (num_params != 2) { + PARSE_ERR(("%s: i440FXSupport directive malformed.", context)); + } + if (strncmp(params[1], "enabled=", 8)) { + PARSE_ERR(("%s: i440FXSupport directive malformed.", context)); + } + if (params[1][8] == '0') + bx_options.Oi440FXSupport->set (0); + else if (params[1][8] == '1') + bx_options.Oi440FXSupport->set (1); + else { + PARSE_ERR(("%s: i440FXSupport directive malformed.", context)); + } + } + else if (!strcmp(params[0], "newharddrivesupport")) { + if (num_params != 2) { + PARSE_ERR(("%s: newharddrivesupport directive malformed.", context)); + } + if (strncmp(params[1], "enabled=", 8)) { + PARSE_ERR(("%s: newharddrivesupport directive malformed.", context)); + } + if (params[1][8] == '0') + bx_options.OnewHardDriveSupport->set (0); + else if (params[1][8] == '1') + bx_options.OnewHardDriveSupport->set (1); + else { + PARSE_ERR(("%s: newharddrivesupport directive malformed.", context)); + } + } + else if (!strcmp(params[0], "cmosimage")) { + if (num_params != 2) { + PARSE_ERR(("%s: cmosimage directive: wrong # args.", context)); + } + bx_options.cmos.Opath->set (strdup(params[1])); + bx_options.cmos.OcmosImage->set (1); // CMOS Image is true + } + else if (!strcmp(params[0], "time0")) { // Deprectated + BX_INFO(("WARNING: time0 directive is deprecated, use clock: instead")); + if (num_params != 2) { + PARSE_ERR(("%s: time0 directive: wrong # args.", context)); + } + bx_options.clock.Otime0->set (atoi(params[1])); + } + else if (!strcmp(params[0], "clock")) { + for (i=1; i<num_params; i++) { + if (!strncmp(params[i], "sync=", 5)) { + bx_options.clock.Osync->set_by_name (¶ms[i][5]); + } + else if (!strcmp(params[i], "time0=local")) { + bx_options.clock.Otime0->set (BX_CLOCK_TIME0_LOCAL); + } + else if (!strcmp(params[i], "time0=utc")) { + bx_options.clock.Otime0->set (BX_CLOCK_TIME0_UTC); + } + else if (!strncmp(params[i], "time0=", 6)) { + bx_options.clock.Otime0->set (atoi(¶ms[i][6])); + } + else { + BX_ERROR(("%s: unknown parameter for clock ignored.", context)); + } + } + } +#ifdef MAGIC_BREAKPOINT + else if (!strcmp(params[0], "magic_break")) { + if (num_params != 2) { + PARSE_ERR(("%s: magic_break directive: wrong # args.", context)); + } + if (strncmp(params[1], "enabled=", 8)) { + PARSE_ERR(("%s: magic_break directive malformed.", context)); + } + if (params[1][8] == '0') { + BX_INFO(("Ignoring magic break points")); + bx_dbg.magic_break_enabled = 0; + } + else if (params[1][8] == '1') { + BX_INFO(("Stopping on magic break points")); + bx_dbg.magic_break_enabled = 1; + } + else { + PARSE_ERR(("%s: magic_break directive malformed.", context)); + } + } +#endif + else if (!strcmp(params[0], "ne2k")) { + int tmp[6]; + char tmpchar[6]; + int valid = 0; + int n; + if (!bx_options.ne2k.Opresent->get ()) { + bx_options.ne2k.Oethmod->set_by_name ("null"); + } + for (i=1; i<num_params; i++) { + if (!strncmp(params[i], "ioaddr=", 7)) { + bx_options.ne2k.Oioaddr->set (strtoul(¶ms[i][7], NULL, 16)); + valid |= 0x01; + } + else if (!strncmp(params[i], "irq=", 4)) { + bx_options.ne2k.Oirq->set (atol(¶ms[i][4])); + valid |= 0x02; + } + else if (!strncmp(params[i], "mac=", 4)) { + n = sscanf(¶ms[i][4], "%x:%x:%x:%x:%x:%x", + &tmp[0],&tmp[1],&tmp[2],&tmp[3],&tmp[4],&tmp[5]); + if (n != 6) { + PARSE_ERR(("%s: ne2k mac address malformed.", context)); + } + for (n=0;n<6;n++) + tmpchar[n] = (unsigned char)tmp[n]; + bx_options.ne2k.Omacaddr->set (tmpchar); + valid |= 0x04; + } + else if (!strncmp(params[i], "ethmod=", 7)) { + if (!bx_options.ne2k.Oethmod->set_by_name (strdup(¶ms[i][7]))) + PARSE_ERR(("%s: ethernet module '%s' not available", context, strdup(¶ms[i][7]))); + } + else if (!strncmp(params[i], "ethdev=", 7)) { + bx_options.ne2k.Oethdev->set (strdup(¶ms[i][7])); + } + else if (!strncmp(params[i], "script=", 7)) { + bx_options.ne2k.Oscript->set (strdup(¶ms[i][7])); + } + else { + PARSE_ERR(("%s: ne2k directive malformed.", context)); + } + } + if (!bx_options.ne2k.Opresent->get ()) { + if (valid == 0x07) { + bx_options.ne2k.Opresent->set (1); + } + else { + PARSE_ERR(("%s: ne2k directive incomplete (ioaddr, irq and mac are required)", context)); + } + } + } + + else if (!strcmp(params[0], "load32bitOSImage")) { + if ( (num_params!=4) && (num_params!=5) ) { + PARSE_ERR(("%s: load32bitOSImage directive: wrong # args.", context)); + } + if (strncmp(params[1], "os=", 3)) { + PARSE_ERR(("%s: load32bitOSImage: directive malformed.", context)); + } + if (!strcmp(¶ms[1][3], "nullkernel")) { + bx_options.load32bitOSImage.OwhichOS->set (Load32bitOSNullKernel); + } + else if (!strcmp(¶ms[1][3], "linux")) { + bx_options.load32bitOSImage.OwhichOS->set (Load32bitOSLinux); + } + else { + PARSE_ERR(("%s: load32bitOSImage: unsupported OS.", context)); + } + if (strncmp(params[2], "path=", 5)) { + PARSE_ERR(("%s: load32bitOSImage: directive malformed.", context)); + } + if (strncmp(params[3], "iolog=", 6)) { + PARSE_ERR(("%s: load32bitOSImage: directive malformed.", context)); + } + bx_options.load32bitOSImage.Opath->set (strdup(¶ms[2][5])); + bx_options.load32bitOSImage.Oiolog->set (strdup(¶ms[3][6])); + if (num_params == 5) { + if (strncmp(params[4], "initrd=", 7)) { + PARSE_ERR(("%s: load32bitOSImage: directive malformed.", context)); + } + bx_options.load32bitOSImage.Oinitrd->set (strdup(¶ms[4][7])); + } + } + else if (!strcmp(params[0], "keyboard_type")) { + if (num_params != 2) { + PARSE_ERR(("%s: keyboard_type directive: wrong # args.", context)); + } + if(strcmp(params[1],"xt")==0){ + bx_options.Okeyboard_type->set (BX_KBD_XT_TYPE); + } + else if(strcmp(params[1],"at")==0){ + bx_options.Okeyboard_type->set (BX_KBD_AT_TYPE); + } + else if(strcmp(params[1],"mf")==0){ + bx_options.Okeyboard_type->set (BX_KBD_MF_TYPE); + } + else{ + PARSE_ERR(("%s: keyboard_type directive: wrong arg %s.", context,params[1])); + } + } + + else if (!strcmp(params[0], "keyboard_mapping") + ||!strcmp(params[0], "keyboardmapping")) { + for (i=1; i<num_params; i++) { + if (!strncmp(params[i], "enabled=", 8)) { + bx_options.keyboard.OuseMapping->set (atol(¶ms[i][8])); + } + else if (!strncmp(params[i], "map=", 4)) { + bx_options.keyboard.Okeymap->set (strdup(¶ms[i][4])); + } + } + } + else if (!strcmp(params[0], "user_shortcut")) { + if (num_params != 2) { + PARSE_ERR(("%s: user_shortcut directive: wrong # args.", context)); + } + if(!strncmp(params[1], "keys=", 4)) { + bx_options.Ouser_shortcut->set (strdup(¶ms[1][5])); + } + } + else if (!strcmp(params[0], "config_interface")) { + if (num_params != 2) { + PARSE_ERR(("%s: config_interface directive: wrong # args.", context)); + } + if (!bx_options.Osel_config->set_by_name (params[1])) + PARSE_ERR(("%s: config_interface '%s' not available", context, params[1])); + } + else if (!strcmp(params[0], "display_library")) { + if (num_params != 2) { + PARSE_ERR(("%s: display_library directive: wrong # args.", context)); + } + if (!bx_options.Osel_displaylib->set_by_name (params[1])) + PARSE_ERR(("%s: display library '%s' not available", context, params[1])); + } + else { + PARSE_ERR(( "%s: directive '%s' not understood", context, params[0])); + } + return 0; +} + +static char *fdtypes[] = { + "none", "1_2", "1_44", "2_88", "720k", "360k", "160k", "180k", "320k" +}; + + +int +bx_write_floppy_options (FILE *fp, int drive, bx_floppy_options *opt) +{ + BX_ASSERT (drive==0 || drive==1); + if (opt->Otype->get () == BX_FLOPPY_NONE) { + fprintf (fp, "# no floppy%c\n", (char)'a'+drive); + return 0; + } + BX_ASSERT (opt->Otype->get () > BX_FLOPPY_NONE && opt->Otype->get () <= BX_FLOPPY_LAST); + fprintf (fp, "floppy%c: %s=\"%s\", status=%s\n", + (char)'a'+drive, + fdtypes[opt->Otype->get () - BX_FLOPPY_NONE], + opt->Opath->getptr (), + opt->Ostatus->get ()==BX_EJECTED ? "ejected" : "inserted"); + return 0; +} + +int +bx_write_ata_options (FILE *fp, Bit8u channel, bx_ata_options *opt) +{ + fprintf (fp, "ata%d: enabled=%d", channel, opt->Opresent->get()); + + if (opt->Opresent->get()) { + fprintf (fp, ", ioaddr1=0x%x, ioaddr2=0x%x, irq=%d", opt->Oioaddr1->get(), + opt->Oioaddr2->get(), opt->Oirq->get()); + } + + fprintf (fp, "\n"); + return 0; +} + +int +bx_write_atadevice_options (FILE *fp, Bit8u channel, Bit8u drive, bx_atadevice_options *opt) +{ + if (opt->Opresent->get()) { + fprintf (fp, "ata%d-%s: ", channel, drive==0?"master":"slave"); + + if (opt->Otype->get() == BX_ATA_DEVICE_DISK) { + fprintf (fp, "type=disk"); + + switch(opt->Omode->get()) { + case BX_ATA_MODE_FLAT: + fprintf (fp, ", mode=flat"); + break; + case BX_ATA_MODE_CONCAT: + fprintf (fp, ", mode=concat"); + break; + case BX_ATA_MODE_EXTDISKSIM: + fprintf (fp, ", mode=external"); + break; + case BX_ATA_MODE_DLL_HD: + fprintf (fp, ", mode=dll"); + break; + case BX_ATA_MODE_SPARSE: + fprintf (fp, ", mode=sparse"); + break; + case BX_ATA_MODE_VMWARE3: + fprintf (fp, ", mode=vmware3"); + break; +// case BX_ATA_MODE_SPLIT: +// fprintf (fp, ", mode=split"); +// break; + case BX_ATA_MODE_UNDOABLE: + fprintf (fp, ", mode=undoable"); + break; + case BX_ATA_MODE_GROWING: + fprintf (fp, ", mode=growing"); + break; + case BX_ATA_MODE_VOLATILE: + fprintf (fp, ", mode=volatile"); + break; +// case BX_ATA_MODE_Z_UNDOABLE: +// fprintf (fp, ", mode=z-undoable"); +// break; +// case BX_ATA_MODE_Z_VOLATILE: +// fprintf (fp, ", mode=z-volatile"); +// break; + } + + switch(opt->Otranslation->get()) { + case BX_ATA_TRANSLATION_NONE: + fprintf (fp, ", translation=none"); + break; + case BX_ATA_TRANSLATION_LBA: + fprintf (fp, ", translation=lba"); + break; + case BX_ATA_TRANSLATION_LARGE: + fprintf (fp, ", translation=large"); + break; + case BX_ATA_TRANSLATION_RECHS: + fprintf (fp, ", translation=rechs"); + break; + case BX_ATA_TRANSLATION_AUTO: + fprintf (fp, ", translation=auto"); + break; + } + + fprintf (fp, ", path=\"%s\", cylinders=%d, heads=%d, spt=%d", + opt->Opath->getptr(), + opt->Ocylinders->get(), opt->Oheads->get(), opt->Ospt->get()); + + if (opt->Ojournal->getptr() != NULL) + if ( strcmp(opt->Ojournal->getptr(), "") != 0) + fprintf (fp, ", journal=\"%s\"", opt->Ojournal->getptr()); + + } + else if (opt->Otype->get() == BX_ATA_DEVICE_CDROM) { + fprintf (fp, "type=cdrom, path=\"%s\", status=%s", + opt->Opath->getptr(), + opt->Ostatus->get ()==BX_EJECTED ? "ejected" : "inserted"); + } + + switch(opt->Obiosdetect->get()) { + case BX_ATA_BIOSDETECT_NONE: + fprintf (fp, ", biosdetect=none"); + break; + case BX_ATA_BIOSDETECT_CMOS: + fprintf (fp, ", biosdetect=cmos"); + break; + case BX_ATA_BIOSDETECT_AUTO: + fprintf (fp, ", biosdetect=auto"); + break; + } + if (strlen(opt->Omodel->getptr())>0) { + fprintf (fp, ", model=\"%s\"", opt->Omodel->getptr()); + } + + fprintf (fp, "\n"); + } + return 0; +} + +int +bx_write_parport_options (FILE *fp, bx_parport_options *opt, int n) +{ + fprintf (fp, "parport%d: enabled=%d", n, opt->Oenabled->get ()); + if (opt->Oenabled->get ()) { + fprintf (fp, ", file=\"%s\"", opt->Ooutfile->getptr ()); + } + fprintf (fp, "\n"); + return 0; +} + +int +bx_write_serial_options (FILE *fp, bx_serial_options *opt, int n) +{ + fprintf (fp, "com%d: enabled=%d", n, opt->Oenabled->get ()); + if (opt->Oenabled->get ()) { + fprintf (fp, ", dev=\"%s\"", opt->Odev->getptr ()); + } + fprintf (fp, "\n"); + return 0; +} + +int +bx_write_usb_options (FILE *fp, bx_usb_options *opt, int n) +{ + fprintf (fp, "usb%d: enabled=%d", n, opt->Oenabled->get ()); + if (opt->Oenabled->get ()) { + fprintf (fp, ", ioaddr=0x%04x, irq=%d", opt->Oioaddr->get (), + opt->Oirq->get ()); + } + fprintf (fp, "\n"); + return 0; +} + +int +bx_write_sb16_options (FILE *fp, bx_sb16_options *opt) +{ + if (!opt->Opresent->get ()) { + fprintf (fp, "# no sb16\n"); + return 0; + } + fprintf (fp, "sb16: midimode=%d, midi=%s, wavemode=%d, wave=%s, loglevel=%d, log=%s, dmatimer=%d\n", opt->Omidimode->get (), opt->Omidifile->getptr (), opt->Owavemode->get (), opt->Owavefile->getptr (), opt->Ologlevel->get (), opt->Ologfile->getptr (), opt->Odmatimer->get ()); + return 0; +} + +int +bx_write_ne2k_options (FILE *fp, bx_ne2k_options *opt) +{ + if (!opt->Opresent->get ()) { + fprintf (fp, "# no ne2k\n"); + return 0; + } + char *ptr = opt->Omacaddr->getptr (); + fprintf (fp, "ne2k: ioaddr=0x%x, irq=%d, mac=%02x:%02x:%02x:%02x:%02x:%02x, ethmod=%s, ethdev=%s, script=%s\n", + opt->Oioaddr->get (), + opt->Oirq->get (), + (unsigned int)(0xff & ptr[0]), + (unsigned int)(0xff & ptr[1]), + (unsigned int)(0xff & ptr[2]), + (unsigned int)(0xff & ptr[3]), + (unsigned int)(0xff & ptr[4]), + (unsigned int)(0xff & ptr[5]), + opt->Oethmod->get_choice(opt->Oethmod->get()), + opt->Oethdev->getptr (), + opt->Oscript->getptr ()); + return 0; +} + +int +bx_write_loader_options (FILE *fp, bx_load32bitOSImage_t *opt) +{ + if (opt->OwhichOS->get () == 0) { + fprintf (fp, "# no loader\n"); + return 0; + } + BX_ASSERT(opt->OwhichOS->get () == Load32bitOSLinux || opt->OwhichOS->get () == Load32bitOSNullKernel); + fprintf (fp, "load32bitOSImage: os=%s, path=%s, iolog=%s, initrd=%s\n", + (opt->OwhichOS->get () == Load32bitOSLinux) ? "linux" : "nullkernel", + opt->Opath->getptr (), + opt->Oiolog->getptr (), + opt->Oinitrd->getptr ()); + return 0; +} + +int +bx_write_clock_options (FILE *fp, bx_clock_options *opt) +{ + fprintf (fp, "clock: "); + + switch (opt->Osync->get()) { + case BX_CLOCK_SYNC_NONE: + fprintf (fp, "sync=none"); + break; + case BX_CLOCK_SYNC_REALTIME: + fprintf (fp, "sync=realtime"); + break; + case BX_CLOCK_SYNC_SLOWDOWN: + fprintf (fp, "sync=slowdown"); + break; + case BX_CLOCK_SYNC_BOTH: + fprintf (fp, "sync=both"); + break; + default: + BX_PANIC(("Unknown value for sync method")); + } + + switch (opt->Otime0->get()) { + case 0: break; + case BX_CLOCK_TIME0_LOCAL: + fprintf (fp, ", time0=local"); + break; + case BX_CLOCK_TIME0_UTC: + fprintf (fp, ", time0=utc"); + break; + default: + fprintf (fp, ", time0=%u", opt->Otime0->get()); + } + + fprintf (fp, "\n"); + return 0; +} + +int +bx_write_log_options (FILE *fp, bx_log_options *opt) +{ + fprintf (fp, "log: %s\n", opt->Ofilename->getptr ()); + fprintf (fp, "logprefix: %s\n", opt->Oprefix->getptr ()); + fprintf (fp, "debugger_log: %s\n", opt->Odebugger_filename->getptr ()); + fprintf (fp, "panic: action=%s\n", + io->getaction(logfunctions::get_default_action (LOGLEV_PANIC))); + fprintf (fp, "error: action=%s\n", + io->getaction(logfunctions::get_default_action (LOGLEV_ERROR))); + fprintf (fp, "info: action=%s\n", + io->getaction(logfunctions::get_default_action (LOGLEV_INFO))); + fprintf (fp, "debug: action=%s\n", + io->getaction(logfunctions::get_default_action (LOGLEV_DEBUG))); + fprintf (fp, "pass: action=%s\n", + io->getaction(logfunctions::get_default_action (LOGLEV_PASS))); + return 0; +} + +int +bx_write_keyboard_options (FILE *fp, bx_keyboard_options *opt) +{ + fprintf (fp, "keyboard_mapping: enabled=%d, map=%s\n", opt->OuseMapping->get(), opt->Okeymap->getptr()); + return 0; +} + +// return values: +// 0: written ok +// -1: failed +// -2: already exists, and overwrite was off +int +bx_write_configuration (char *rc, int overwrite) +{ + BX_INFO (("write configuration to %s\n", rc)); + // check if it exists. If so, only proceed if overwrite is set. + FILE *fp = fopen (rc, "r"); + if (fp != NULL) { + fclose (fp); + if (!overwrite) return -2; + } + fp = fopen (rc, "w"); + if (fp == NULL) return -1; + // finally it's open and we can start writing. + fprintf (fp, "# configuration file generated by Bochs\n"); + fprintf (fp, "config_interface: %s\n", bx_options.Osel_config->get_choice(bx_options.Osel_config->get())); + fprintf (fp, "display_library: %s\n", bx_options.Osel_displaylib->get_choice(bx_options.Osel_displaylib->get())); + fprintf (fp, "megs: %d\n", bx_options.memory.Osize->get ()); + if (strlen (bx_options.rom.Opath->getptr ()) > 0) + fprintf (fp, "romimage: file=%s, address=0x%05x\n", bx_options.rom.Opath->getptr(), (unsigned int)bx_options.rom.Oaddress->get ()); + else + fprintf (fp, "# no romimage\n"); + if (strlen (bx_options.vgarom.Opath->getptr ()) > 0) + fprintf (fp, "vgaromimage: %s\n", bx_options.vgarom.Opath->getptr ()); + else + fprintf (fp, "# no vgaromimage\n"); + int bootdrive = bx_options.Obootdrive->get (); + fprintf (fp, "boot: %s\n", (bootdrive==BX_BOOT_FLOPPYA) ? "floppy" : (bootdrive==BX_BOOT_DISKC) ? "disk" : "cdrom"); + // it would be nice to put this type of function as methods on + // the structs like bx_floppy_options::print or something. + bx_write_floppy_options (fp, 0, &bx_options.floppya); + bx_write_floppy_options (fp, 1, &bx_options.floppyb); + for (Bit8u channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) { + bx_write_ata_options (fp, channel, &bx_options.ata[channel]); + bx_write_atadevice_options (fp, channel, 0, &bx_options.atadevice[channel][0]); + bx_write_atadevice_options (fp, channel, 1, &bx_options.atadevice[channel][1]); + } + if (strlen (bx_options.optrom[0].Opath->getptr ()) > 0) + fprintf (fp, "optromimage1: file=%s, address=0x%05x\n", bx_options.optrom[0].Opath->getptr(), (unsigned int)bx_options.optrom[0].Oaddress->get ()); + if (strlen (bx_options.optrom[1].Opath->getptr ()) > 0) + fprintf (fp, "optromimage2: file=%s, address=0x%05x\n", bx_options.optrom[1].Opath->getptr(), (unsigned int)bx_options.optrom[1].Oaddress->get ()); + if (strlen (bx_options.optrom[2].Opath->getptr ()) > 0) + fprintf (fp, "optromimage3: file=%s, address=0x%05x\n", bx_options.optrom[2].Opath->getptr(), (unsigned int)bx_options.optrom[2].Oaddress->get ()); + if (strlen (bx_options.optrom[3].Opath->getptr ()) > 0) + fprintf (fp, "optromimage4: file=%s, address=0x%05x\n", bx_options.optrom[3].Opath->getptr(), (unsigned int)bx_options.optrom[3].Oaddress->get ()); + bx_write_parport_options (fp, &bx_options.par[0], 1); + //bx_write_parport_options (fp, &bx_options.par[1], 2); + bx_write_serial_options (fp, &bx_options.com[0], 1); + //bx_write_serial_options (fp, &bx_options.com[1], 2); + //bx_write_serial_options (fp, &bx_options.com[2], 3); + //bx_write_serial_options (fp, &bx_options.com[3], 4); + bx_write_usb_options (fp, &bx_options.usb[0], 1); + bx_write_sb16_options (fp, &bx_options.sb16); + fprintf (fp, "floppy_bootsig_check: disabled=%d\n", bx_options.OfloppySigCheck->get ()); + fprintf (fp, "vga_update_interval: %u\n", bx_options.Ovga_update_interval->get ()); + fprintf (fp, "keyboard_serial_delay: %u\n", bx_options.Okeyboard_serial_delay->get ()); + fprintf (fp, "keyboard_paste_delay: %u\n", bx_options.Okeyboard_paste_delay->get ()); + fprintf (fp, "floppy_command_delay: %u\n", bx_options.Ofloppy_command_delay->get ()); + fprintf (fp, "ips: %u\n", bx_options.Oips->get ()); + fprintf (fp, "text_snapshot_check: %d\n", bx_options.Otext_snapshot_check->get ()); + fprintf (fp, "mouse: enabled=%d\n", bx_options.Omouse_enabled->get ()); + fprintf (fp, "private_colormap: enabled=%d\n", bx_options.Oprivate_colormap->get ()); +#if BX_WITH_AMIGAOS + fprintf (fp, "fullscreen: enabled=%d\n", bx_options.Ofullscreen->get ()); + fprintf (fp, "screenmode: name=\"%s\"\n", bx_options.Oscreenmode->getptr ()); +#endif + fprintf (fp, "i440fxsupport: enabled=%d\n", bx_options.Oi440FXSupport->get ()); + bx_write_clock_options (fp, &bx_options.clock); + bx_write_ne2k_options (fp, &bx_options.ne2k); + fprintf (fp, "newharddrivesupport: enabled=%d\n", bx_options.OnewHardDriveSupport->get ()); + bx_write_loader_options (fp, &bx_options.load32bitOSImage); + bx_write_log_options (fp, &bx_options.log); + bx_write_keyboard_options (fp, &bx_options.keyboard); + fprintf (fp, "keyboard_type: %s\n", bx_options.Okeyboard_type->get ()==BX_KBD_XT_TYPE?"xt": + bx_options.Okeyboard_type->get ()==BX_KBD_AT_TYPE?"at":"mf"); + fprintf (fp, "user_shortcut: keys=%s\n", bx_options.Ouser_shortcut->getptr ()); + if (strlen (bx_options.cmos.Opath->getptr ()) > 0) + fprintf (fp, "cmosimage: %s\n", bx_options.cmos.Opath->getptr()); + else + fprintf (fp, "# no cmosimage\n"); + fclose (fp); + return 0; +} +#endif // #if BX_PROVIDE_MAIN + + void +bx_signal_handler( int signum) +{ + // in a multithreaded environment, a signal such as SIGINT can be sent to all + // threads. This function is only intended to handle signals in the + // simulator thread. It will simply return if called from any other thread. + // Otherwise the BX_PANIC() below can be called in multiple threads at + // once, leading to multiple threads trying to display a dialog box, + // leading to GUI deadlock. + if (!SIM->is_sim_thread ()) { + BX_INFO (("bx_signal_handler: ignored sig %d because it wasn't called from the simulator thread", signum)); + return; + } +#if BX_GUI_SIGHANDLER + if (bx_gui_sighandler) { + // GUI signal handler gets first priority, if the mask says it's wanted + if ((1<<signum) & bx_gui->get_sighandler_mask ()) { + bx_gui->sighandler (signum); + return; + } + } +#endif + +#if BX_SHOW_IPS + extern unsigned long ips_count; + + if (signum == SIGALRM ) { + BX_INFO(("ips = %lu", ips_count)); + ips_count = 0; +#ifndef __MINGW32__ + signal(SIGALRM, bx_signal_handler); + alarm( 1 ); +#endif + return; + } +#endif + +#if BX_GUI_SIGHANDLER + if (bx_gui_sighandler) { + if ((1<<signum) & bx_gui->get_sighandler_mask ()) { + bx_gui->sighandler (signum); + return; + } + } +#endif + BX_PANIC(("SIGNAL %u caught", signum)); +} + diff --git a/tools/ioemu/iodev/ne2k.cc b/tools/ioemu/iodev/ne2k.cc new file mode 100644 index 0000000000..d6e0ac4966 --- /dev/null +++ b/tools/ioemu/iodev/ne2k.cc @@ -0,0 +1,1608 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: ne2k.cc,v 1.56.2.1 2004/02/02 22:37:22 cbothamy Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + +// Peter Grehan (grehan@iprg.nokia.com) coded all of this +// NE2000/ether stuff. + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#if BX_NE2K_SUPPORT + +//Never completely fill the ne2k ring so that we never +// hit the unclear completely full buffer condition. +#define BX_NE2K_NEVER_FULL_RING (1) + +#define LOG_THIS theNE2kDevice-> + +bx_ne2k_c *theNE2kDevice = NULL; + + int +libne2k_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + theNE2kDevice = new bx_ne2k_c (); + bx_devices.pluginNE2kDevice = theNE2kDevice; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theNE2kDevice, BX_PLUGIN_NE2K); + return(0); // Success +} + + void +libne2k_LTX_plugin_fini(void) +{ +} + +bx_ne2k_c::bx_ne2k_c(void) +{ + put("NE2K"); + settype(NE2KLOG); + s.tx_timer_index = BX_NULL_TIMER_HANDLE; +} + + +bx_ne2k_c::~bx_ne2k_c(void) +{ + // nothing for now +} + +// +// reset - restore state to power-up, cancelling all i/o +// +void +bx_ne2k_c::reset(unsigned type) +{ + BX_DEBUG (("reset")); + // Zero out registers and memory + memset( & BX_NE2K_THIS s.CR, 0, sizeof(BX_NE2K_THIS s.CR) ); + memset( & BX_NE2K_THIS s.ISR, 0, sizeof(BX_NE2K_THIS s.ISR)); + memset( & BX_NE2K_THIS s.IMR, 0, sizeof(BX_NE2K_THIS s.IMR)); + memset( & BX_NE2K_THIS s.DCR, 0, sizeof(BX_NE2K_THIS s.DCR)); + memset( & BX_NE2K_THIS s.TCR, 0, sizeof(BX_NE2K_THIS s.TCR)); + memset( & BX_NE2K_THIS s.TSR, 0, sizeof(BX_NE2K_THIS s.TSR)); + memset( & BX_NE2K_THIS s.RCR, 0, sizeof(BX_NE2K_THIS s.RCR)); + memset( & BX_NE2K_THIS s.RSR, 0, sizeof(BX_NE2K_THIS s.RSR)); + BX_NE2K_THIS s.local_dma = 0; + BX_NE2K_THIS s.page_start = 0; + BX_NE2K_THIS s.page_stop = 0; + BX_NE2K_THIS s.bound_ptr = 0; + BX_NE2K_THIS s.tx_page_start = 0; + BX_NE2K_THIS s.num_coll = 0; + BX_NE2K_THIS s.tx_bytes = 0; + BX_NE2K_THIS s.fifo = 0; + BX_NE2K_THIS s.remote_dma = 0; + BX_NE2K_THIS s.remote_start = 0; + BX_NE2K_THIS s.remote_bytes = 0; + BX_NE2K_THIS s.tallycnt_0 = 0; + BX_NE2K_THIS s.tallycnt_1 = 0; + BX_NE2K_THIS s.tallycnt_2 = 0; + + memset( & BX_NE2K_THIS s.physaddr, 0, sizeof(BX_NE2K_THIS s.physaddr)); + memset( & BX_NE2K_THIS s.mchash, 0, sizeof(BX_NE2K_THIS s.mchash)); + BX_NE2K_THIS s.curr_page = 0; + + BX_NE2K_THIS s.rempkt_ptr = 0; + BX_NE2K_THIS s.localpkt_ptr = 0; + BX_NE2K_THIS s.address_cnt = 0; + + memset( & BX_NE2K_THIS s.mem, 0, sizeof(BX_NE2K_THIS s.mem)); + + // Set power-up conditions + BX_NE2K_THIS s.CR.stop = 1; + BX_NE2K_THIS s.CR.rdma_cmd = 4; + BX_NE2K_THIS s.ISR.reset = 1; + BX_NE2K_THIS s.DCR.longaddr = 1; + DEV_pic_lower_irq(BX_NE2K_THIS s.base_irq); +} + +// +// read_cr/write_cr - utility routines for handling reads/writes to +// the Command Register +// +Bit32u +bx_ne2k_c::read_cr(void) +{ + Bit32u val = + (((BX_NE2K_THIS s.CR.pgsel & 0x03) << 6) | + ((BX_NE2K_THIS s.CR.rdma_cmd & 0x07) << 3) | + (BX_NE2K_THIS s.CR.tx_packet << 2) | + (BX_NE2K_THIS s.CR.start << 1) | + (BX_NE2K_THIS s.CR.stop)); + BX_DEBUG(("read CR returns 0x%08x", val)); + return val; +} + +void +bx_ne2k_c::write_cr(Bit32u value) +{ + BX_DEBUG(("wrote 0x%02x to CR", value)); + + // Validate remote-DMA + if ((value & 0x38) == 0x00) { + BX_DEBUG(("CR write - invalid rDMA value 0")); + value |= 0x20; /* dma_cmd == 4 is a safe default */ + } + + // Check for s/w reset + if (value & 0x01) { + BX_NE2K_THIS s.ISR.reset = 1; + BX_NE2K_THIS s.CR.stop = 1; + } else { + BX_NE2K_THIS s.CR.stop = 0; + } + + BX_NE2K_THIS s.CR.rdma_cmd = (value & 0x38) >> 3; + + // If start command issued, the RST bit in the ISR + // must be cleared + if ((value & 0x02) && !BX_NE2K_THIS s.CR.start) { + BX_NE2K_THIS s.ISR.reset = 0; + } + + BX_NE2K_THIS s.CR.start = ((value & 0x02) == 0x02); + BX_NE2K_THIS s.CR.pgsel = (value & 0xc0) >> 6; + + // Check for send-packet command + if (BX_NE2K_THIS s.CR.rdma_cmd == 3) { + // Set up DMA read from receive ring + BX_NE2K_THIS s.remote_start = BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.bound_ptr * 256; + BX_NE2K_THIS s.remote_bytes = *((Bit16u*) & BX_NE2K_THIS s.mem[BX_NE2K_THIS s.bound_ptr * 256 + 2 - BX_NE2K_MEMSTART]); + BX_INFO(("Sending buffer #x%x length %d", + BX_NE2K_THIS s.remote_start, + BX_NE2K_THIS s.remote_bytes)); + } + + // Check for start-tx + if ((value & 0x04) && BX_NE2K_THIS s.TCR.loop_cntl) { + if (BX_NE2K_THIS s.TCR.loop_cntl != 1) { + BX_INFO(("Loop mode %d not supported.", BX_NE2K_THIS s.TCR.loop_cntl)); + } else { + rx_frame (& BX_NE2K_THIS s.mem[BX_NE2K_THIS s.tx_page_start*256 - BX_NE2K_MEMSTART], + BX_NE2K_THIS s.tx_bytes); + } + } else if (value & 0x04) { + if (BX_NE2K_THIS s.CR.stop || !BX_NE2K_THIS s.CR.start) + BX_PANIC(("CR write - tx start, dev in reset")); + + if (BX_NE2K_THIS s.tx_bytes == 0) + BX_PANIC(("CR write - tx start, tx bytes == 0")); + +#ifdef notdef + // XXX debug stuff + printf("packet tx (%d bytes):\t", BX_NE2K_THIS s.tx_bytes); + for (int i = 0; i < BX_NE2K_THIS s.tx_bytes; i++) { + printf("%02x ", BX_NE2K_THIS s.mem[BX_NE2K_THIS s.tx_page_start*256 - + BX_NE2K_MEMSTART + i]); + if (i && (((i+1) % 16) == 0)) + printf("\t"); + } + printf(""); +#endif + + // Send the packet to the system driver + BX_NE2K_THIS ethdev->sendpkt(& BX_NE2K_THIS s.mem[BX_NE2K_THIS s.tx_page_start*256 - BX_NE2K_MEMSTART], BX_NE2K_THIS s.tx_bytes); + + // some more debug + if (BX_NE2K_THIS s.tx_timer_active) + BX_PANIC(("CR write, tx timer still active")); + + // Schedule a timer to trigger a tx-complete interrupt + // The number of microseconds is the bit-time / 10. + // The bit-time is the preamble+sfd (64 bits), the + // inter-frame gap (96 bits), the CRC (4 bytes), and the + // the number of bits in the frame (s.tx_bytes * 8). + // + bx_pc_system.activate_timer(BX_NE2K_THIS s.tx_timer_index, + (64 + 96 + 4*8 + BX_NE2K_THIS s.tx_bytes*8)/10, + 0); // not continuous + } + + // Linux probes for an interrupt by setting up a remote-DMA read + // of 0 bytes with remote-DMA completion interrupts enabled. + // Detect this here + if (BX_NE2K_THIS s.CR.rdma_cmd == 0x01 && + BX_NE2K_THIS s.CR.start && + BX_NE2K_THIS s.remote_bytes == 0) { + BX_NE2K_THIS s.ISR.rdma_done = 1; + if (BX_NE2K_THIS s.IMR.rdma_inte) { + DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq); + } + } +} + +// +// chipmem_read/chipmem_write - access the 64K private RAM. +// The ne2000 memory is accessed through the data port of +// the asic (offset 0) after setting up a remote-DMA transfer. +// Both byte and word accesses are allowed. +// The first 16 bytes contains the MAC address at even locations, +// and there is 16K of buffer memory starting at 16K +// +Bit32u BX_CPP_AttrRegparmN(2) +bx_ne2k_c::chipmem_read(Bit32u address, unsigned int io_len) +{ + Bit32u retval = 0; + + if ((io_len == 2) && (address & 0x1)) + BX_PANIC(("unaligned chipmem word read")); + + // ROM'd MAC address + if ((address >=0) && (address <= 31)) { + retval = BX_NE2K_THIS s.macaddr[address]; + if (io_len == 2) { + retval |= (BX_NE2K_THIS s.macaddr[address + 1] << 8); + } + return (retval); + } + + if ((address >= BX_NE2K_MEMSTART) && (address < BX_NE2K_MEMEND)) { + retval = BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART]; + if (io_len == 2) { + retval |= (BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART + 1] << 8); + } + return (retval); + } + + BX_DEBUG(("out-of-bounds chipmem read, %04X", address)); + + return (0xff); +} + +void BX_CPP_AttrRegparmN(3) +bx_ne2k_c::chipmem_write(Bit32u address, Bit32u value, unsigned io_len) +{ + if ((io_len == 2) && (address & 0x1)) + BX_PANIC(("unaligned chipmem word write")); + + if ((address >= BX_NE2K_MEMSTART) && (address < BX_NE2K_MEMEND)) { + BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART] = value & 0xff; + if (io_len == 2) + BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART + 1] = value >> 8; + } else + BX_DEBUG(("out-of-bounds chipmem write, %04X", address)); +} + +// +// asic_read/asic_write - This is the high 16 bytes of i/o space +// (the lower 16 bytes is for the DS8390). Only two locations +// are used: offset 0, which is used for data transfer, and +// offset 0xf, which is used to reset the device. +// The data transfer port is used to as 'external' DMA to the +// DS8390. The chip has to have the DMA registers set up, and +// after that, insw/outsw instructions can be used to move +// the appropriate number of bytes to/from the device. +// +Bit32u BX_CPP_AttrRegparmN(2) +bx_ne2k_c::asic_read(Bit32u offset, unsigned int io_len) +{ + Bit32u retval = 0; + + switch (offset) { + case 0x0: // Data register + // + // A read remote-DMA command must have been issued, + // and the source-address and length registers must + // have been initialised. + // + if (io_len > BX_NE2K_THIS s.remote_bytes) + { + BX_ERROR(("ne2K: dma read underrun iolen=%d remote_bytes=%d",io_len,BX_NE2K_THIS s.remote_bytes)); + //return 0; + } + + //BX_INFO(("ne2k read DMA: addr=%4x remote_bytes=%d",BX_NE2K_THIS s.remote_dma,BX_NE2K_THIS s.remote_bytes)); + retval = chipmem_read(BX_NE2K_THIS s.remote_dma, io_len); + // + // The 8390 bumps the address and decreases the byte count + // by the selected word size after every access, not by + // the amount of data requested by the host (io_len). + // + BX_NE2K_THIS s.remote_dma += (BX_NE2K_THIS s.DCR.wdsize + 1); + if (BX_NE2K_THIS s.remote_dma == BX_NE2K_THIS s.page_stop << 8) { + BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.page_start << 8; + } + // keep s.remote_bytes from underflowing + if (BX_NE2K_THIS s.remote_bytes > 1) + BX_NE2K_THIS s.remote_bytes -= (BX_NE2K_THIS s.DCR.wdsize + 1); + else + BX_NE2K_THIS s.remote_bytes = 0; + + // If all bytes have been written, signal remote-DMA complete + if (BX_NE2K_THIS s.remote_bytes == 0) { + BX_NE2K_THIS s.ISR.rdma_done = 1; + if (BX_NE2K_THIS s.IMR.rdma_inte) { + DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq); + } + } + break; + + case 0xf: // Reset register + theNE2kDevice->reset(BX_RESET_SOFTWARE); + break; + + default: + BX_INFO(("asic read invalid address %04x", (unsigned) offset)); + break; + } + + return (retval); +} + +void +bx_ne2k_c::asic_write(Bit32u offset, Bit32u value, unsigned io_len) +{ + BX_DEBUG(("asic write addr=0x%02x, value=0x%04x", (unsigned) offset, (unsigned) value)); + switch (offset) { + case 0x0: // Data register - see asic_read for a description + + if ((io_len == 2) && (BX_NE2K_THIS s.DCR.wdsize == 0)) { + BX_PANIC(("dma write length 2 on byte mode operation")); + break; + } + + if (BX_NE2K_THIS s.remote_bytes == 0) + BX_PANIC(("ne2K: dma write, byte count 0")); + + chipmem_write(BX_NE2K_THIS s.remote_dma, value, io_len); + // is this right ??? asic_read uses DCR.wordsize + BX_NE2K_THIS s.remote_dma += io_len; + if (BX_NE2K_THIS s.remote_dma == BX_NE2K_THIS s.page_stop << 8) { + BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.page_start << 8; + } + + BX_NE2K_THIS s.remote_bytes -= io_len; + if (BX_NE2K_THIS s.remote_bytes > BX_NE2K_MEMSIZ) + BX_NE2K_THIS s.remote_bytes = 0; + + // If all bytes have been written, signal remote-DMA complete + if (BX_NE2K_THIS s.remote_bytes == 0) { + BX_NE2K_THIS s.ISR.rdma_done = 1; + if (BX_NE2K_THIS s.IMR.rdma_inte) { + DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq); + } + } + break; + + case 0xf: // Reset register + theNE2kDevice->reset(BX_RESET_SOFTWARE); + break; + + default: // this is invalid, but happens under win95 device detection + BX_INFO(("asic write invalid address %04x, ignoring", (unsigned) offset)); + break ; + } +} + +// +// page0_read/page0_write - These routines handle reads/writes to +// the 'zeroth' page of the DS8390 register file +// +Bit32u +bx_ne2k_c::page0_read(Bit32u offset, unsigned int io_len) +{ + BX_DEBUG(("page 0 read from port %04x, len=%u", (unsigned) offset, + (unsigned) io_len)); + if (io_len > 1) { + BX_ERROR(("bad length! page 0 read from port %04x, len=%u", (unsigned) offset, + (unsigned) io_len)); /* encountered with win98 hardware probe */ + return 0; + } + + + switch (offset) { + case 0x0: // CR + return (read_cr()); + break; + + case 0x1: // CLDA0 + return (BX_NE2K_THIS s.local_dma & 0xff); + break; + + case 0x2: // CLDA1 + return (BX_NE2K_THIS s.local_dma >> 8); + break; + + case 0x3: // BNRY + return (BX_NE2K_THIS s.bound_ptr); + break; + + case 0x4: // TSR + return ((BX_NE2K_THIS s.TSR.ow_coll << 7) | + (BX_NE2K_THIS s.TSR.cd_hbeat << 6) | + (BX_NE2K_THIS s.TSR.fifo_ur << 5) | + (BX_NE2K_THIS s.TSR.no_carrier << 4) | + (BX_NE2K_THIS s.TSR.aborted << 3) | + (BX_NE2K_THIS s.TSR.collided << 2) | + (BX_NE2K_THIS s.TSR.tx_ok)); + break; + + case 0x5: // NCR + return (BX_NE2K_THIS s.num_coll); + break; + + case 0x6: // FIFO + // reading FIFO is only valid in loopback mode + BX_ERROR(("reading FIFO not supported yet")); + return (BX_NE2K_THIS s.fifo); + break; + + case 0x7: // ISR + return ((BX_NE2K_THIS s.ISR.reset << 7) | + (BX_NE2K_THIS s.ISR.rdma_done << 6) | + (BX_NE2K_THIS s.ISR.cnt_oflow << 5) | + (BX_NE2K_THIS s.ISR.overwrite << 4) | + (BX_NE2K_THIS s.ISR.tx_err << 3) | + (BX_NE2K_THIS s.ISR.rx_err << 2) | + (BX_NE2K_THIS s.ISR.pkt_tx << 1) | + (BX_NE2K_THIS s.ISR.pkt_rx)); + break; + + case 0x8: // CRDA0 + return (BX_NE2K_THIS s.remote_dma & 0xff); + break; + + case 0x9: // CRDA1 + return (BX_NE2K_THIS s.remote_dma >> 8); + break; + + case 0xa: // reserved + BX_INFO(("reserved read - page 0, 0xa")); + return (0xff); + break; + + case 0xb: // reserved + BX_INFO(("reserved read - page 0, 0xb")); + return (0xff); + break; + + case 0xc: // RSR + return ((BX_NE2K_THIS s.RSR.deferred << 7) | + (BX_NE2K_THIS s.RSR.rx_disabled << 6) | + (BX_NE2K_THIS s.RSR.rx_mbit << 5) | + (BX_NE2K_THIS s.RSR.rx_missed << 4) | + (BX_NE2K_THIS s.RSR.fifo_or << 3) | + (BX_NE2K_THIS s.RSR.bad_falign << 2) | + (BX_NE2K_THIS s.RSR.bad_crc << 1) | + (BX_NE2K_THIS s.RSR.rx_ok)); + break; + + case 0xd: // CNTR0 + return (BX_NE2K_THIS s.tallycnt_0); + break; + + case 0xe: // CNTR1 + return (BX_NE2K_THIS s.tallycnt_1); + break; + + case 0xf: // CNTR2 + return (BX_NE2K_THIS s.tallycnt_2); + break; + + default: + BX_PANIC(("page 0 offset %04x out of range", (unsigned) offset)); + } + + return(0); +} + +void +bx_ne2k_c::page0_write(Bit32u offset, Bit32u value, unsigned io_len) +{ + BX_DEBUG(("page 0 write to port %04x, len=%u", (unsigned) offset, + (unsigned) io_len)); + + // It appears to be a common practice to use outw on page0 regs... + + // break up outw into two outb's + if (io_len == 2) { + page0_write(offset, (value & 0xff), 1); + page0_write(offset + 1, ((value >> 8) & 0xff), 1); + return; + } + + switch (offset) { + case 0x0: // CR + write_cr(value); + break; + + case 0x1: // PSTART + BX_NE2K_THIS s.page_start = value; + break; + + case 0x2: // PSTOP + // BX_INFO(("Writing to PSTOP: %02x", value)); + BX_NE2K_THIS s.page_stop = value; + break; + + case 0x3: // BNRY + BX_NE2K_THIS s.bound_ptr = value; + break; + + case 0x4: // TPSR + BX_NE2K_THIS s.tx_page_start = value; + break; + + case 0x5: // TBCR0 + // Clear out low byte and re-insert + BX_NE2K_THIS s.tx_bytes &= 0xff00; + BX_NE2K_THIS s.tx_bytes |= (value & 0xff); + break; + + case 0x6: // TBCR1 + // Clear out high byte and re-insert + BX_NE2K_THIS s.tx_bytes &= 0x00ff; + BX_NE2K_THIS s.tx_bytes |= ((value & 0xff) << 8); + break; + + case 0x7: // ISR + value &= 0x7f; // clear RST bit - status-only bit + // All other values are cleared iff the ISR bit is 1 + BX_NE2K_THIS s.ISR.pkt_rx &= ~((bx_bool)((value & 0x01) == 0x01)); + BX_NE2K_THIS s.ISR.pkt_tx &= ~((bx_bool)((value & 0x02) == 0x02)); + BX_NE2K_THIS s.ISR.rx_err &= ~((bx_bool)((value & 0x04) == 0x04)); + BX_NE2K_THIS s.ISR.tx_err &= ~((bx_bool)((value & 0x08) == 0x08)); + BX_NE2K_THIS s.ISR.overwrite &= ~((bx_bool)((value & 0x10) == 0x10)); + BX_NE2K_THIS s.ISR.cnt_oflow &= ~((bx_bool)((value & 0x20) == 0x20)); + BX_NE2K_THIS s.ISR.rdma_done &= ~((bx_bool)((value & 0x40) == 0x40)); + value = ((BX_NE2K_THIS s.ISR.rdma_done << 6) | + (BX_NE2K_THIS s.ISR.cnt_oflow << 5) | + (BX_NE2K_THIS s.ISR.overwrite << 4) | + (BX_NE2K_THIS s.ISR.tx_err << 3) | + (BX_NE2K_THIS s.ISR.rx_err << 2) | + (BX_NE2K_THIS s.ISR.pkt_tx << 1) | + (BX_NE2K_THIS s.ISR.pkt_rx)); + value &= ((BX_NE2K_THIS s.IMR.rdma_inte << 6) | + (BX_NE2K_THIS s.IMR.cofl_inte << 5) | + (BX_NE2K_THIS s.IMR.overw_inte << 4) | + (BX_NE2K_THIS s.IMR.txerr_inte << 3) | + (BX_NE2K_THIS s.IMR.rxerr_inte << 2) | + (BX_NE2K_THIS s.IMR.tx_inte << 1) | + (BX_NE2K_THIS s.IMR.rx_inte)); + if (value == 0) + DEV_pic_lower_irq(BX_NE2K_THIS s.base_irq); + break; + + case 0x8: // RSAR0 + // Clear out low byte and re-insert + BX_NE2K_THIS s.remote_start &= 0xff00; + BX_NE2K_THIS s.remote_start |= (value & 0xff); + BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.remote_start; + break; + + case 0x9: // RSAR1 + // Clear out high byte and re-insert + BX_NE2K_THIS s.remote_start &= 0x00ff; + BX_NE2K_THIS s.remote_start |= ((value & 0xff) << 8); + BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.remote_start; + break; + + case 0xa: // RBCR0 + // Clear out low byte and re-insert + BX_NE2K_THIS s.remote_bytes &= 0xff00; + BX_NE2K_THIS s.remote_bytes |= (value & 0xff); + break; + + case 0xb: // RBCR1 + // Clear out high byte and re-insert + BX_NE2K_THIS s.remote_bytes &= 0x00ff; + BX_NE2K_THIS s.remote_bytes |= ((value & 0xff) << 8); + break; + + case 0xc: // RCR + // Check if the reserved bits are set + if (value & 0xc0) + BX_INFO(("RCR write, reserved bits set")); + + // Set all other bit-fields + BX_NE2K_THIS s.RCR.errors_ok = ((value & 0x01) == 0x01); + BX_NE2K_THIS s.RCR.runts_ok = ((value & 0x02) == 0x02); + BX_NE2K_THIS s.RCR.broadcast = ((value & 0x04) == 0x04); + BX_NE2K_THIS s.RCR.multicast = ((value & 0x08) == 0x08); + BX_NE2K_THIS s.RCR.promisc = ((value & 0x10) == 0x10); + BX_NE2K_THIS s.RCR.monitor = ((value & 0x20) == 0x20); + + // Monitor bit is a little suspicious... + if (value & 0x20) + BX_INFO(("RCR write, monitor bit set!")); + break; + + case 0xd: // TCR + // Check reserved bits + if (value & 0xe0) + BX_ERROR(("TCR write, reserved bits set")); + + // Test loop mode (not supported) + if (value & 0x06) { + BX_NE2K_THIS s.TCR.loop_cntl = (value & 0x6) >> 1; + BX_INFO(("TCR write, loop mode %d not supported", BX_NE2K_THIS s.TCR.loop_cntl)); + } else { + BX_NE2K_THIS s.TCR.loop_cntl = 0; + } + + // Inhibit-CRC not supported. + if (value & 0x01) + BX_PANIC(("TCR write, inhibit-CRC not supported")); + + // Auto-transmit disable very suspicious + if (value & 0x08) + BX_PANIC(("TCR write, auto transmit disable not supported")); + + // Allow collision-offset to be set, although not used + BX_NE2K_THIS s.TCR.coll_prio = ((value & 0x08) == 0x08); + break; + + case 0xe: // DCR + // the loopback mode is not suppported yet + if (!(value & 0x08)) { + BX_ERROR(("DCR write, loopback mode selected")); + } + // It is questionable to set longaddr and auto_rx, since they + // aren't supported on the ne2000. Print a warning and continue + if (value & 0x04) + BX_INFO(("DCR write - LAS set ???")); + if (value & 0x10) + BX_INFO(("DCR write - AR set ???")); + + // Set other values. + BX_NE2K_THIS s.DCR.wdsize = ((value & 0x01) == 0x01); + BX_NE2K_THIS s.DCR.endian = ((value & 0x02) == 0x02); + BX_NE2K_THIS s.DCR.longaddr = ((value & 0x04) == 0x04); // illegal ? + BX_NE2K_THIS s.DCR.loop = ((value & 0x08) == 0x08); + BX_NE2K_THIS s.DCR.auto_rx = ((value & 0x10) == 0x10); // also illegal ? + BX_NE2K_THIS s.DCR.fifo_size = (value & 0x50) >> 5; + break; + + case 0xf: // IMR + // Check for reserved bit + if (value & 0x80) + BX_PANIC(("IMR write, reserved bit set")); + + // Set other values + BX_NE2K_THIS s.IMR.rx_inte = ((value & 0x01) == 0x01); + BX_NE2K_THIS s.IMR.tx_inte = ((value & 0x02) == 0x02); + BX_NE2K_THIS s.IMR.rxerr_inte = ((value & 0x04) == 0x04); + BX_NE2K_THIS s.IMR.txerr_inte = ((value & 0x08) == 0x08); + BX_NE2K_THIS s.IMR.overw_inte = ((value & 0x10) == 0x10); + BX_NE2K_THIS s.IMR.cofl_inte = ((value & 0x20) == 0x20); + BX_NE2K_THIS s.IMR.rdma_inte = ((value & 0x40) == 0x40); + break; + + default: + BX_PANIC(("page 0 write, bad offset %0x", offset)); + } +} + + +// +// page1_read/page1_write - These routines handle reads/writes to +// the first page of the DS8390 register file +// +Bit32u +bx_ne2k_c::page1_read(Bit32u offset, unsigned int io_len) +{ + BX_DEBUG(("page 1 read from port %04x, len=%u", (unsigned) offset, + (unsigned) io_len)); + if (io_len > 1) + BX_PANIC(("bad length! page 1 read from port %04x, len=%u", (unsigned) offset, + (unsigned) io_len)); + + switch (offset) { + case 0x0: // CR + return (read_cr()); + break; + + case 0x1: // PAR0-5 + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + return (BX_NE2K_THIS s.physaddr[offset - 1]); + break; + + case 0x7: // CURR + BX_DEBUG(("returning current page: %02x", (BX_NE2K_THIS s.curr_page))); + return (BX_NE2K_THIS s.curr_page); + + case 0x8: // MAR0-7 + case 0x9: + case 0xa: + case 0xb: + case 0xc: + case 0xd: + case 0xe: + case 0xf: + return (BX_NE2K_THIS s.mchash[offset - 8]); + break; + + default: + BX_PANIC(("page 1 r offset %04x out of range", (unsigned) offset)); + } + + return (0); +} + +void +bx_ne2k_c::page1_write(Bit32u offset, Bit32u value, unsigned io_len) +{ + BX_DEBUG(("page 1 w offset %04x", (unsigned) offset)); + switch (offset) { + case 0x0: // CR + write_cr(value); + break; + + case 0x1: // PAR0-5 + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + BX_NE2K_THIS s.physaddr[offset - 1] = value; + break; + + case 0x7: // CURR + BX_NE2K_THIS s.curr_page = value; + break; + + case 0x8: // MAR0-7 + case 0x9: + case 0xa: + case 0xb: + case 0xc: + case 0xd: + case 0xe: + case 0xf: + BX_NE2K_THIS s.mchash[offset - 8] = value; + break; + + default: + BX_PANIC(("page 1 w offset %04x out of range", (unsigned) offset)); + } +} + + +// +// page2_read/page2_write - These routines handle reads/writes to +// the second page of the DS8390 register file +// +Bit32u +bx_ne2k_c::page2_read(Bit32u offset, unsigned int io_len) +{ + BX_DEBUG(("page 2 read from port %04x, len=%u", (unsigned) offset, (unsigned) io_len)); + + if (io_len > 1) + BX_PANIC(("bad length! page 2 read from port %04x, len=%u", (unsigned) offset, (unsigned) io_len)); + + switch (offset) { + case 0x0: // CR + return (read_cr()); + break; + + case 0x1: // PSTART + return (BX_NE2K_THIS s.page_start); + break; + + case 0x2: // PSTOP + return (BX_NE2K_THIS s.page_stop); + break; + + case 0x3: // Remote Next-packet pointer + return (BX_NE2K_THIS s.rempkt_ptr); + break; + + case 0x4: // TPSR + return (BX_NE2K_THIS s.tx_page_start); + break; + + case 0x5: // Local Next-packet pointer + return (BX_NE2K_THIS s.localpkt_ptr); + break; + + case 0x6: // Address counter (upper) + return (BX_NE2K_THIS s.address_cnt >> 8); + break; + + case 0x7: // Address counter (lower) + return (BX_NE2K_THIS s.address_cnt & 0xff); + break; + + case 0x8: // Reserved + case 0x9: + case 0xa: + case 0xb: + BX_ERROR(("reserved read - page 2, 0x%02x", (unsigned) offset)); + return (0xff); + break; + + case 0xc: // RCR + return ((BX_NE2K_THIS s.RCR.monitor << 5) | + (BX_NE2K_THIS s.RCR.promisc << 4) | + (BX_NE2K_THIS s.RCR.multicast << 3) | + (BX_NE2K_THIS s.RCR.broadcast << 2) | + (BX_NE2K_THIS s.RCR.runts_ok << 1) | + (BX_NE2K_THIS s.RCR.errors_ok)); + break; + + case 0xd: // TCR + return ((BX_NE2K_THIS s.TCR.coll_prio << 4) | + (BX_NE2K_THIS s.TCR.ext_stoptx << 3) | + ((BX_NE2K_THIS s.TCR.loop_cntl & 0x3) << 1) | + (BX_NE2K_THIS s.TCR.crc_disable)); + break; + + case 0xe: // DCR + return (((BX_NE2K_THIS s.DCR.fifo_size & 0x3) << 5) | + (BX_NE2K_THIS s.DCR.auto_rx << 4) | + (BX_NE2K_THIS s.DCR.loop << 3) | + (BX_NE2K_THIS s.DCR.longaddr << 2) | + (BX_NE2K_THIS s.DCR.endian << 1) | + (BX_NE2K_THIS s.DCR.wdsize)); + break; + + case 0xf: // IMR + return ((BX_NE2K_THIS s.IMR.rdma_inte << 6) | + (BX_NE2K_THIS s.IMR.cofl_inte << 5) | + (BX_NE2K_THIS s.IMR.overw_inte << 4) | + (BX_NE2K_THIS s.IMR.txerr_inte << 3) | + (BX_NE2K_THIS s.IMR.rxerr_inte << 2) | + (BX_NE2K_THIS s.IMR.tx_inte << 1) | + (BX_NE2K_THIS s.IMR.rx_inte)); + break; + + default: + BX_PANIC(("page 2 offset %04x out of range", (unsigned) offset)); + } + + return (0); +}; + +void +bx_ne2k_c::page2_write(Bit32u offset, Bit32u value, unsigned io_len) +{ + // Maybe all writes here should be BX_PANIC()'d, since they + // affect internal operation, but let them through for now + // and print a warning. + if (offset != 0) + BX_ERROR(("page 2 write ?")); + + switch (offset) { + case 0x0: // CR + write_cr(value); + break; + + case 0x1: // CLDA0 + // Clear out low byte and re-insert + BX_NE2K_THIS s.local_dma &= 0xff00; + BX_NE2K_THIS s.local_dma |= (value & 0xff); + break; + + case 0x2: // CLDA1 + // Clear out high byte and re-insert + BX_NE2K_THIS s.local_dma &= 0x00ff; + BX_NE2K_THIS s.local_dma |= ((value & 0xff) << 8); + break; + + case 0x3: // Remote Next-pkt pointer + BX_NE2K_THIS s.rempkt_ptr = value; + break; + + case 0x4: + BX_PANIC(("page 2 write to reserved offset 4")); + break; + + case 0x5: // Local Next-packet pointer + BX_NE2K_THIS s.localpkt_ptr = value; + break; + + case 0x6: // Address counter (upper) + // Clear out high byte and re-insert + BX_NE2K_THIS s.address_cnt &= 0x00ff; + BX_NE2K_THIS s.address_cnt |= ((value & 0xff) << 8); + break; + + case 0x7: // Address counter (lower) + // Clear out low byte and re-insert + BX_NE2K_THIS s.address_cnt &= 0xff00; + BX_NE2K_THIS s.address_cnt |= (value & 0xff); + break; + + case 0x8: + case 0x9: + case 0xa: + case 0xb: + case 0xc: + case 0xd: + case 0xe: + case 0xf: + BX_PANIC(("page 2 write to reserved offset %0x", offset)); + break; + + default: + BX_PANIC(("page 2 write, illegal offset %0x", offset)); + break; + } +} + +// +// page3_read/page3_write - writes to this page are illegal +// +Bit32u +bx_ne2k_c::page3_read(Bit32u offset, unsigned int io_len) +{ + BX_PANIC(("page 3 read attempted")); + return (0); +} + +void +bx_ne2k_c::page3_write(Bit32u offset, Bit32u value, unsigned io_len) +{ + BX_PANIC(("page 3 write attempted")); +} + +// +// tx_timer_handler/tx_timer +// +void +bx_ne2k_c::tx_timer_handler(void *this_ptr) +{ + bx_ne2k_c *class_ptr = (bx_ne2k_c *) this_ptr; + + class_ptr->tx_timer(); +} + +void +bx_ne2k_c::tx_timer(void) +{ + BX_DEBUG(("tx_timer")); + BX_NE2K_THIS s.TSR.tx_ok = 1; + // Generate an interrupt if not masked and not one in progress + if (BX_NE2K_THIS s.IMR.tx_inte && !BX_NE2K_THIS s.ISR.pkt_tx) { + BX_NE2K_THIS s.ISR.pkt_tx = 1; + DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq); + } + BX_NE2K_THIS s.tx_timer_active = 0; +} + + +// +// read_handler/read - i/o 'catcher' function called from BOCHS +// mainline when the CPU attempts a read in the i/o space registered +// by this ne2000 instance +// +Bit32u +bx_ne2k_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_NE2K_SMF + bx_ne2k_c *class_ptr = (bx_ne2k_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + +Bit32u +bx_ne2k_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_NE2K_SMF + BX_DEBUG(("read addr %x, len %d", address, io_len)); + Bit32u retval = 0; + int offset = address - BX_NE2K_THIS s.base_address; + + if (offset >= 0x10) { + retval = asic_read(offset - 0x10, io_len); + } else { + switch (BX_NE2K_THIS s.CR.pgsel) { + case 0x00: + retval = page0_read(offset, io_len); + break; + + case 0x01: + retval = page1_read(offset, io_len); + break; + + case 0x02: + retval = page2_read(offset, io_len); + break; + + case 0x03: + retval = page3_read(offset, io_len); + break; + + default: + BX_PANIC(("ne2K: unknown value of pgsel in read - %d", + BX_NE2K_THIS s.CR.pgsel)); + } + } + + return (retval); +} + +// +// write_handler/write - i/o 'catcher' function called from BOCHS +// mainline when the CPU attempts a write in the i/o space registered +// by this ne2000 instance +// +void +bx_ne2k_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, + unsigned io_len) +{ +#if !BX_USE_NE2K_SMF + bx_ne2k_c *class_ptr = (bx_ne2k_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + +void +bx_ne2k_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_NE2K_SMF + BX_DEBUG(("write with length %d", io_len)); + int offset = address - BX_NE2K_THIS s.base_address; + + // + // The high 16 bytes of i/o space are for the ne2000 asic - + // the low 16 bytes are for the DS8390, with the current + // page being selected by the PS0,PS1 registers in the + // command register + // + if (offset >= 0x10) { + asic_write(offset - 0x10, value, io_len); + } else { + switch (BX_NE2K_THIS s.CR.pgsel) { + case 0x00: + page0_write(offset, value, io_len); + break; + + case 0x01: + page1_write(offset, value, io_len); + break; + + case 0x02: + page2_write(offset, value, io_len); + break; + + case 0x03: + page3_write(offset, value, io_len); + break; + + default: + BX_PANIC(("ne2K: unknown value of pgsel in write - %d", + BX_NE2K_THIS s.CR.pgsel)); + } + } +} + + +/* + * mcast_index() - return the 6-bit index into the multicast + * table. Stolen unashamedly from FreeBSD's if_ed.c + */ +unsigned +bx_ne2k_c::mcast_index(const void *dst) +{ +#define POLYNOMIAL 0x04c11db6 + unsigned long crc = 0xffffffffL; + int carry, i, j; + unsigned char b; + unsigned char *ep = (unsigned char *) dst; + + for (i = 6; --i >= 0;) { + b = *ep++; + for (j = 8; --j >= 0;) { + carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01); + crc <<= 1; + b >>= 1; + if (carry) + crc = ((crc ^ POLYNOMIAL) | carry); + } + } + return (crc >> 26); +#undef POLYNOMIAL +} + +/* + * Callback from the eth system driver when a frame has arrived + */ +void +bx_ne2k_c::rx_handler(void *arg, const void *buf, unsigned len) +{ + // BX_DEBUG(("rx_handler with length %d", len)); + bx_ne2k_c *class_ptr = (bx_ne2k_c *) arg; + + class_ptr->rx_frame(buf, len); +} + +/* + * rx_frame() - called by the platform-specific code when an + * ethernet frame has been received. The destination address + * is tested to see if it should be accepted, and if the + * rx ring has enough room, it is copied into it and + * the receive process is updated + */ +void +bx_ne2k_c::rx_frame(const void *buf, unsigned io_len) +{ + unsigned pages; + unsigned avail; + unsigned idx; + int wrapped; + int nextpage; + unsigned char pkthdr[4]; + unsigned char *pktbuf = (unsigned char *) buf; + unsigned char *startptr; + static unsigned char bcast_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; + + BX_DEBUG(("rx_frame with length %d", io_len)); + + + if ((BX_NE2K_THIS s.CR.stop != 0) || + (BX_NE2K_THIS s.page_start == 0) || + ((BX_NE2K_THIS s.DCR.loop == 0) && + (BX_NE2K_THIS s.TCR.loop_cntl != 0))) { + + return; + } + + // Add the pkt header + CRC to the length, and work + // out how many 256-byte pages the frame would occupy + pages = (io_len + 4 + 4 + 255)/256; + + if (BX_NE2K_THIS s.curr_page < BX_NE2K_THIS s.bound_ptr) { + avail = BX_NE2K_THIS s.bound_ptr - BX_NE2K_THIS s.curr_page; + } else { + avail = (BX_NE2K_THIS s.page_stop - BX_NE2K_THIS s.page_start) - + (BX_NE2K_THIS s.curr_page - BX_NE2K_THIS s.bound_ptr); + wrapped = 1; + } + + // Avoid getting into a buffer overflow condition by not attempting + // to do partial receives. The emulation to handle this condition + // seems particularly painful. + if ((avail < pages) +#if BX_NE2K_NEVER_FULL_RING + || (avail == pages) +#endif + ) { + return; + } + + if ((io_len < 60) && !BX_NE2K_THIS s.RCR.runts_ok) { + BX_DEBUG(("rejected small packet, length %d", io_len)); + return; + } + + // Do address filtering if not in promiscuous mode + if (! BX_NE2K_THIS s.RCR.promisc) { + if (!memcmp(buf, bcast_addr, 6)) { + if (!BX_NE2K_THIS s.RCR.broadcast) { + return; + } + } else if (pktbuf[0] & 0x01) { + if (! BX_NE2K_THIS s.RCR.multicast) { + return; + } + idx = mcast_index(buf); + if (!(BX_NE2K_THIS s.mchash[idx >> 3] & (1 << (idx & 0x7)))) { + return; + } + } else if (0 != memcmp(buf, BX_NE2K_THIS s.physaddr, 6)) { + return; + } + } else { + BX_DEBUG(("rx_frame promiscuous receive")); + } + +// BX_INFO(("rx_frame %d to %x:%x:%x:%x:%x:%x from %x:%x:%x:%x:%x:%x", +// io_len, +// pktbuf[0], pktbuf[1], pktbuf[2], pktbuf[3], pktbuf[4], pktbuf[5], +// pktbuf[6], pktbuf[7], pktbuf[8], pktbuf[9], pktbuf[10], pktbuf[11])); + + nextpage = BX_NE2K_THIS s.curr_page + pages; + if (nextpage >= BX_NE2K_THIS s.page_stop) { + nextpage -= BX_NE2K_THIS s.page_stop - BX_NE2K_THIS s.page_start; + } + + // Setup packet header + pkthdr[0] = 0; // rx status - old behavior + pkthdr[0] = 1; // Probably better to set it all the time + // rather than set it to 0, which is clearly wrong. + if (pktbuf[0] & 0x01) { + pkthdr[0] |= 0x20; // rx status += multicast packet + } + pkthdr[1] = nextpage; // ptr to next packet + pkthdr[2] = (io_len + 4) & 0xff; // length-low + pkthdr[3] = (io_len + 4) >> 8; // length-hi + + // copy into buffer, update curpage, and signal interrupt if config'd + startptr = & BX_NE2K_THIS s.mem[BX_NE2K_THIS s.curr_page * 256 - + BX_NE2K_MEMSTART]; + if ((nextpage > BX_NE2K_THIS s.curr_page) || + ((BX_NE2K_THIS s.curr_page + pages) == BX_NE2K_THIS s.page_stop)) { + memcpy(startptr, pkthdr, 4); + memcpy(startptr + 4, buf, io_len); + BX_NE2K_THIS s.curr_page = nextpage; + } else { + int endbytes = (BX_NE2K_THIS s.page_stop - BX_NE2K_THIS s.curr_page) + * 256; + memcpy(startptr, pkthdr, 4); + memcpy(startptr + 4, buf, endbytes - 4); + startptr = & BX_NE2K_THIS s.mem[BX_NE2K_THIS s.page_start * 256 - + BX_NE2K_MEMSTART]; + memcpy(startptr, (void *)(pktbuf + endbytes - 4), + io_len - endbytes + 8); + BX_NE2K_THIS s.curr_page = nextpage; + } + + BX_NE2K_THIS s.RSR.rx_ok = 1; + if (pktbuf[0] & 0x80) { + BX_NE2K_THIS s.RSR.rx_mbit = 1; + } + + BX_NE2K_THIS s.ISR.pkt_rx = 1; + + if (BX_NE2K_THIS s.IMR.rx_inte) { + DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq); + } + +} + +void +bx_ne2k_c::init(void) +{ + BX_DEBUG(("Init $Id: ne2k.cc,v 1.56.2.1 2004/02/02 22:37:22 cbothamy Exp $")); + + // Read in values from config file + BX_NE2K_THIS s.base_address = bx_options.ne2k.Oioaddr->get (); + BX_NE2K_THIS s.base_irq = bx_options.ne2k.Oirq->get (); + memcpy(BX_NE2K_THIS s.physaddr, bx_options.ne2k.Omacaddr->getptr (), 6); + + if (BX_NE2K_THIS s.tx_timer_index == BX_NULL_TIMER_HANDLE) { + BX_NE2K_THIS s.tx_timer_index = + bx_pc_system.register_timer(this, tx_timer_handler, 0, + 0,0, "ne2k"); // one-shot, inactive + } + // Register the IRQ and i/o port addresses + DEV_register_irq(BX_NE2K_THIS s.base_irq, "NE2000 ethernet NIC"); + + for (unsigned addr = BX_NE2K_THIS s.base_address; + addr <= BX_NE2K_THIS s.base_address + 0x20; + addr++) { + DEV_register_ioread_handler(this, read_handler, addr, "ne2000 NIC", 3); + DEV_register_iowrite_handler(this, write_handler, addr, "ne2000 NIC", 3); + } + BX_INFO(("port 0x%x/32 irq %d mac %02x:%02x:%02x:%02x:%02x:%02x", + BX_NE2K_THIS s.base_address, + BX_NE2K_THIS s.base_irq, + BX_NE2K_THIS s.physaddr[0], + BX_NE2K_THIS s.physaddr[1], + BX_NE2K_THIS s.physaddr[2], + BX_NE2K_THIS s.physaddr[3], + BX_NE2K_THIS s.physaddr[4], + BX_NE2K_THIS s.physaddr[5])); + + // Initialise the mac address area by doubling the physical address + BX_NE2K_THIS s.macaddr[0] = BX_NE2K_THIS s.physaddr[0]; + BX_NE2K_THIS s.macaddr[1] = BX_NE2K_THIS s.physaddr[0]; + BX_NE2K_THIS s.macaddr[2] = BX_NE2K_THIS s.physaddr[1]; + BX_NE2K_THIS s.macaddr[3] = BX_NE2K_THIS s.physaddr[1]; + BX_NE2K_THIS s.macaddr[4] = BX_NE2K_THIS s.physaddr[2]; + BX_NE2K_THIS s.macaddr[5] = BX_NE2K_THIS s.physaddr[2]; + BX_NE2K_THIS s.macaddr[6] = BX_NE2K_THIS s.physaddr[3]; + BX_NE2K_THIS s.macaddr[7] = BX_NE2K_THIS s.physaddr[3]; + BX_NE2K_THIS s.macaddr[8] = BX_NE2K_THIS s.physaddr[4]; + BX_NE2K_THIS s.macaddr[9] = BX_NE2K_THIS s.physaddr[4]; + BX_NE2K_THIS s.macaddr[10] = BX_NE2K_THIS s.physaddr[5]; + BX_NE2K_THIS s.macaddr[11] = BX_NE2K_THIS s.physaddr[5]; + + // ne2k signature + for (int i = 12; i < 32; i++) + BX_NE2K_THIS s.macaddr[i] = 0x57; + + // Attach to the simulated ethernet dev + char *ethmod = bx_options.ne2k.Oethmod->get_choice(bx_options.ne2k.Oethmod->get()); + BX_NE2K_THIS ethdev = eth_locator_c::create(ethmod, + bx_options.ne2k.Oethdev->getptr (), + (const char *) bx_options.ne2k.Omacaddr->getptr (), + rx_handler, + this); + + if (BX_NE2K_THIS ethdev == NULL) { + BX_PANIC(("could not find eth module %s", ethmod)); + // if they continue, use null. + BX_INFO(("could not find eth module %s - using null instead", ethmod)); + + BX_NE2K_THIS ethdev = eth_locator_c::create("null", NULL, + (const char *) bx_options.ne2k.Omacaddr->getptr (), + rx_handler, + this); + if (BX_NE2K_THIS ethdev == NULL) + BX_PANIC(("could not locate null module")); + } + + // Bring the register state into power-up state + theNE2kDevice->reset(BX_RESET_HARDWARE); +} + +#if BX_DEBUGGER + +/* + * this implements the info ne2k commands in the debugger. + * info ne2k - shows all registers + * info ne2k page N - shows all registers in a page + * info ne2k page N reg M - shows just one register + */ + +#define SHOW_FIELD(reg,field) do { \ + if (n>0 && !(n%5)) dbg_printf ("\n "); \ + dbg_printf ("%s=%d ", #field, BX_NE2K_THIS s.reg.field); \ + n++; \ +} while (0); +#define BX_HIGH_BYTE(x) ((0xff00 & (x)) >> 8) +#define BX_LOW_BYTE(x) (0x00ff & (x)) +#define BX_DUPLICATE(n) if (brief && num!=n) break; + +void +bx_ne2k_c::print_info (FILE *fp, int page, int reg, int brief) +{ + int i; + int n = 0; + if (page < 0) { + for (page=0; page<=2; page++) + theNE2kDevice->print_info (fp, page, reg, 1); + // tell them how to use this command + dbg_printf ("\nHow to use the info ne2k command:\n"); + dbg_printf ("info ne2k - show all registers\n"); + dbg_printf ("info ne2k page N - show registers in page N\n"); + dbg_printf ("info ne2k page N reg M - show just one register\n"); + return; + } + if (page > 2) { + dbg_printf ("NE2K has only pages 0, 1, and 2. Page %d is out of range.\n", page); + return; + } + if (reg < 0) { + dbg_printf ("NE2K registers, page %d\n", page); + dbg_printf ("----------------------\n"); + for (reg=0; reg<=15; reg++) + theNE2kDevice->print_info (fp, page, reg, 1); + dbg_printf ("----------------------\n"); + return; + } + if (reg > 15) { + dbg_printf ("NE2K has only registers 0-15 (0x0-0xf). Register %d is out of range.\n", reg); + return; + } + if (!brief) { + dbg_printf ("NE2K Info - page %d, register 0x%02x\n", page, reg); + dbg_printf ("----------------------------------\n"); + } + int num = page*0x100 + reg; + switch (num) { + case 0x0000: + case 0x0100: + case 0x0200: + dbg_printf ("CR (Command register):\n "); + SHOW_FIELD (CR, stop); + SHOW_FIELD (CR, start); + SHOW_FIELD (CR, tx_packet); + SHOW_FIELD (CR, rdma_cmd); + SHOW_FIELD (CR, pgsel); + dbg_printf ("\n"); + break; + case 0x0003: + dbg_printf ("BNRY = Boundary Pointer = 0x%02x\n", BX_NE2K_THIS s.bound_ptr); + break; + case 0x0004: + dbg_printf ("TSR (Transmit Status Register), read-only:\n "); + SHOW_FIELD (TSR, tx_ok); + SHOW_FIELD (TSR, reserved); + SHOW_FIELD (TSR, collided); + SHOW_FIELD (TSR, aborted); + SHOW_FIELD (TSR, no_carrier); + SHOW_FIELD (TSR, fifo_ur); + SHOW_FIELD (TSR, cd_hbeat); + SHOW_FIELD (TSR, ow_coll); + dbg_printf ("\n"); + // fall through into TPSR, no break line. + case 0x0204: + dbg_printf ("TPSR = Transmit Page Start = 0x%02x\n", BX_NE2K_THIS s.tx_page_start); + break; + case 0x0005: + case 0x0006: BX_DUPLICATE(0x0005); + dbg_printf ("NCR = Number of Collisions Register (read-only) = 0x%02x\n", BX_NE2K_THIS s.num_coll); + dbg_printf ("TBCR1,TBCR0 = Transmit Byte Count = %02x %02x\n", + BX_HIGH_BYTE (BX_NE2K_THIS s.tx_bytes), + BX_LOW_BYTE (BX_NE2K_THIS s.tx_bytes)); + dbg_printf ("FIFO = %02x\n", BX_NE2K_THIS s.fifo); + break; + case 0x0007: + dbg_printf ("ISR (Interrupt Status Register):\n "); + SHOW_FIELD (ISR, pkt_rx); + SHOW_FIELD (ISR, pkt_tx); + SHOW_FIELD (ISR, rx_err); + SHOW_FIELD (ISR, tx_err); + SHOW_FIELD (ISR, overwrite); + SHOW_FIELD (ISR, cnt_oflow); + SHOW_FIELD (ISR, rdma_done); + SHOW_FIELD (ISR, reset); + dbg_printf ("\n"); + break; + case 0x0008: + case 0x0009: BX_DUPLICATE(0x0008); + dbg_printf ("CRDA1,0 = Current remote DMA address = %02x %02x\n", + BX_HIGH_BYTE (BX_NE2K_THIS s.remote_dma), + BX_LOW_BYTE (BX_NE2K_THIS s.remote_dma)); + dbg_printf ("RSAR1,0 = Remote start address = %02x %02x\n", + BX_HIGH_BYTE(s.remote_start), + BX_LOW_BYTE(s.remote_start)); + break; + case 0x000a: + case 0x000b: BX_DUPLICATE(0x000a); + dbg_printf ("RCBR1,0 = Remote byte count = %02x\n", BX_NE2K_THIS s.remote_bytes); + break; + case 0x000c: + dbg_printf ("RSR (Receive Status Register), read-only:\n "); + SHOW_FIELD (RSR, rx_ok); + SHOW_FIELD (RSR, bad_crc); + SHOW_FIELD (RSR, bad_falign); + SHOW_FIELD (RSR, fifo_or); + SHOW_FIELD (RSR, rx_missed); + SHOW_FIELD (RSR, rx_mbit); + SHOW_FIELD (RSR, rx_disabled); + SHOW_FIELD (RSR, deferred); + dbg_printf ("\n"); + // fall through into RCR + case 0x020c: + dbg_printf ("RCR (Receive Configuration Register):\n "); + SHOW_FIELD (RCR, errors_ok); + SHOW_FIELD (RCR, runts_ok); + SHOW_FIELD (RCR, broadcast); + SHOW_FIELD (RCR, multicast); + SHOW_FIELD (RCR, promisc); + SHOW_FIELD (RCR, monitor); + SHOW_FIELD (RCR, reserved); + dbg_printf ("\n"); + break; + case 0x000d: + dbg_printf ("CNTR0 = Tally Counter 0 (Frame alignment errors) = %02x\n", + BX_NE2K_THIS s.tallycnt_0); + // fall through into TCR + case 0x020d: + dbg_printf ("TCR (Transmit Configuration Register):\n "); + SHOW_FIELD (TCR, crc_disable); + SHOW_FIELD (TCR, loop_cntl); + SHOW_FIELD (TCR, ext_stoptx); + SHOW_FIELD (TCR, coll_prio); + SHOW_FIELD (TCR, reserved); + dbg_printf ("\n"); + break; + case 0x000e: + dbg_printf ("CNTR1 = Tally Counter 1 (CRC Errors) = %02x\n", + BX_NE2K_THIS s.tallycnt_1); + // fall through into DCR + case 0x020e: + dbg_printf ("DCR (Data Configuration Register):\n "); + SHOW_FIELD (DCR, wdsize); + SHOW_FIELD (DCR, endian); + SHOW_FIELD (DCR, longaddr); + SHOW_FIELD (DCR, loop); + SHOW_FIELD (DCR, auto_rx); + SHOW_FIELD (DCR, fifo_size); + dbg_printf ("\n"); + break; + case 0x000f: + dbg_printf ("CNTR2 = Tally Counter 2 (Missed Packet Errors) = %02x\n", + BX_NE2K_THIS s.tallycnt_2); + // fall through into IMR + case 0x020f: + dbg_printf ("IMR (Interrupt Mask Register)\n "); + SHOW_FIELD (IMR, rx_inte); + SHOW_FIELD (IMR, tx_inte); + SHOW_FIELD (IMR, rxerr_inte); + SHOW_FIELD (IMR, txerr_inte); + SHOW_FIELD (IMR, overw_inte); + SHOW_FIELD (IMR, cofl_inte); + SHOW_FIELD (IMR, rdma_inte); + SHOW_FIELD (IMR, reserved); + dbg_printf ("\n"); + break; + case 0x0101: + case 0x0102: BX_DUPLICATE(0x0101); + case 0x0103: BX_DUPLICATE(0x0101); + case 0x0104: BX_DUPLICATE(0x0101); + case 0x0105: BX_DUPLICATE(0x0101); + case 0x0106: BX_DUPLICATE(0x0101); + dbg_printf ("MAC address registers are located at page 1, registers 1-6.\n"); + dbg_printf ("The MAC address is "); + for (i=0; i<=5; i++) + dbg_printf ("%02x%c", BX_NE2K_THIS s.physaddr[i], i<5?':' : '\n'); + break; + case 0x0107: + dbg_printf ("Current page is 0x%02x\n", BX_NE2K_THIS s.curr_page); + break; + case 0x0108: + case 0x0109: BX_DUPLICATE(0x0108); + case 0x010A: BX_DUPLICATE(0x0108); + case 0x010B: BX_DUPLICATE(0x0108); + case 0x010C: BX_DUPLICATE(0x0108); + case 0x010D: BX_DUPLICATE(0x0108); + case 0x010E: BX_DUPLICATE(0x0108); + case 0x010F: BX_DUPLICATE(0x0108); + dbg_printf ("MAR0-7 (Multicast address registers 0-7) are set to:\n"); + for (i=0; i<8; i++) dbg_printf ("%02x ", BX_NE2K_THIS s.mchash[i]); + dbg_printf ("\nMAR0 is listed first.\n"); + break; + case 0x0001: + case 0x0002: BX_DUPLICATE(0x0001); + case 0x0201: BX_DUPLICATE(0x0001); + case 0x0202: BX_DUPLICATE(0x0001); + dbg_printf ("PSTART = Page start register = %02x\n", BX_NE2K_THIS s.page_start); + dbg_printf ("PSTOP = Page stop register = %02x\n", BX_NE2K_THIS s.page_stop); + dbg_printf ("Local DMA address = %02x %02x\n", + BX_HIGH_BYTE(BX_NE2K_THIS s.local_dma), + BX_LOW_BYTE(BX_NE2K_THIS s.local_dma)); + break; + case 0x0203: + dbg_printf ("Remote Next Packet Pointer = %02x\n", BX_NE2K_THIS s.rempkt_ptr); + break; + case 0x0205: + dbg_printf ("Local Next Packet Pointer = %02x\n", BX_NE2K_THIS s.localpkt_ptr); + break; + case 0x0206: + case 0x0207: BX_DUPLICATE(0x0206); + dbg_printf ("Address Counter= %02x %02x\n", + BX_HIGH_BYTE(BX_NE2K_THIS s.address_cnt), + BX_LOW_BYTE(BX_NE2K_THIS s.address_cnt)); + break; + case 0x0208: + case 0x0209: BX_DUPLICATE(0x0208); + case 0x020A: BX_DUPLICATE(0x0208); + case 0x020B: BX_DUPLICATE(0x0208); + if (!brief) dbg_printf ("Reserved\n"); + case 0xffff: + dbg_printf ("IMR (Interrupt Mask Register):\n "); + dbg_printf ("\n"); + break; + default: + dbg_printf ("NE2K info: sorry, page %d register %d cannot be displayed.\n", page, reg); + } + if (!brief) + dbg_printf ("\n"); +} + +#else + +void +bx_ne2k_c::print_info (FILE *fp, int page, int reg, int brief) +{ +} + +#endif + +#endif /* if BX_NE2K_SUPPORT */ diff --git a/tools/ioemu/iodev/ne2k.h b/tools/ioemu/iodev/ne2k.h new file mode 100644 index 0000000000..37cc712106 --- /dev/null +++ b/tools/ioemu/iodev/ne2k.h @@ -0,0 +1,239 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: ne2k.h,v 1.11 2003/03/02 23:59:11 cbothamy 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 + +// Peter Grehan (grehan@iprg.nokia.com) coded all of this +// NE2000/ether stuff. + +// +// An implementation of an ne2000 ISA ethernet adapter. This part uses +// a National Semiconductor DS-8390 ethernet MAC chip, with some h/w +// to provide a windowed memory region for the chip and a MAC address. +// + + +#if BX_USE_NE2K_SMF +# define BX_NE2K_SMF static +# define BX_NE2K_THIS theNE2kDevice-> +#else +# define BX_NE2K_SMF +# define BX_NE2K_THIS this-> +#endif + +#define BX_NE2K_MEMSIZ (32*1024) +#define BX_NE2K_MEMSTART (16*1024) +#define BX_NE2K_MEMEND (BX_NE2K_MEMSTART + BX_NE2K_MEMSIZ) + +typedef struct { + // + // ne2k register state + + // + // Page 0 + // + // Command Register - 00h read/write + struct { + bx_bool stop; // STP - Software Reset command + bx_bool start; // START - start the NIC + bx_bool tx_packet; // TXP - initiate packet transmission + Bit8u rdma_cmd; // RD0,RD1,RD2 - Remote DMA command + Bit8u pgsel; // PS0,PS1 - Page select + } CR; + // Interrupt Status Register - 07h read/write + struct { + bx_bool pkt_rx; // PRX - packet received with no errors + bx_bool pkt_tx; // PTX - packet transmitted with no errors + bx_bool rx_err; // RXE - packet received with 1 or more errors + bx_bool tx_err; // TXE - packet tx'd " " " " " + bx_bool overwrite; // OVW - rx buffer resources exhausted + bx_bool cnt_oflow; // CNT - network tally counter MSB's set + bx_bool rdma_done; // RDC - remote DMA complete + bx_bool reset; // RST - reset status + } ISR; + // Interrupt Mask Register - 0fh write + struct { + bx_bool rx_inte; // PRXE - packet rx interrupt enable + bx_bool tx_inte; // PTXE - packet tx interrput enable + bx_bool rxerr_inte; // RXEE - rx error interrupt enable + bx_bool txerr_inte; // TXEE - tx error interrupt enable + bx_bool overw_inte; // OVWE - overwrite warn int enable + bx_bool cofl_inte; // CNTE - counter o'flow int enable + bx_bool rdma_inte; // RDCE - remote DMA complete int enable + bx_bool reserved; // D7 - reserved + } IMR; + // Data Configuration Register - 0eh write + struct { + bx_bool wdsize; // WTS - 8/16-bit select + bx_bool endian; // BOS - byte-order select + bx_bool longaddr; // LAS - long-address select + bx_bool loop; // LS - loopback select + bx_bool auto_rx; // AR - auto-remove rx packets with remote DMA + Bit8u fifo_size; // FT0,FT1 - fifo threshold + } DCR; + // Transmit Configuration Register - 0dh write + struct { + bx_bool crc_disable; // CRC - inhibit tx CRC + Bit8u loop_cntl; // LB0,LB1 - loopback control + bx_bool ext_stoptx; // ATD - allow tx disable by external mcast + bx_bool coll_prio; // OFST - backoff algorithm select + Bit8u reserved; // D5,D6,D7 - reserved + } TCR; + // Transmit Status Register - 04h read + struct { + bx_bool tx_ok; // PTX - tx complete without error + bx_bool reserved; // D1 - reserved + bx_bool collided; // COL - tx collided >= 1 times + bx_bool aborted; // ABT - aborted due to excessive collisions + bx_bool no_carrier; // CRS - carrier-sense lost + bx_bool fifo_ur; // FU - FIFO underrun + bx_bool cd_hbeat; // CDH - no tx cd-heartbeat from transceiver + bx_bool ow_coll; // OWC - out-of-window collision + } TSR; + // Receive Configuration Register - 0ch write + struct { + bx_bool errors_ok; // SEP - accept pkts with rx errors + bx_bool runts_ok; // AR - accept < 64-byte runts + bx_bool broadcast; // AB - accept eth broadcast address + bx_bool multicast; // AM - check mcast hash array + bx_bool promisc; // PRO - accept all packets + bx_bool monitor; // MON - check pkts, but don't rx + Bit8u reserved; // D6,D7 - reserved + } RCR; + // Receive Status Register - 0ch read + struct { + bx_bool rx_ok; // PRX - rx complete without error + bx_bool bad_crc; // CRC - Bad CRC detected + bx_bool bad_falign; // FAE - frame alignment error + bx_bool fifo_or; // FO - FIFO overrun + bx_bool rx_missed; // MPA - missed packet error + bx_bool rx_mbit; // PHY - unicast or mcast/bcast address match + bx_bool rx_disabled; // DIS - set when in monitor mode + bx_bool deferred; // DFR - collision active + } RSR; + + Bit16u local_dma; // 01,02h read ; current local DMA addr + Bit8u page_start; // 01h write ; page start register + Bit8u page_stop; // 02h write ; page stop register + Bit8u bound_ptr; // 03h read/write ; boundary pointer + Bit8u tx_page_start; // 04h write ; transmit page start register + Bit8u num_coll; // 05h read ; number-of-collisions register + Bit16u tx_bytes; // 05,06h write ; transmit byte-count register + Bit8u fifo; // 06h read ; FIFO + Bit16u remote_dma; // 08,09h read ; current remote DMA addr + Bit16u remote_start; // 08,09h write ; remote start address register + Bit16u remote_bytes; // 0a,0bh write ; remote byte-count register + Bit8u tallycnt_0; // 0dh read ; tally counter 0 (frame align errors) + Bit8u tallycnt_1; // 0eh read ; tally counter 1 (CRC errors) + Bit8u tallycnt_2; // 0fh read ; tally counter 2 (missed pkt errors) + + // + // Page 1 + // + // Command Register 00h (repeated) + // + Bit8u physaddr[6]; // 01-06h read/write ; MAC address + Bit8u curr_page; // 07h read/write ; current page register + Bit8u mchash[8]; // 08-0fh read/write ; multicast hash array + + // + // Page 2 - diagnostic use only + // + // Command Register 00h (repeated) + // + // Page Start Register 01h read (repeated) + // Page Stop Register 02h read (repeated) + // Current Local DMA Address 01,02h write (repeated) + // Transmit Page start address 04h read (repeated) + // Receive Configuration Register 0ch read (repeated) + // Transmit Configuration Register 0dh read (repeated) + // Data Configuration Register 0eh read (repeated) + // Interrupt Mask Register 0fh read (repeated) + // + Bit8u rempkt_ptr; // 03h read/write ; remote next-packet pointer + Bit8u localpkt_ptr; // 05h read/write ; local next-packet pointer + Bit16u address_cnt; // 06,07h read/write ; address counter + + // + // Page 3 - should never be modified. + // + + // Novell ASIC state + Bit8u macaddr[32]; // ASIC ROM'd MAC address, even bytes + Bit8u mem[BX_NE2K_MEMSIZ]; // on-chip packet memory + + // ne2k internal state + Bit32u base_address; + int base_irq; + int tx_timer_index; + int tx_timer_active; + +} bx_ne2k_t; + + + +class bx_ne2k_c : public bx_ne2k_stub_c { +public: + bx_ne2k_c(void); + ~bx_ne2k_c(void); + virtual void init(void); + virtual void reset(unsigned type); + virtual void print_info (FILE *file, int page, int reg, int nodups); + +private: + bx_ne2k_t s; + + eth_pktmover_c *ethdev; + + BX_NE2K_SMF Bit32u read_cr(void); + BX_NE2K_SMF void write_cr(Bit32u value); + + BX_NE2K_SMF Bit32u chipmem_read(Bit32u address, unsigned io_len) BX_CPP_AttrRegparmN(2); + BX_NE2K_SMF Bit32u asic_read(Bit32u offset, unsigned io_len) BX_CPP_AttrRegparmN(2); + BX_NE2K_SMF Bit32u page0_read(Bit32u offset, unsigned io_len); + BX_NE2K_SMF Bit32u page1_read(Bit32u offset, unsigned io_len); + BX_NE2K_SMF Bit32u page2_read(Bit32u offset, unsigned io_len); + BX_NE2K_SMF Bit32u page3_read(Bit32u offset, unsigned io_len); + + BX_NE2K_SMF void chipmem_write(Bit32u address, Bit32u value, unsigned io_len) BX_CPP_AttrRegparmN(3); + BX_NE2K_SMF void asic_write(Bit32u address, Bit32u value, unsigned io_len); + BX_NE2K_SMF void page0_write(Bit32u address, Bit32u value, unsigned io_len); + BX_NE2K_SMF void page1_write(Bit32u address, Bit32u value, unsigned io_len); + BX_NE2K_SMF void page2_write(Bit32u address, Bit32u value, unsigned io_len); + BX_NE2K_SMF void page3_write(Bit32u address, Bit32u value, unsigned io_len); + + static void tx_timer_handler(void *); + BX_NE2K_SMF void tx_timer(void); + + static void rx_handler(void *arg, const void *buf, unsigned len); + BX_NE2K_SMF unsigned mcast_index(const void *dst); + BX_NE2K_SMF void rx_frame(const void *buf, unsigned io_len); + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#if !BX_USE_NE2K_SMF + Bit32u read(Bit32u address, unsigned io_len); + void write(Bit32u address, Bit32u value, unsigned io_len); +#endif +}; diff --git a/tools/ioemu/iodev/osdep.cc b/tools/ioemu/iodev/osdep.cc new file mode 100644 index 0000000000..c010306c82 --- /dev/null +++ b/tools/ioemu/iodev/osdep.cc @@ -0,0 +1,340 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: osdep.cc,v 1.14.2.1 2004/02/06 22:14:34 danielg4 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 + +// +// osdep.cc +// +// Provide definition of library functions that are missing on various +// systems. The only reason this is a .cc file rather than a .c file +// is so that it can include bochs.h. Bochs.h includes all the required +// system headers, with appropriate #ifdefs for different compilers and +// platforms. +// + +#include "bochs.h" + +////////////////////////////////////////////////////////////////////// +// Missing library functions. These should work on any platform +// that needs them. +////////////////////////////////////////////////////////////////////// + +#if !BX_HAVE_SNPRINTF +/* XXX use real snprintf */ +/* if they don't have snprintf, just use sprintf */ +int bx_snprintf (char *s, size_t maxlen, const char *format, ...) +{ + va_list arg; + int done; + + va_start (arg, format); + done = vsprintf (s, format, arg); + va_end (arg); + + return done; +} +#endif /* !BX_HAVE_SNPRINTF */ + + +#if (!BX_HAVE_STRTOULL && !BX_HAVE_STRTOUQ) +/* taken from glibc-2.2.2: strtod.c, and stripped down a lot. There are + still a few leftover references to decimal points and exponents, + but it works for bases 10 and 16 */ + +#define RETURN(val,end) \ + do { if (endptr != NULL) *endptr = (char *) (end); \ + return val; } while (0) + +Bit64u +bx_strtoull (const char *nptr, char **endptr, int baseignore) +{ + int negative; /* The sign of the number. */ + int exponent; /* Exponent of the number. */ + + /* Numbers starting `0X' or `0x' have to be processed with base 16. */ + int base = 10; + + /* Number of bits currently in result value. */ + int bits; + + /* Running pointer after the last character processed in the string. */ + const char *cp, *tp; + /* Start of significant part of the number. */ + const char *startp, *start_of_digits; + /* Total number of digit and number of digits in integer part. */ + int dig_no; + /* Contains the last character read. */ + char c; + + Bit64s n = 0; + char const *p; + + /* Prepare number representation. */ + exponent = 0; + negative = 0; + bits = 0; + + /* Parse string to get maximal legal prefix. We need the number of + characters of the integer part, the fractional part and the exponent. */ + cp = nptr - 1; + /* Ignore leading white space. */ + do + c = *++cp; + while (isspace (c)); + + /* Get sign of the result. */ + if (c == '-') + { + negative = 1; + c = *++cp; + } + else if (c == '+') + c = *++cp; + + if (c < '0' || c > '9') + { + /* It is really a text we do not recognize. */ + RETURN (0, nptr); + } + + /* First look whether we are faced with a hexadecimal number. */ + if (c == '0' && tolower (cp[1]) == 'x') + { + /* Okay, it is a hexa-decimal number. Remember this and skip + the characters. BTW: hexadecimal numbers must not be + grouped. */ + base = 16; + cp += 2; + c = *cp; + } + + /* Record the start of the digits, in case we will check their grouping. */ + start_of_digits = startp = cp; + + /* Ignore leading zeroes. This helps us to avoid useless computations. */ + while (c == '0') + c = *++cp; + + /* If no other digit but a '0' is found the result is 0.0. + Return current read pointer. */ + if ((c < '0' || c > '9') + && (base == 16 && (c < tolower ('a') || c > tolower ('f'))) + && (base == 16 && (cp == start_of_digits || tolower (c) != 'p')) + && (base != 16 && tolower (c) != 'e')) + { + tp = start_of_digits; + /* If TP is at the start of the digits, there was no correctly + grouped prefix of the string; so no number found. */ + RETURN (0, tp == start_of_digits ? (base == 16 ? cp - 1 : nptr) : tp); + } + + /* Remember first significant digit and read following characters until the + decimal point, exponent character or any non-FP number character. */ + startp = cp; + dig_no = 0; + while (1) + { + if ((c >= '0' && c <= '9') + || (base == 16 && tolower (c) >= 'a' && tolower (c) <= 'f')) + ++dig_no; + else + break; + c = *++cp; + } + + /* The whole string is parsed. Store the address of the next character. */ + if (endptr) + *endptr = (char *) cp; + + if (dig_no == 0) + return 0; + + for (p=start_of_digits; p!=cp; p++) { + n = n * (Bit64s)base; + c = tolower (*p); + c = (c >= 'a') ? (10+c-'a') : c-'0'; + n = n + (Bit64s)c; + //printf ("after shifting in digit %c, n is %lld\n", *p, n); + } + return negative? -n : n; +} +#endif /* !BX_HAVE_STRTOULL */ + +#if BX_TEST_STRTOULL_MAIN +/* test driver for strtoull. Do not compile by default. */ +int main (int argc, char **argv) +{ + char buf[256], *endbuf; + long l; + Bit64s ll; + while (1) { + printf ("Enter a long int: "); + gets (buf); + l = strtoul (buf, &endbuf, 10); + printf ("As a long, %ld\n", l); + printf ("Endbuf is at buf[%d]\n", endbuf-buf); + ll = bx_strtoull (buf, &endbuf, 10); + printf ("As a long long, %lld\n", ll); + printf ("Endbuf is at buf[%d]\n", endbuf-buf); + } + return 0; +} +#endif /* BX_TEST_STRTOULL_MAIN */ + +#if !BX_HAVE_STRDUP +/* XXX use real strdup */ +char *bx_strdup(const char *str) +{ + char *temp; + + temp = (char*)malloc(strlen(str)+1); + sprintf(temp, "%s", str); + return temp; + + // Well, I'm sure this isn't how strdup is REALLY implemented, + // but it works... +} +#endif /* !BX_HAVE_STRDUP */ + +#if !BX_HAVE_STRREV +char *bx_strrev(char *str) +{ + char *p1, *p2; + + if (! str || ! *str) + return str; + + for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) { + *p1 ^= *p2; + *p2 ^= *p1; + *p1 ^= *p2; + } + return str; +} +#endif /* !BX_HAVE_STRREV */ + +#if BX_WITH_MACOS +namespace std{extern "C" {char *mktemp(char *tpl);}} +#endif +#if !BX_HAVE_MKSTEMP +int bx_mkstemp(char *tpl) +{ + mktemp(tpl); + return ::open(tpl, O_RDWR | O_CREAT | O_TRUNC +# ifdef O_BINARY + | O_BINARY +# endif + , S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP); +} +#endif // !BX_HAVE_MKSTEMP + +////////////////////////////////////////////////////////////////////// +// Missing library functions, implemented for MacOS only +////////////////////////////////////////////////////////////////////// + +#if BX_WITH_MACOS +// these functions are part of MacBochs. They are not intended to be +// portable! +#include <Devices.h> +#include <Files.h> +#include <Disks.h> + +int fd_read(char *buffer, Bit32u offset, Bit32u bytes) +{ + OSErr err; + IOParam param; + + param.ioRefNum=-5; // Refnum of the floppy disk driver + param.ioVRefNum=1; + param.ioPosMode=fsFromStart; + param.ioPosOffset=offset; + param.ioBuffer=buffer; + param.ioReqCount=bytes; + err = PBReadSync((union ParamBlockRec *)(¶m)); + return param.ioActCount; +} + +int fd_write(char *buffer, Bit32u offset, Bit32u bytes) +{ + OSErr err; + IOParam param; + + param.ioRefNum=-5; // Refnum of the floppy disk driver + param.ioVRefNum=1; + param.ioPosMode=fsFromStart; + param.ioPosOffset=offset; + param.ioBuffer=buffer; + param.ioReqCount=bytes; + err = PBWriteSync((union ParamBlockRec *)(¶m)); + return param.ioActCount; +} + +int fd_stat(struct stat *buf) +{ + OSErr err; + DrvSts status; + int result; + + result = 0; + err = DriveStatus(1, &status); + if (status.diskInPlace <1 || status.diskInPlace > 2) + result = -1; + buf->st_mode = S_IFCHR; + return result; +} +#endif /* BX_WITH_MACOS */ + + + +////////////////////////////////////////////////////////////////////// +// New functions to replace library functions +// with OS-independent versions +////////////////////////////////////////////////////////////////////// + +#if BX_HAVE_REALTIME_USEC +# if BX_HAVE_GETTIMEOFDAY +Bit64u bx_get_realtime64_usec (void) { + timeval thetime; + gettimeofday(&thetime,0); + Bit64u mytime; + mytime=(Bit64u)thetime.tv_sec*(Bit64u)1000000+(Bit64u)thetime.tv_usec; + return mytime; +} +# elif defined(WIN32) +Bit64u last_realtime64_top = 0; +Bit64u last_realtime64_bottom = 0; +Bit64u bx_get_realtime64_usec (void) { + Bit64u new_bottom = ((Bit64u) GetTickCount()) & BX_CONST64(0x0FFFFFFFF); + if(new_bottom < last_realtime64_bottom) { + last_realtime64_top += BX_CONST64(0x0000000100000000); + } + last_realtime64_bottom = new_bottom; + Bit64u interim_realtime64 = + (last_realtime64_top & BX_CONST64(0xFFFFFFFF00000000)) | + (new_bottom & BX_CONST64(0x00000000FFFFFFFF)); + return interim_realtime64*(BX_CONST64(1000)); +} +# endif +#endif diff --git a/tools/ioemu/iodev/parallel.cc b/tools/ioemu/iodev/parallel.cc new file mode 100644 index 0000000000..8b0d4b3245 --- /dev/null +++ b/tools/ioemu/iodev/parallel.cc @@ -0,0 +1,300 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: parallel.cc,v 1.24 2003/10/29 17:29:26 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 +// +//////////////////////////////////////////////////////// +// This code was just a few stubs until Volker.Ruppert@t-online.de +// fixed it up in November 2001. + + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#define LOG_THIS theParallelDevice-> + +bx_parallel_c *theParallelDevice = NULL; + + int +libparallel_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + theParallelDevice = new bx_parallel_c (); + bx_devices.pluginParallelDevice = theParallelDevice; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theParallelDevice, BX_PLUGIN_PARALLEL); + return(0); // Success +} + + void +libparallel_LTX_plugin_fini(void) +{ +} + +bx_parallel_c::bx_parallel_c(void) +{ + put("PAR"); + settype(PARLOG); + s.output = NULL; +} + +bx_parallel_c::~bx_parallel_c(void) +{ + if (s.output != NULL) + fclose(s.output); +} + + void +bx_parallel_c::init(void) +{ + BX_DEBUG(("Init $Id: parallel.cc,v 1.24 2003/10/29 17:29:26 vruppert Exp $")); + + if (bx_options.par[0].Oenabled->get ()) { + + /* PARALLEL PORT 1 */ + + DEV_register_irq(7, "Parallel Port 1"); + BX_INFO (("parallel port 1 at 0x378 irq 7")); + for (unsigned addr=0x0378; addr<=0x037A; addr++) { + DEV_register_ioread_handler(this, read_handler, addr, "Parallel Port 1", 1); + } + DEV_register_iowrite_handler(this, write_handler, 0x0378, "Parallel Port 1", 1); + DEV_register_iowrite_handler(this, write_handler, 0x037A, "Parallel Port 1", 1); + + BX_PAR_THIS s.STATUS.error = 1; + BX_PAR_THIS s.STATUS.slct = 1; + BX_PAR_THIS s.STATUS.pe = 0; + BX_PAR_THIS s.STATUS.ack = 1; + BX_PAR_THIS s.STATUS.busy = 1; + + BX_PAR_THIS s.CONTROL.strobe = 0; + BX_PAR_THIS s.CONTROL.autofeed = 0; + BX_PAR_THIS s.CONTROL.init = 1; + BX_PAR_THIS s.CONTROL.slct_in = 1; + BX_PAR_THIS s.CONTROL.irq = 0; + BX_PAR_THIS s.CONTROL.input = 0; + + BX_PAR_THIS s.initmode = 0; + + if (strlen(bx_options.par[0].Ooutfile->getptr ()) > 0) { + s.output = fopen(bx_options.par[0].Ooutfile->getptr (), "wb"); + if (!s.output) + BX_PANIC (("Could not open '%s' to write parport1 output", + bx_options.par[0].Ooutfile->getptr ())); + } + } +} + + void +bx_parallel_c::reset(unsigned type) +{ +} + + void +bx_parallel_c::virtual_printer(void) +{ + if (BX_PAR_THIS s.STATUS.slct) { + if (BX_PAR_THIS s.output != NULL) { + fputc(BX_PAR_THIS s.data, BX_PAR_THIS s.output); + fflush (BX_PAR_THIS s.output); + } + if (BX_PAR_THIS s.CONTROL.irq == 1) { + DEV_pic_raise_irq(7); + } + BX_PAR_THIS s.STATUS.ack = 0; + BX_PAR_THIS s.STATUS.busy = 1; + } + else { + BX_ERROR(("data is valid, but printer is offline")); + } +} + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_parallel_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_PAR_SMF + bx_parallel_c *class_ptr = (bx_parallel_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + Bit32u +bx_parallel_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PAR_SMF + + Bit32u retval; + + switch (address) { + /* PARALLEL PORT 1 */ + case 0x0378: + if (!BX_PAR_THIS s.CONTROL.input) { + return (Bit32u)BX_PAR_THIS s.data; + } else { + BX_ERROR(("read: input mode not supported")); + return (0xFF); + } + break; + case 0x0379: + { + retval = ((BX_PAR_THIS s.STATUS.busy << 7) | + (BX_PAR_THIS s.STATUS.ack << 6) | + (BX_PAR_THIS s.STATUS.pe << 5) | + (BX_PAR_THIS s.STATUS.slct << 4) | + (BX_PAR_THIS s.STATUS.error << 3)); + if (BX_PAR_THIS s.STATUS.ack == 0) { + BX_PAR_THIS s.STATUS.ack = 1; + if (BX_PAR_THIS s.CONTROL.irq == 1) { + DEV_pic_lower_irq(7); + } + } + if (BX_PAR_THIS s.initmode == 1) { + BX_PAR_THIS s.STATUS.busy = 1; + BX_PAR_THIS s.STATUS.slct = 1; + BX_PAR_THIS s.STATUS.ack = 0; + if (BX_PAR_THIS s.CONTROL.irq == 1) { + DEV_pic_raise_irq(7); + } + BX_PAR_THIS s.initmode = 0; + } + BX_DEBUG(("read: status register returns 0x%02x", retval)); + return retval; + } + break; + case 0x037A: + { + retval = ((BX_PAR_THIS s.CONTROL.input << 5) | + (BX_PAR_THIS s.CONTROL.irq << 4) | + (BX_PAR_THIS s.CONTROL.slct_in << 3) | + (BX_PAR_THIS s.CONTROL.init << 2) | + (BX_PAR_THIS s.CONTROL.autofeed << 1) | + (BX_PAR_THIS s.CONTROL.strobe)); + BX_DEBUG(("read: control register returns 0x%02x", retval)); + return retval; + } + break; + } + return(0); +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_parallel_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_PAR_SMF + bx_parallel_c *class_ptr = (bx_parallel_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + void +bx_parallel_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PAR_SMF + + switch (address) { + /* PARALLEL PORT 1 */ + case 0x0378: + BX_PAR_THIS s.data = (Bit8u)value; + BX_DEBUG(("write: data output register = 0x%02x", (Bit8u)value)); + break; + case 0x037A: + { + if ((value & 0x01) == 0x01) { + if (BX_PAR_THIS s.CONTROL.strobe == 0) { + BX_PAR_THIS s.CONTROL.strobe = 1; + virtual_printer(); // data is valid now + } + } else { + if (BX_PAR_THIS s.CONTROL.strobe == 1) { + BX_PAR_THIS s.CONTROL.strobe = 0; + } + } + BX_PAR_THIS s.CONTROL.autofeed = ((value & 0x02) == 0x02); + if ((value & 0x04) == 0x04) { + if (BX_PAR_THIS s.CONTROL.init == 0) { + BX_PAR_THIS s.CONTROL.init = 1; + BX_PAR_THIS s.STATUS.busy = 0; + BX_PAR_THIS s.STATUS.slct = 0; + BX_PAR_THIS s.initmode = 1; + BX_DEBUG(("printer init requested")); + } + } else { + if (BX_PAR_THIS s.CONTROL.init == 1) { + BX_PAR_THIS s.CONTROL.init = 0; + } + } + if ((value & 0x08) == 0x08) { + if (BX_PAR_THIS s.CONTROL.slct_in == 0) { + BX_PAR_THIS s.CONTROL.slct_in = 1; + BX_DEBUG(("printer now online")); + } + } else { + if (BX_PAR_THIS s.CONTROL.slct_in == 1) { + BX_PAR_THIS s.CONTROL.slct_in = 0; + BX_DEBUG(("printer now offline")); + } + } + BX_PAR_THIS s.STATUS.slct = BX_PAR_THIS s.CONTROL.slct_in; + if ((value & 0x10) == 0x10) { + if (BX_PAR_THIS s.CONTROL.irq == 0) { + BX_PAR_THIS s.CONTROL.irq = 1; + BX_DEBUG(("irq mode selected")); + } + } else { + if (BX_PAR_THIS s.CONTROL.irq == 1) { + BX_PAR_THIS s.CONTROL.irq = 0; + BX_DEBUG(("polling mode selected")); + } + } + if ((value & 0x20) == 0x20) { + if (BX_PAR_THIS s.CONTROL.input == 0) { + BX_PAR_THIS s.CONTROL.input = 1; + BX_DEBUG(("data input mode selected")); + } + } else { + if (BX_PAR_THIS s.CONTROL.input == 1) { + BX_PAR_THIS s.CONTROL.input = 0; + BX_DEBUG(("data output mode selected")); + } + } + if ((value & 0xC0) > 0) { + BX_ERROR(("write: unsupported control bit ignored")); + } + } + break; + } +} diff --git a/tools/ioemu/iodev/parallel.h b/tools/ioemu/iodev/parallel.h new file mode 100644 index 0000000000..b4a256a616 --- /dev/null +++ b/tools/ioemu/iodev/parallel.h @@ -0,0 +1,78 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: parallel.h,v 1.11 2002/10/25 11:44:40 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 + + +#if BX_USE_PAR_SMF +# define BX_PAR_SMF static +# define BX_PAR_THIS theParallelDevice-> +#else +# define BX_PAR_SMF +# define BX_PAR_THIS this-> +#endif + +typedef struct { + Bit8u data; + struct { + bx_bool error; + bx_bool slct; + bx_bool pe; + bx_bool ack; + bx_bool busy; + } STATUS; + struct { + bx_bool strobe; + bx_bool autofeed; + bx_bool init; + bx_bool slct_in; + bx_bool irq; + bx_bool input; + } CONTROL; + FILE *output; + bx_bool initmode; +} bx_par_t; + + + +class bx_parallel_c : public bx_devmodel_c { +public: + + bx_parallel_c(void); + ~bx_parallel_c(void); + virtual void init(void); + virtual void reset(unsigned type); + +private: + bx_par_t s; + + static void virtual_printer(); + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#if !BX_USE_PAR_SMF + Bit32u read(Bit32u address, unsigned io_len); + void write(Bit32u address, Bit32u value, unsigned io_len); +#endif + }; diff --git a/tools/ioemu/iodev/pc_system.cc b/tools/ioemu/iodev/pc_system.cc new file mode 100644 index 0000000000..de34d9914b --- /dev/null +++ b/tools/ioemu/iodev/pc_system.cc @@ -0,0 +1,570 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pc_system.cc,v 1.34 2003/06/07 19:16:51 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + + +#include "bochs.h" +#define LOG_THIS bx_pc_system. + +#ifdef WIN32 +#ifndef __MINGW32__ +// #include <winsock2.h> // +++ +#include <winsock.h> +#endif +#endif + +#if BX_SHOW_IPS +unsigned long ips_count=0; +#endif + +#if defined(PROVIDE_M_IPS) +double m_ips; // Millions of Instructions Per Second +#endif + +#ifdef BX_USE_VMX +unsigned int tsc_per_bx_tick; +#endif + +// Option for turning off BX_TIMER_DEBUG? +// Check out m_ips and ips + +#define SpewPeriodicTimerInfo 0 +#define MinAllowableTimerPeriod 1 + + +#if SpewPeriodicTimerInfo +// If debugging, set the heartbeat to 5M cycles. Each heartbeat +// spews the active timer info. +const Bit64u bx_pc_system_c::NullTimerInterval = 5000000; +#else +// This must be the maximum 32-bit unsigned int value, NOT (Bit64u) -1. +const Bit64u bx_pc_system_c::NullTimerInterval = 0xffffffff; +#endif + + // constructor +bx_pc_system_c::bx_pc_system_c(void) +{ + this->put("SYS"); + + // Timer[0] is the null timer. It is initialized as a special + // case here. It should never be turned off or modified, and its + // duration should always remain the same. + ticksTotal = 0; // Reset ticks since emulator started. + timer[0].period = NullTimerInterval; + timer[0].timeToFire = ticksTotal + NullTimerInterval; + timer[0].active = 1; + timer[0].continuous = 1; + timer[0].funct = nullTimer; + timer[0].this_ptr = this; + currCountdown = NullTimerInterval; + currCountdownPeriod = NullTimerInterval; + numTimers = 1; // So far, only the nullTimer. + lastTimeUsec = 0; + usecSinceLast = 0; +} + + void +bx_pc_system_c::init_ips(Bit32u ips) +{ + HRQ = 0; + + enable_a20 = 1; + //set_INTR (0); + +#if BX_CPU_LEVEL < 2 + a20_mask = 0xfffff; +#elif BX_CPU_LEVEL == 2 + a20_mask = 0xffffff; +#else /* 386+ */ + a20_mask = 0xffffffff; +#endif + +#ifdef BX_USE_VMX + Bit64u phy_cpu_freq = cpu_calibrate_ticks(); + + if (ips == 500000) { //default ips: we use fixed scaling factor to calulate ips + tsc_per_bx_tick = 2000; + ips = phy_cpu_freq / tsc_per_bx_tick; + } else //use uesr defined ips to calulate factor + tsc_per_bx_tick = ((phy_cpu_freq + (ips>>1)) / ips); +#endif + + // parameter 'ips' is the processor speed in Instructions-Per-Second + m_ips = double(ips) / 1000000.0L; + + BX_DEBUG(("ips = %u", (unsigned) ips)); +} + + void +bx_pc_system_c::set_HRQ(bx_bool val) +{ + HRQ = val; + if (val) + BX_CPU(0)->async_event = 1; +} + + +#if (BX_NUM_SIMULATORS < 2) + void +bx_pc_system_c::set_INTR(bx_bool value) +{ + if (bx_dbg.interrupts) + BX_INFO(("pc_system: Setting INTR=%d on bootstrap processor %d", (int)value, BX_BOOTSTRAP_PROCESSOR)); + //INTR = value; + BX_CPU(BX_BOOTSTRAP_PROCESSOR)->set_INTR(value); +} +#endif + +// +// Read from the IO memory address space +// + + Bit32u BX_CPP_AttrRegparmN(2) +bx_pc_system_c::inp(Bit16u addr, unsigned io_len) +{ + Bit32u ret; + + ret = bx_devices.inp(addr, io_len); + + return( ret ); +} + + +// +// Write to the IO memory address space. +// + + void BX_CPP_AttrRegparmN(3) +bx_pc_system_c::outp(Bit16u addr, Bit32u value, unsigned io_len) +{ + bx_devices.outp(addr, value, io_len); +} + + void BX_CPP_AttrRegparmN(1) +bx_pc_system_c::set_enable_a20(Bit8u value) +{ +#if BX_CPU_LEVEL < 2 + BX_PANIC(("set_enable_a20() called: 8086 emulation")); +#else + +#if BX_SUPPORT_A20 + unsigned old_enable_a20 = enable_a20; + + if (value) { + enable_a20 = 1; +#if BX_CPU_LEVEL == 2 + a20_mask = 0xffffff; /* 286: enable all 24 address lines */ +#else /* 386+ */ + a20_mask = 0xffffffff; /* 386: enable all 32 address lines */ +#endif + } + else { + enable_a20 = 0; + a20_mask = 0xffefffff; /* mask off A20 address line */ + } + + BX_DBG_A20_REPORT(value); + + BX_DEBUG(("A20: set() = %u", (unsigned) enable_a20)); + + // If there has been a transition, we need to notify the CPUs so + // they can potentially invalidate certain cache info based on + // A20-line-applied physical addresses. + if (old_enable_a20 != enable_a20) { + for (unsigned i=0; i<BX_SMP_PROCESSORS; i++) + BX_CPU(i)->pagingA20Changed(); + } +#else + BX_DEBUG(("set_enable_a20: ignoring: SUPPORT_A20 = 0")); +#endif // #if BX_SUPPORT_A20 + +#endif +} + + bx_bool +bx_pc_system_c::get_enable_a20(void) +{ +#if BX_SUPPORT_A20 + if (bx_dbg.a20) + BX_INFO(("A20: get() = %u", (unsigned) enable_a20)); + + if (enable_a20) return(1); + else return(0); +#else + BX_INFO(("get_enable_a20: ignoring: SUPPORT_A20 = 0")); + return(1); +#endif // #if BX_SUPPORT_A20 +} + + int +bx_pc_system_c::ResetSignal( PCS_OP operation ) +{ + UNUSED( operation ); + // Reset the processor. + + BX_ERROR(( "# bx_pc_system_c::ResetSignal() called" )); + for (int i=0; i<BX_SMP_PROCESSORS; i++) + BX_CPU(i)->reset(BX_RESET_SOFTWARE); + DEV_reset_devices(BX_RESET_SOFTWARE); + return(0); +} + + + Bit8u +bx_pc_system_c::IAC(void) +{ + return( DEV_pic_iac() ); +} + + void +bx_pc_system_c::exit(void) +{ + if (DEV_hd_present()) + DEV_hd_close_harddrive(); + + BX_INFO(("Last time is %u", (unsigned) DEV_cmos_get_timeval())); + + if (bx_gui) bx_gui->exit(); +} + + +// ================================================ +// Bochs internal timer delivery framework features +// ================================================ + + int +bx_pc_system_c::register_timer( void *this_ptr, void (*funct)(void *), + Bit32u useconds, bx_bool continuous, bx_bool active, const char *id) +{ + Bit64u ticks; + + // Convert useconds to number of ticks. + ticks = (Bit64u) (double(useconds) * m_ips); + + return register_timer_ticks(this_ptr, funct, ticks, continuous, active, id); +} + + int +bx_pc_system_c::register_timer_ticks(void* this_ptr, bx_timer_handler_t funct, + Bit64u ticks, bx_bool continuous, bx_bool active, const char *id) +{ + unsigned i; + +#if BX_TIMER_DEBUG + if (numTimers >= BX_MAX_TIMERS) { + BX_PANIC(("register_timer: too many registered timers.")); + } + if (this_ptr == NULL) + BX_PANIC(("register_timer_ticks: this_ptr is NULL")); + if (funct == NULL) + BX_PANIC(("register_timer_ticks: funct is NULL")); +#endif + + // If the timer frequency is rediculously low, make it more sane. + // This happens when 'ips' is too low. + if (ticks < MinAllowableTimerPeriod) { + //BX_INFO(("register_timer_ticks: adjusting ticks of %llu to min of %u", + // ticks, MinAllowableTimerPeriod)); + ticks = MinAllowableTimerPeriod; + } + + for (i=0; i < numTimers; i++) { + if (timer[i].inUse == 0) + break; + } + + timer[i].inUse = 1; + timer[i].period = ticks; + timer[i].timeToFire = (ticksTotal + Bit64u(currCountdownPeriod-currCountdown)) + + ticks; + timer[i].active = active; + timer[i].continuous = continuous; + timer[i].funct = funct; + timer[i].this_ptr = this_ptr; + strncpy(timer[i].id, id, BxMaxTimerIDLen); + timer[i].id[BxMaxTimerIDLen-1] = 0; // Null terminate if not already. + + if (active) { + if (ticks < Bit64u(currCountdown)) { + // This new timer needs to fire before the current countdown. + // Skew the current countdown and countdown period to be smaller + // by the delta. + currCountdownPeriod -= (currCountdown - Bit32u(ticks)); + currCountdown = Bit32u(ticks); + } + } + + // If we didn't find a free slot, increment the bound, numTimers. + if (i==numTimers) + numTimers++; // One new timer installed. + + // Return timer id. + return(i); +} + + + void +bx_pc_system_c::countdownEvent(void) +{ + unsigned i; + Bit64u minTimeToFire; + bx_bool triggered[BX_MAX_TIMERS]; + + // The countdown decremented to 0. We need to service all the active + // timers, and invoke callbacks from those timers which have fired. +#if BX_TIMER_DEBUG + if (currCountdown != 0) + BX_PANIC(("countdownEvent: ticks!=0")); +#endif + + // Increment global ticks counter by number of ticks which have + // elapsed since the last update. + ticksTotal += Bit64u(currCountdownPeriod); + minTimeToFire = (Bit64u) -1; + + for (i=0; i < numTimers; i++) { + triggered[i] = 0; // Reset triggered flag. + if (timer[i].active) { +#if BX_TIMER_DEBUG + if (ticksTotal > timer[i].timeToFire) + BX_PANIC(("countdownEvent: ticksTotal > timeToFire[%u], D " FMT_LL "u", i, + timer[i].timeToFire-ticksTotal)); +#endif + if (ticksTotal == timer[i].timeToFire) { + // This timer is ready to fire. + triggered[i] = 1; + + if (timer[i].continuous==0) { + // If triggered timer is one-shot, deactive. + timer[i].active = 0; + } + else { + // Continuous timer, increment time-to-fire by period. + timer[i].timeToFire += timer[i].period; + if (timer[i].timeToFire < minTimeToFire) + minTimeToFire = timer[i].timeToFire; + } + } + else { + // This timer is not ready to fire yet. + if (timer[i].timeToFire < minTimeToFire) + minTimeToFire = timer[i].timeToFire; + } + } + } + + // Calculate next countdown period. We need to do this before calling + // any of the callbacks, as they may call timer features, which need + // to be advanced to the next countdown cycle. + currCountdown = currCountdownPeriod = + Bit32u(minTimeToFire - ticksTotal); + + for (i=0; i < numTimers; i++) { + // Call requested timer function. It may request a different + // timer period or deactivate etc. + if (triggered[i]) { + timer[i].funct(timer[i].this_ptr); + } + } +} + + void +bx_pc_system_c::nullTimer(void* this_ptr) +{ + // This function is always inserted in timer[0]. It is sort of + // a heartbeat timer. It ensures that at least one timer is + // always active to make the timer logic more simple, and has + // a duration of less than the maximum 32-bit integer, so that + // a 32-bit size can be used for the hot countdown timer. The + // rest of the timer info can be 64-bits. This is also a good + // place for some logic to report actual emulated + // instructions-per-second (IPS) data when measured relative to + // the host computer's wall clock. + + UNUSED(this_ptr); + +#if SpewPeriodicTimerInfo + BX_INFO(("===================================")); + for (unsigned i=0; i < bx_pc_system.numTimers; i++) { + if (bx_pc_system.timer[i].active) { + BX_INFO(("BxTimer(%s): period=" FMT_LL "u, continuous=%u", + bx_pc_system.timer[i].id, bx_pc_system.timer[i].period, + bx_pc_system.timer[i].continuous)); + } + } +#endif +} + +#if BX_DEBUGGER + void +bx_pc_system_c::timebp_handler(void* this_ptr) +{ + BX_CPU(0)->break_point = BREAK_POINT_TIME; + BX_DEBUG(( "Time breakpoint triggered" )); + + if (timebp_queue_size > 1) { + Bit64s new_diff = timebp_queue[1] - bx_pc_system.time_ticks(); + bx_pc_system.activate_timer_ticks(timebp_timer, new_diff, 1); + } + timebp_queue_size--; + for (int i = 0; i < timebp_queue_size; i++) + timebp_queue[i] = timebp_queue[i+1]; +} +#endif // BX_DEBUGGER + + Bit64u +bx_pc_system_c::time_usec_sequential() { + Bit64u this_time_usec = time_usec(); + if(this_time_usec != lastTimeUsec) { + Bit64u diff_usec = this_time_usec-lastTimeUsec; + lastTimeUsec = this_time_usec; + if(diff_usec >= usecSinceLast) { + usecSinceLast = 0; + } else { + usecSinceLast -= diff_usec; + } + } + usecSinceLast++; + return (this_time_usec+usecSinceLast); +} + Bit64u +bx_pc_system_c::time_usec() { + return (Bit64u) (((double)(Bit64s)time_ticks()) / m_ips ); +} + + void +bx_pc_system_c::start_timers(void) +{ +} + + void +bx_pc_system_c::activate_timer_ticks(unsigned i, Bit64u ticks, bx_bool continuous) +{ +#if BX_TIMER_DEBUG + if (i >= numTimers) + BX_PANIC(("activate_timer_ticks: timer %u OOB", i)); + if (timer[i].period < MinAllowableTimerPeriod) + BX_PANIC(("activate_timer_ticks: timer[%u].period of " FMT_LL "u < min of %u", + i, timer[i].period, MinAllowableTimerPeriod)); +#endif + + // If the timer frequency is rediculously low, make it more sane. + // This happens when 'ips' is too low. + if (ticks < MinAllowableTimerPeriod) { + //BX_INFO(("activate_timer_ticks: adjusting ticks of %llu to min of %u", + // ticks, MinAllowableTimerPeriod)); + ticks = MinAllowableTimerPeriod; + } + + timer[i].period = ticks; + timer[i].timeToFire = (ticksTotal + Bit64u(currCountdownPeriod-currCountdown)) + + ticks; + timer[i].active = 1; + timer[i].continuous = continuous; + + if (ticks < Bit64u(currCountdown)) { + // This new timer needs to fire before the current countdown. + // Skew the current countdown and countdown period to be smaller + // by the delta. + currCountdownPeriod -= (currCountdown - Bit32u(ticks)); + currCountdown = Bit32u(ticks); + } +} + + void +bx_pc_system_c::activate_timer(unsigned i, Bit32u useconds, bx_bool continuous) +{ + Bit64u ticks; + +#if BX_TIMER_DEBUG + if (i >= numTimers) + BX_PANIC(("activate_timer: timer %u OOB", i)); +#endif + + // if useconds = 0, use default stored in period field + // else set new period from useconds + if (useconds==0) { + ticks = timer[i].period; + } + else { + // convert useconds to number of ticks + ticks = (Bit64u) (double(useconds) * m_ips); + + // If the timer frequency is rediculously low, make it more sane. + // This happens when 'ips' is too low. + if (ticks < MinAllowableTimerPeriod) { + //BX_INFO(("activate_timer: adjusting ticks of %llu to min of %u", + // ticks, MinAllowableTimerPeriod)); + ticks = MinAllowableTimerPeriod; + } + + timer[i].period = ticks; + } + + activate_timer_ticks(i, ticks, continuous); +} + + void +bx_pc_system_c::deactivate_timer( unsigned i ) +{ +#if BX_TIMER_DEBUG + if (i >= numTimers) + BX_PANIC(("deactivate_timer: timer %u OOB", i)); +#endif + + timer[i].active = 0; +} + + unsigned +bx_pc_system_c::unregisterTimer(int timerIndex) +{ + unsigned i = (unsigned) timerIndex; + +#if BX_TIMER_DEBUG + if (i >= numTimers) + BX_PANIC(("unregisterTimer: timer %u OOB", i)); + if (i == 0) + BX_PANIC(("unregisterTimer: timer 0 is the nullTimer!")); + if (timer[i].inUse == 0) + BX_PANIC(("unregisterTimer: timer %u is not in-use!", i)); +#endif + + if (timer[i].active) { + BX_PANIC(("unregisterTimer: timer '%s' is still active!", timer[i].id)); + return(0); // Fail. + } + + // Reset timer fields for good measure. + timer[i].inUse = 0; // No longer registered. + timer[i].period = BX_MAX_BIT64S; // Max value (invalid) + timer[i].timeToFire = BX_MAX_BIT64S; // Max value (invalid) + timer[i].continuous = 0; + timer[i].funct = NULL; + timer[i].this_ptr = NULL; + memset(timer[i].id, 0, BxMaxTimerIDLen); + + return(1); // OK +} diff --git a/tools/ioemu/iodev/pci.cc b/tools/ioemu/iodev/pci.cc new file mode 100644 index 0000000000..6dfe5dbbad --- /dev/null +++ b/tools/ioemu/iodev/pci.cc @@ -0,0 +1,467 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pci.cc,v 1.29 2003/07/31 19:51:42 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + +// +// i440FX Support - PMC/DBX +// + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#if BX_PCI_SUPPORT + +#define LOG_THIS thePciBridge-> + +bx_pci_c *thePciBridge = NULL; + + int +libpci_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + thePciBridge = new bx_pci_c (); + bx_devices.pluginPciBridge = thePciBridge; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, thePciBridge, BX_PLUGIN_PCI); + return(0); // Success +} + + void +libpci_LTX_plugin_fini(void) +{ +} + +bx_pci_c::bx_pci_c(void) +{ + put("PCI"); + settype(PCILOG); +} + +bx_pci_c::~bx_pci_c(void) +{ + // nothing for now + BX_DEBUG(("Exit.")); +} + + + void +bx_pci_c::init(void) +{ + // called once when bochs initializes + unsigned i; + BX_PCI_THIS num_pci_handles = 0; + + /* set unused elements to appropriate values */ + for (i=0; i < BX_MAX_PCI_DEVICES; i++) { + BX_PCI_THIS pci_handler[i].read = NULL; + BX_PCI_THIS pci_handler[i].write = NULL; + } + + for (i=0; i < 0x100; i++) { + BX_PCI_THIS pci_handler_id[i] = BX_MAX_PCI_DEVICES; // not assigned + } + + // confAddr accepts dword i/o only + DEV_register_ioread_handler(this, read_handler, 0x0CF8, "i440FX", 4); + DEV_register_iowrite_handler(this, write_handler, 0x0CF8, "i440FX", 4); + + for (i=0x0CFC; i<=0x0CFF; i++) { + DEV_register_ioread_handler(this, read_handler, i, "i440FX", 7); + } + for (i=0x0CFC; i<=0x0CFF; i++) { + DEV_register_iowrite_handler(this, write_handler, i, "i440FX", 7); + } + + DEV_register_pci_handlers(this, pci_read_handler, pci_write_handler, + BX_PCI_DEVICE(0,0), "440FX Host bridge"); + + for (i=0; i<256; i++) + BX_PCI_THIS s.i440fx.pci_conf[i] = 0x0; + // readonly registers + BX_PCI_THIS s.i440fx.pci_conf[0x00] = 0x86; + BX_PCI_THIS s.i440fx.pci_conf[0x01] = 0x80; + BX_PCI_THIS s.i440fx.pci_conf[0x02] = 0x37; + BX_PCI_THIS s.i440fx.pci_conf[0x03] = 0x12; + BX_PCI_THIS s.i440fx.pci_conf[0x0b] = 0x06; +} + + void +bx_pci_c::reset(unsigned type) +{ + BX_PCI_THIS s.i440fx.confAddr = 0; + BX_PCI_THIS s.i440fx.confData = 0; + + BX_PCI_THIS s.i440fx.pci_conf[0x04] = 0x06; + BX_PCI_THIS s.i440fx.pci_conf[0x05] = 0x00; + BX_PCI_THIS s.i440fx.pci_conf[0x06] = 0x80; + BX_PCI_THIS s.i440fx.pci_conf[0x07] = 0x02; + BX_PCI_THIS s.i440fx.pci_conf[0x0d] = 0x00; + BX_PCI_THIS s.i440fx.pci_conf[0x0f] = 0x00; + BX_PCI_THIS s.i440fx.pci_conf[0x50] = 0x00; + BX_PCI_THIS s.i440fx.pci_conf[0x51] = 0x01; + BX_PCI_THIS s.i440fx.pci_conf[0x52] = 0x00; + BX_PCI_THIS s.i440fx.pci_conf[0x53] = 0x80; + BX_PCI_THIS s.i440fx.pci_conf[0x54] = 0x00; + BX_PCI_THIS s.i440fx.pci_conf[0x55] = 0x00; + BX_PCI_THIS s.i440fx.pci_conf[0x56] = 0x00; + BX_PCI_THIS s.i440fx.pci_conf[0x57] = 0x01; + BX_PCI_THIS s.i440fx.pci_conf[0x58] = 0x10; + for (unsigned i=0x59; i<0x60; i++) + BX_PCI_THIS s.i440fx.pci_conf[i] = 0x00; +} + + + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_pci_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_PCI_SMF + bx_pci_c *class_ptr = (bx_pci_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + Bit32u +bx_pci_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PCI_SMF + + switch (address) { + case 0x0CF8: + { + return BX_PCI_THIS s.i440fx.confAddr; + } + break; + case 0x0CFC: + case 0x0CFD: + case 0x0CFE: + case 0x0CFF: + { + Bit32u handle, retval; + Bit8u devfunc, regnum; + + if ((BX_PCI_THIS s.i440fx.confAddr & 0x80FF0000) == 0x80000000) { + devfunc = (BX_PCI_THIS s.i440fx.confAddr >> 8) & 0xff; + regnum = (BX_PCI_THIS s.i440fx.confAddr & 0xfc) + (address & 0x03); + handle = BX_PCI_THIS pci_handler_id[devfunc]; + if ((io_len <= 4) && (handle < BX_MAX_PCI_DEVICES)) + retval = (* BX_PCI_THIS pci_handler[handle].read) + (BX_PCI_THIS pci_handler[handle].this_ptr, regnum, io_len); + else + retval = 0xFFFFFFFF; + } + else + retval = 0xFFFFFFFF; + BX_PCI_THIS s.i440fx.confData = retval; + return retval; + } + } + + BX_PANIC(("unsupported IO read to port 0x%x", + (unsigned) address)); + return(0xffffffff); +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_pci_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_PCI_SMF + bx_pci_c *class_ptr = (bx_pci_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + void +bx_pci_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PCI_SMF + + switch (address) { + case 0xCF8: + { + BX_PCI_THIS s.i440fx.confAddr = value; + if ((value & 0x80FFFF00) == 0x80000000) { + BX_DEBUG(("440FX PMC register 0x%02x selected", value & 0xfc)); + } else if ((value & 0x80000000) == 0x80000000) { + BX_DEBUG(("440FX request for bus 0x%02x device 0x%02x function 0x%02x", + (value >> 16) & 0xFF, (value >> 11) & 0x1F, (value >> 8) & 0x07)); + } + } + break; + + case 0xCFC: + case 0xCFD: + case 0xCFE: + case 0xCFF: + { + Bit32u handle; + Bit8u devfunc, regnum; + + if ((BX_PCI_THIS s.i440fx.confAddr & 0x80FF0000) == 0x80000000) { + devfunc = (BX_PCI_THIS s.i440fx.confAddr >> 8) & 0xff; + regnum = (BX_PCI_THIS s.i440fx.confAddr & 0xfc) + (address & 0x03); + handle = BX_PCI_THIS pci_handler_id[devfunc]; + if ((io_len <= 4) && (handle < BX_MAX_PCI_DEVICES)) { + if (((regnum>=4) && (regnum<=7)) || (regnum==12) || (regnum==13) || (regnum>14)) { + (* BX_PCI_THIS pci_handler[handle].write) + (BX_PCI_THIS pci_handler[handle].this_ptr, regnum, value, io_len); + BX_PCI_THIS s.i440fx.confData = value << (8 * (address & 0x03)); + } + else + BX_DEBUG(("read only register, write ignored")); + } + } + } + break; + + default: + BX_PANIC(("IO write to port 0x%x", (unsigned) address)); + } +} + + + // static pci configuration space read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_pci_c::pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len) +{ +#if !BX_USE_PCI_SMF + bx_pci_c *class_ptr = (bx_pci_c *) this_ptr; + + return( class_ptr->pci_read(address, io_len) ); +} + + + Bit32u +bx_pci_c::pci_read(Bit8u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PCI_SMF + + Bit32u val440fx = 0; + + if (io_len <= 4) { + for (unsigned i=0; i<io_len; i++) { + val440fx |= (BX_PCI_THIS s.i440fx.pci_conf[address+i] << (i*8)); + } + BX_DEBUG(("440FX PMC read register 0x%02x value 0x%08x", address, val440fx)); + return val440fx; + } + else + return(0xffffffff); +} + + + // static pci configuration space write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_pci_c::pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_PCI_SMF + bx_pci_c *class_ptr = (bx_pci_c *) this_ptr; + + class_ptr->pci_write(address, value, io_len); +} + + void +bx_pci_c::pci_write(Bit8u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PCI_SMF + + Bit8u value8; + + if (io_len <= 4) { + for (unsigned i=0; i<io_len; i++) { + value8 = (value >> (i*8)) & 0xFF; + switch (address+i) { + case 0x06: + case 0x0c: + break; + default: + BX_PCI_THIS s.i440fx.pci_conf[address+i] = value8; + BX_DEBUG(("440FX PMC write register 0x%02x value 0x%02x", address, + value8)); + } + } + } +} + + + Bit8u +bx_pci_c::rd_memType (Bit32u addr) +{ + switch ((addr & 0xFC000) >> 12) { + case 0xC0: + return (BX_PCI_THIS s.i440fx.pci_conf[0x5A] & 0x1); + case 0xC4: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5A] >> 4) & 0x1); + case 0xC8: + return (BX_PCI_THIS s.i440fx.pci_conf[0x5B] & 0x1); + case 0xCC: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5B] >> 4) & 0x1); + + + case 0xD0: + return (BX_PCI_THIS s.i440fx.pci_conf[0x5C] & 0x1); + case 0xD4: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5C] >> 4) & 0x1); + case 0xD8: + return (BX_PCI_THIS s.i440fx.pci_conf[0x5D] & 0x1); + case 0xDC: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5D] >> 4) & 0x1); + + case 0xE0: + return (BX_PCI_THIS s.i440fx.pci_conf[0x5E] & 0x1); + case 0xE4: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5E] >> 4) & 0x1); + case 0xE8: + return (BX_PCI_THIS s.i440fx.pci_conf[0x5F] & 0x1); + case 0xEC: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5F] >> 4) & 0x1); + + case 0xF0: + case 0xF4: + case 0xF8: + case 0xFC: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x59] >> 4) & 0x1); + + default: + BX_PANIC(("rd_memType () Error: Memory Type not known !")); + return(0); // keep compiler happy + break; + } + +} + + Bit8u +bx_pci_c::wr_memType (Bit32u addr) +{ + switch ((addr & 0xFC000) >> 12) { + case 0xC0: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5A] >> 1) & 0x1); + case 0xC4: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5A] >> 5) & 0x1); + case 0xC8: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5B] >> 1) & 0x1); + case 0xCC: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5B] >> 5) & 0x1); + + + case 0xD0: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5C] >> 1) & 0x1); + case 0xD4: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5C] >> 5) & 0x1); + case 0xD8: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5D] >> 1) & 0x1); + case 0xDC: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5D] >> 5) & 0x1); + + case 0xE0: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5E] >> 1) & 0x1); + case 0xE4: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5E] >> 5) & 0x1); + case 0xE8: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5F] >> 1) & 0x1); + case 0xEC: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5F] >> 5) & 0x1); + + case 0xF0: + case 0xF4: + case 0xF8: + case 0xFC: + return ( (BX_PCI_THIS s.i440fx.pci_conf[0x59] >> 5) & 0x1); + + default: + BX_PANIC(("wr_memType () Error: Memory Type not known !")); + return(0); // keep compiler happy + break; + } +} + + void +bx_pci_c::print_i440fx_state() +{ + int i; + + BX_DEBUG(( "i440fxConfAddr:0x%08x", BX_PCI_THIS s.i440fx.confAddr )); + BX_DEBUG(( "i440fxConfData:0x%08x", BX_PCI_THIS s.i440fx.confData )); + +#ifdef DUMP_FULL_I440FX + for (i=0; i<256; i++) { + BX_DEBUG(( "i440fxArray%02x:0x%02x", i, BX_PCI_THIS s.i440fx.pci_conf[i] )); + } +#else /* DUMP_FULL_I440FX */ + for (i=0x59; i<0x60; i++) { + BX_DEBUG(( "i440fxArray%02x:0x%02x", i, BX_PCI_THIS s.i440fx.pci_conf[i] )); + } +#endif /* DUMP_FULL_I440FX */ +} + + bx_bool +bx_pci_c::register_pci_handlers( void *this_ptr, bx_pci_read_handler_t f1, + bx_pci_write_handler_t f2, Bit8u devfunc, + const char *name) +{ + unsigned handle; + + /* first check if device/function is available */ + if (BX_PCI_THIS pci_handler_id[devfunc] == BX_MAX_PCI_DEVICES) { + if (BX_PCI_THIS num_pci_handles >= BX_MAX_PCI_DEVICES) { + BX_INFO(("too many PCI devices installed.")); + BX_PANIC((" try increasing BX_MAX_PCI_DEVICES")); + return false; + } + handle = BX_PCI_THIS num_pci_handles++; + BX_PCI_THIS pci_handler[handle].read = f1; + BX_PCI_THIS pci_handler[handle].write = f2; + BX_PCI_THIS pci_handler[handle].this_ptr = this_ptr; + BX_PCI_THIS pci_handler_id[devfunc] = handle; + BX_INFO(("%s present at device %d, function %d", name, devfunc >> 3, + devfunc & 0x07)); + return true; // device/function mapped successfully + } + else { + return false; // device/function not available, return false. + } +} +#endif /* BX_PCI_SUPPORT */ diff --git a/tools/ioemu/iodev/pci.h b/tools/ioemu/iodev/pci.h new file mode 100644 index 0000000000..92e7beef4f --- /dev/null +++ b/tools/ioemu/iodev/pci.h @@ -0,0 +1,90 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pci.h,v 1.14 2003/01/23 19:31:27 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 BX_MAX_PCI_DEVICES 20 + +#define BX_PCI_DEVICE(device, function) ((device)<<3 | (function)) + +typedef Bit32u (*bx_pci_read_handler_t)(void *, Bit8u, unsigned); +typedef void (*bx_pci_write_handler_t)(void *, Bit8u, Bit32u, unsigned); + +#if BX_USE_PCI_SMF +# define BX_PCI_SMF static +# define BX_PCI_THIS thePciBridge-> +#else +# define BX_PCI_SMF +# define BX_PCI_THIS this-> +#endif + + +typedef struct { + Bit32u confAddr; + Bit32u confData; + Bit8u pci_conf[256]; + } bx_def440fx_t; + + + +class bx_pci_c : public bx_pci_stub_c { + +public: + bx_pci_c(void); + ~bx_pci_c(void); + virtual void init(void); + virtual void reset(unsigned type); + virtual bx_bool register_pci_handlers(void *this_ptr, + bx_pci_read_handler_t f1, + bx_pci_write_handler_t f2, + Bit8u devfunc, const char *name); + virtual void print_i440fx_state(void); + virtual Bit8u rd_memType (Bit32u addr); + virtual Bit8u wr_memType (Bit32u addr); + +private: + Bit8u pci_handler_id[0x100]; // 256 devices/functions + struct { + bx_pci_read_handler_t read; + bx_pci_write_handler_t write; + void *this_ptr; + } pci_handler[BX_MAX_PCI_DEVICES]; + unsigned num_pci_handles; + + struct { + bx_def440fx_t i440fx; + } s; + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); + static Bit32u pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len); + static void pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len); +#if !BX_USE_PCI_SMF + Bit32u read(Bit32u address, unsigned io_len); + void write(Bit32u address, Bit32u value, unsigned io_len); + Bit32u pci_read(Bit8u address, unsigned io_len); + void pci_write(Bit8u address, Bit32u value, unsigned io_len); +#endif + }; diff --git a/tools/ioemu/iodev/pci2isa.cc b/tools/ioemu/iodev/pci2isa.cc new file mode 100644 index 0000000000..54c3bc441a --- /dev/null +++ b/tools/ioemu/iodev/pci2isa.cc @@ -0,0 +1,294 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pci2isa.cc,v 1.10 2003/07/31 19:51:42 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + +// +// i440FX Support - PCI-to-ISA bridge (PIIX3) +// + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#if BX_PCI_SUPPORT + +#define LOG_THIS thePci2IsaBridge-> + +bx_pci2isa_c *thePci2IsaBridge = NULL; + + int +libpci2isa_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + thePci2IsaBridge = new bx_pci2isa_c (); + bx_devices.pluginPci2IsaBridge = thePci2IsaBridge; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, thePci2IsaBridge, BX_PLUGIN_PCI2ISA); + return(0); // Success +} + + void +libpci2isa_LTX_plugin_fini(void) +{ +} + +bx_pci2isa_c::bx_pci2isa_c(void) +{ + put("P2I"); + settype(PCI2ISALOG); +} + +bx_pci2isa_c::~bx_pci2isa_c(void) +{ + // nothing for now + BX_DEBUG(("Exit.")); +} + + + void +bx_pci2isa_c::init(void) +{ + // called once when bochs initializes + + DEV_register_pci_handlers(this, pci_read_handler, pci_write_handler, + BX_PCI_DEVICE(1,0), "PIIX3 PCI-to-ISA bridge"); + + DEV_register_iowrite_handler(this, write_handler, 0x00B2, "PIIX3 PCI-to-ISA bridge", 1); + DEV_register_iowrite_handler(this, write_handler, 0x00B3, "PIIX3 PCI-to-ISA bridge", 1); + DEV_register_iowrite_handler(this, write_handler, 0x04D0, "PIIX3 PCI-to-ISA bridge", 1); + DEV_register_iowrite_handler(this, write_handler, 0x04D1, "PIIX3 PCI-to-ISA bridge", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0CF9, "PIIX3 PCI-to-ISA bridge", 1); + + DEV_register_ioread_handler(this, read_handler, 0x00B2, "PIIX3 PCI-to-ISA bridge", 1); + DEV_register_ioread_handler(this, read_handler, 0x00B3, "PIIX3 PCI-to-ISA bridge", 1); + DEV_register_ioread_handler(this, read_handler, 0x04D0, "PIIX3 PCI-to-ISA bridge", 1); + DEV_register_ioread_handler(this, read_handler, 0x04D1, "PIIX3 PCI-to-ISA bridge", 1); + DEV_register_ioread_handler(this, read_handler, 0x0CF9, "PIIX3 PCI-to-ISA bridge", 1); + + for (unsigned i=0; i<256; i++) + BX_P2I_THIS s.pci_conf[i] = 0x0; + // readonly registers + BX_P2I_THIS s.pci_conf[0x00] = 0x86; + BX_P2I_THIS s.pci_conf[0x01] = 0x80; + BX_P2I_THIS s.pci_conf[0x02] = 0x00; + BX_P2I_THIS s.pci_conf[0x03] = 0x70; + BX_P2I_THIS s.pci_conf[0x0a] = 0x01; + BX_P2I_THIS s.pci_conf[0x0b] = 0x06; + BX_P2I_THIS s.pci_conf[0x0e] = 0x80; +} + + void +bx_pci2isa_c::reset(unsigned type) +{ + BX_P2I_THIS s.pci_conf[0x04] = 0x07; + BX_P2I_THIS s.pci_conf[0x05] = 0x00; + BX_P2I_THIS s.pci_conf[0x06] = 0x00; + BX_P2I_THIS s.pci_conf[0x07] = 0x02; + BX_P2I_THIS s.pci_conf[0x4c] = 0x4d; + BX_P2I_THIS s.pci_conf[0x4e] = 0x03; + BX_P2I_THIS s.pci_conf[0x4f] = 0x00; + BX_P2I_THIS s.pci_conf[0x60] = 0x80; + BX_P2I_THIS s.pci_conf[0x69] = 0x02; + BX_P2I_THIS s.pci_conf[0x70] = 0x80; + BX_P2I_THIS s.pci_conf[0x76] = 0x0c; + BX_P2I_THIS s.pci_conf[0x77] = 0x0c; + BX_P2I_THIS s.pci_conf[0x78] = 0x02; + BX_P2I_THIS s.pci_conf[0x79] = 0x00; + BX_P2I_THIS s.pci_conf[0x80] = 0x00; + BX_P2I_THIS s.pci_conf[0x82] = 0x00; + BX_P2I_THIS s.pci_conf[0xa0] = 0x08; + BX_P2I_THIS s.pci_conf[0xa0] = 0x08; + BX_P2I_THIS s.pci_conf[0xa2] = 0x00; + BX_P2I_THIS s.pci_conf[0xa3] = 0x00; + BX_P2I_THIS s.pci_conf[0xa4] = 0x00; + BX_P2I_THIS s.pci_conf[0xa5] = 0x00; + BX_P2I_THIS s.pci_conf[0xa6] = 0x00; + BX_P2I_THIS s.pci_conf[0xa7] = 0x00; + BX_P2I_THIS s.pci_conf[0xa8] = 0x0f; + BX_P2I_THIS s.pci_conf[0xaa] = 0x00; + BX_P2I_THIS s.pci_conf[0xab] = 0x00; + BX_P2I_THIS s.pci_conf[0xac] = 0x00; + BX_P2I_THIS s.pci_conf[0xae] = 0x00; + + BX_P2I_THIS s.elcr1 = 0x00; + BX_P2I_THIS s.elcr2 = 0x00; +} + + + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_pci2isa_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_P2I_SMF + bx_pci2isa_c *class_ptr = (bx_pci2isa_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + Bit32u +bx_pci2isa_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_P2I_SMF + + switch (address) { + case 0x00b2: + BX_ERROR(("read: APM command register not supported yet")); + break; + case 0x00b3: + BX_ERROR(("read: APM status register not supported yet")); + break; + case 0x04d0: + return(BX_P2I_THIS s.elcr1); + break; + case 0x04d1: + return(BX_P2I_THIS s.elcr2); + break; + case 0x0cf9: + BX_ERROR(("read: CPU reset register not supported yet")); + break; + } + + return(0xffffffff); +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_pci2isa_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_P2I_SMF + bx_pci2isa_c *class_ptr = (bx_pci2isa_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + void +bx_pci2isa_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_P2I_SMF + + switch (address) { + case 0x00b2: + BX_ERROR(("write: APM command register not supported yet")); + break; + case 0x00b3: + BX_ERROR(("write: APM status register not supported yet")); + break; + case 0x04d0: + BX_P2I_THIS s.elcr1 = (value & 0xf8); + BX_ERROR(("write: ELCR1 changes have no effect yet")); + break; + case 0x04d1: + BX_P2I_THIS s.elcr2 = (value & 0xde); + BX_ERROR(("write: ELCR2 changes have no effect yet")); + break; + case 0x0cf9: + BX_ERROR(("write: CPU reset register not supported yet")); + break; + } +} + + + // static pci configuration space read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_pci2isa_c::pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len) +{ +#if !BX_USE_P2I_SMF + bx_pci2isa_c *class_ptr = (bx_pci2isa_c *) this_ptr; + + return( class_ptr->pci_read(address, io_len) ); +} + + + Bit32u +bx_pci2isa_c::pci_read(Bit8u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_P2I_SMF + + Bit32u value = 0; + + if (io_len <= 4) { + for (unsigned i=0; i<io_len; i++) { + value |= (BX_P2I_THIS s.pci_conf[address+i] << (i*8)); + } + BX_DEBUG(("PIIX3 PCI-to-ISA read register 0x%02x value 0x%08x", address, value)); + return value; + } + else + return(0xffffffff); +} + + + // static pci configuration space write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_pci2isa_c::pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_P2I_SMF + bx_pci2isa_c *class_ptr = (bx_pci2isa_c *) this_ptr; + + class_ptr->pci_write(address, value, io_len); +} + + void +bx_pci2isa_c::pci_write(Bit8u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_P2I_SMF + + Bit8u value8; + + if (io_len <= 4) { + for (unsigned i=0; i<io_len; i++) { + value8 = (value >> (i*8)) & 0xFF; + switch (address+i) { + case 0x06: + break; + default: + BX_P2I_THIS s.pci_conf[address+i] = value8; + BX_DEBUG(("PIIX3 PCI-to-ISA write register 0x%02x value 0x%02x", address, + value8)); + } + } + } +} + +#endif /* BX_PCI_SUPPORT */ diff --git a/tools/ioemu/iodev/pci2isa.h b/tools/ioemu/iodev/pci2isa.h new file mode 100644 index 0000000000..1517052d92 --- /dev/null +++ b/tools/ioemu/iodev/pci2isa.h @@ -0,0 +1,63 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pci2isa.h,v 1.4 2002/11/09 20:51:40 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + +#if BX_USE_P2I_SMF +# define BX_P2I_SMF static +# define BX_P2I_THIS thePci2IsaBridge-> +#else +# define BX_P2I_SMF +# define BX_P2I_THIS this-> +#endif + + +class bx_pci2isa_c : public bx_devmodel_c { + +public: + bx_pci2isa_c(void); + ~bx_pci2isa_c(void); + virtual void init(void); + virtual void reset(unsigned type); + +private: + + struct { + Bit8u pci_conf[256]; + Bit8u elcr1; + Bit8u elcr2; + } s; + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); + static Bit32u pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len); + static void pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len); +#if !BX_USE_P2I_SMF + Bit32u read(Bit32u address, unsigned io_len); + void write(Bit32u address, Bit32u value, unsigned io_len); + Bit32u pci_read(Bit8u address, unsigned io_len); + void pci_write(Bit8u address, Bit32u value, unsigned io_len); +#endif + }; diff --git a/tools/ioemu/iodev/pciusb.cc b/tools/ioemu/iodev/pciusb.cc new file mode 100644 index 0000000000..e2d4248369 --- /dev/null +++ b/tools/ioemu/iodev/pciusb.cc @@ -0,0 +1,668 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pciusb.cc,v 1.3 2003/02/06 19:09:24 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2003 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 + +// +// Experimental PCI USB adapter +// Benjamin D Lunt (fys@cybertrails.com) coded most of this usb emulation. +// I hope to add to this code to make it more functionable. +// + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#if BX_PCI_SUPPORT && BX_PCI_USB_SUPPORT + +#define LOG_THIS theUSBDevice-> + +bx_pciusb_c* theUSBDevice = NULL; + + int +libpciusb_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + theUSBDevice = new bx_pciusb_c (); + bx_devices.pluginPciUSBAdapter = theUSBDevice; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theUSBDevice, BX_PLUGIN_PCIUSB); + return 0; // Success +} + + void +libpciusb_LTX_plugin_fini(void) +{ +} + + +bx_pciusb_c::bx_pciusb_c(void) +{ + put("USB"); + settype(PCIUSBLOG); +} + +bx_pciusb_c::~bx_pciusb_c(void) +{ + // nothing for now + BX_DEBUG(("Exit.")); +} + + + void +bx_pciusb_c::init(void) +{ + // called once when bochs initializes + + if (!bx_options.usb[0].Oenabled->get()) return; + + Bit16u base_ioaddr = bx_options.usb[0].Oioaddr->get(); + Bit8u irq = bx_options.usb[0].Oirq->get(); + + DEV_register_irq(irq, "USB Hub #1"); + BX_USB_THIS hub[0].irq = irq; + + // Call our timer routine every 1mS (1,000uS) + // Continuous and active + BX_USB_THIS hub[0].timer_index = + bx_pc_system.register_timer(this, usb_timer_handler, 1000, 1,1, "usb.timer"); + + for (unsigned addr=base_ioaddr; addr<(unsigned)(base_ioaddr+0x14); addr++) { + BX_DEBUG(("register read/write: 0x%04x", addr)); + DEV_register_ioread_handler(this, read_handler, addr, "USB Hub #1", 7); + DEV_register_iowrite_handler(this, write_handler, addr, "USB Hub #1", 7); + } + BX_USB_THIS hub[0].base_ioaddr = base_ioaddr; + + DEV_register_pci_handlers(this, + pci_read_handler, + pci_write_handler, + BX_PCI_DEVICE(1,2), + "Experimental PCI USB"); + + for (unsigned i=0; i<256; i++) { + BX_USB_THIS hub[0].pci_conf[i] = 0x0; + } + + BX_INFO(("usb1 at 0x%04x-0x%04x irq %d", base_ioaddr, base_ioaddr+0x13, irq)); +} + + void +bx_pciusb_c::reset(unsigned type) +{ + unsigned i; + + static const struct reset_vals_t { + unsigned addr; + unsigned char val; + } reset_vals[] = { + { 0x00, 0x86 }, { 0x01, 0x80 }, // 0x8086 = vendor + { 0x02, 0x20 }, { 0x03, 0x70 }, // 0x7020 = device + { 0x04, 0x05 }, { 0x05, 0x00 }, // command_io + { 0x06, 0x80 }, { 0x07, 0x02 }, // status + { 0x08, 0x01 }, // revision number + { 0x09, 0x00 }, // interface + { 0x0a, 0x03 }, // class_sub USB Host Controller + { 0x0b, 0x0c }, // class_base Serial Bus Controller + { 0x0D, 0x20 }, // bus latency + { 0x0e, 0x00 }, // header_type_generic + // address space 0x20 - 0x23 + { 0x20, ((bx_options.usb[0].Oioaddr->get() & 0xE0) | 0x01) }, + { 0x21, (bx_options.usb[0].Oioaddr->get() >> 8) }, + { 0x22, 0x00 }, { 0x23, 0x00 }, + { 0x3c, bx_options.usb[0].Oirq->get() }, // IRQ + { 0x3d, 0x04 }, // INT + { 0x6a, 0x01 }, // USB clock + { 0xc1, 0x20 } // PIRQ enable + + }; + for (i = 0; i < sizeof(reset_vals) / sizeof(*reset_vals); ++i) { + BX_USB_THIS hub[0].pci_conf[reset_vals[i].addr] = reset_vals[i].val; + } + + // reset locals + BX_USB_THIS global_reset = 0; + + // Put the USB registers into their RESET state + for (i=0; i<BX_USB_CONFDEV; i++) { + BX_USB_THIS hub[i].usb_command.max_packet_size = 0; + BX_USB_THIS hub[i].usb_command.configured = 0; + BX_USB_THIS hub[i].usb_command.debug = 0; + BX_USB_THIS hub[i].usb_command.resume = 0; + BX_USB_THIS hub[i].usb_command.suspend = 1; + BX_USB_THIS hub[i].usb_command.host_reset = 0; + BX_USB_THIS hub[i].usb_command.reset = 0; + BX_USB_THIS hub[i].usb_command.schedule = 0; + BX_USB_THIS hub[i].usb_status.error_interrupt = 0; + BX_USB_THIS hub[i].usb_status.host_error = 0; + BX_USB_THIS hub[i].usb_status.host_halted = 0; + BX_USB_THIS hub[i].usb_status.interrupt = 0; + BX_USB_THIS hub[i].usb_status.pci_error = 0; + BX_USB_THIS hub[i].usb_status.resume = 0; + BX_USB_THIS hub[i].usb_enable.short_packet = 0; + BX_USB_THIS hub[i].usb_enable.on_complete = 0; + BX_USB_THIS hub[i].usb_enable.resume = 0; + BX_USB_THIS hub[i].usb_enable.timeout_crc = 0; + BX_USB_THIS hub[i].usb_frame_num.frame_num = 0x0000; + BX_USB_THIS hub[i].usb_frame_base.frame_base = 0x00000000; + BX_USB_THIS hub[i].usb_sof.sof_timing = 0x40; + for (unsigned j=0; j<USB_NUM_PORTS; j++) { + BX_USB_THIS hub[i].usb_port[j].connect_changed = 0; + BX_USB_THIS hub[i].usb_port[j].line_dminus = 0; + BX_USB_THIS hub[i].usb_port[j].line_dplus = 0; + BX_USB_THIS hub[i].usb_port[j].low_speed = 0; + BX_USB_THIS hub[i].usb_port[j].reset = 0; + BX_USB_THIS hub[i].usb_port[j].resume = 0; + BX_USB_THIS hub[i].usb_port[j].suspend = 0; + BX_USB_THIS hub[i].usb_port[j].enabled = 0; + BX_USB_THIS hub[i].usb_port[j].able_changed = 0; + BX_USB_THIS hub[i].usb_port[j].status = 0; + } + } +} + + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_pciusb_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_PCIUSB_SMF + bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + Bit32u +bx_pciusb_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PCIUSB_SMF + Bit32u val = 0x0; + Bit8u offset,port; + + BX_DEBUG(("register read from address 0x%04x - ", (unsigned) address)); + + offset = address - BX_USB_THIS hub[0].base_ioaddr; + + switch (offset) { + case 0x0C: // Start of Frame Modify + case 0x11: // port0 (high byte read) + case 0x13: // port1 (high byte read) + if (io_len != 1) + BX_PANIC(("io read from port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len)); + break; + case 0x10: // port0 + case 0x12: // port1 + if ((io_len < 1) || (io_len > 2)) + BX_PANIC(("io read from port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len)); + break; + case 0x00: // command register (16-bit) + case 0x02: // status register (16-bit) + case 0x04: // interrupt enable register (1-bit) + case 0x06: // frame number register (16-bit) + if (io_len != 2) + BX_PANIC(("io read from port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len)); + break; + case 0x08: // frame base register (32-bit) + if (io_len != 4) + BX_PANIC(("io read from port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len)); + break; + } + + switch (offset) { + case 0x00: // command register (16-bit) + val = BX_USB_THIS hub[0].usb_command.max_packet_size << 7 + | BX_USB_THIS hub[0].usb_command.configured << 6 + | BX_USB_THIS hub[0].usb_command.debug << 5 + | BX_USB_THIS hub[0].usb_command.resume << 4 + | BX_USB_THIS hub[0].usb_command.suspend << 3 + | BX_USB_THIS hub[0].usb_command.reset << 2 + | BX_USB_THIS hub[0].usb_command.host_reset << 1 + | BX_USB_THIS hub[0].usb_command.schedule; + break; + + case 0x02: // status register (16-bit) + val = BX_USB_THIS hub[0].usb_status.host_halted << 5 + | BX_USB_THIS hub[0].usb_status.host_error << 4 + | BX_USB_THIS hub[0].usb_status.pci_error << 3 + | BX_USB_THIS hub[0].usb_status.resume << 2 + | BX_USB_THIS hub[0].usb_status.error_interrupt << 1 + | BX_USB_THIS hub[0].usb_status.interrupt; + break; + + case 0x04: // interrupt enable register (16-bit) + val = BX_USB_THIS hub[0].usb_enable.short_packet << 3 + | BX_USB_THIS hub[0].usb_enable.on_complete << 2 + | BX_USB_THIS hub[0].usb_enable.resume << 1 + | BX_USB_THIS hub[0].usb_enable.timeout_crc; + break; + + case 0x06: // frame number register (16-bit) + val = BX_USB_THIS hub[0].usb_frame_num.frame_num; + break; + + case 0x08: // frame base register (32-bit) + val = BX_USB_THIS hub[0].usb_frame_base.frame_base; + break; + + case 0x0C: // start of Frame Modify register (8-bit) + val = BX_USB_THIS hub[0].usb_sof.sof_timing; + break; + + case 0x10: // port0 + case 0x12: // port1 + port = (offset & 0x0F) >> 1; + if (port < USB_NUM_PORTS) { + val = BX_USB_THIS hub[0].usb_port[port].suspend << 12 + | BX_USB_THIS hub[0].usb_port[port].reset << 9 + | BX_USB_THIS hub[0].usb_port[port].low_speed << 8 + | 1 << 7 + | BX_USB_THIS hub[0].usb_port[port].resume << 6 + | BX_USB_THIS hub[0].usb_port[port].line_dplus << 5 + | BX_USB_THIS hub[0].usb_port[port].line_dminus << 4 + | BX_USB_THIS hub[0].usb_port[port].able_changed << 3 + | BX_USB_THIS hub[0].usb_port[port].enabled << 2 + | BX_USB_THIS hub[0].usb_port[port].connect_changed << 1 + | BX_USB_THIS hub[0].usb_port[port].status; + break; + } // else fall through to default + + default: + val = 0; // keep compiler happy + BX_PANIC(("unsupported io read from address=0x%04x!", (unsigned) address)); + break; + } + + BX_DEBUG(("val = 0x%08x", (Bit32u) val)); + + return(val); +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_pciusb_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_PCIUSB_SMF + bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + void +bx_pciusb_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PCIUSB_SMF + Bit8u offset,port; + + BX_DEBUG(("register write to address 0x%04x - ", (unsigned) address)); + + offset = address - BX_USB_THIS hub[0].base_ioaddr; + + switch (offset) { + case 0x0C: // Start of Frame Modify + if (io_len != 1) + BX_PANIC(("io write to port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len)); + break; + case 0x00: // command register (16-bit) + case 0x02: // status register (16-bit) + case 0x04: // interrupt enable register (1-bit) + case 0x06: // frame number register (16-bit) + case 0x10: // port0 + case 0x12: // port1 + if (io_len != 2) + BX_PANIC(("io write to port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len)); + break; + case 0x08: // frame base register (32-bit) + if (io_len != 4) + BX_PANIC(("io write to port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len)); + break; + } + + switch (offset) { + case 0x00: // command register (16-bit) (R/W) + if (value & 0xFF00) + BX_ERROR(("write to command register with bits 15:8 not zero: 0x%04x", value)); + + BX_USB_THIS hub[0].usb_command.max_packet_size = (value & 0x80) ? 1: 0; + BX_USB_THIS hub[0].usb_command.configured = (value & 0x40) ? 1: 0; + BX_USB_THIS hub[0].usb_command.debug = (value & 0x20) ? 1: 0; + BX_USB_THIS hub[0].usb_command.resume = (value & 0x10) ? 1: 0; + BX_USB_THIS hub[0].usb_command.suspend = (value & 0x08) ? 1: 0; + BX_USB_THIS hub[0].usb_command.reset = (value & 0x04) ? 1: 0; + BX_USB_THIS hub[0].usb_command.host_reset = (value & 0x02) ? 1: 0; + BX_USB_THIS hub[0].usb_command.schedule = (value & 0x01) ? 1: 0; + + // If software set the reset bit, we need to set reset bit of each port for 10ms. + if (BX_USB_THIS hub[0].usb_command.reset) + BX_USB_THIS global_reset = 10; + + // If host_reset then reset all registers, etc. + if (BX_USB_THIS hub[0].usb_command.host_reset) + BX_USB_THIS reset(0); + + // If Run/Stop, identify in log and ignore + if (BX_USB_THIS hub[0].usb_command.schedule) + BX_INFO(("Software set Schedule bit in Command register")); + + // If Debug mode set, panic. Not implemented + if (BX_USB_THIS hub[0].usb_command.debug) + BX_PANIC(("Software set DEBUG bit in Command register. Not implemented")); + + break; + + case 0x02: // status register (16-bit) (R/WC) + if (value & 0xFFC0) + BX_ERROR(("write to status register with bits 15:6 not zero: 0x%04x", value)); + + BX_USB_THIS hub[0].usb_status.host_halted = (value & 0x20) ? 0: BX_USB_THIS hub[0].usb_status.host_halted; + BX_USB_THIS hub[0].usb_status.host_error = (value & 0x10) ? 0: BX_USB_THIS hub[0].usb_status.host_error; + BX_USB_THIS hub[0].usb_status.pci_error = (value & 0x08) ? 0: BX_USB_THIS hub[0].usb_status.pci_error; + BX_USB_THIS hub[0].usb_status.resume = (value & 0x04) ? 0: BX_USB_THIS hub[0].usb_status.resume; + BX_USB_THIS hub[0].usb_status.error_interrupt = (value & 0x02) ? 0: BX_USB_THIS hub[0].usb_status.error_interrupt; + BX_USB_THIS hub[0].usb_status.interrupt = (value & 0x01) ? 0: BX_USB_THIS hub[0].usb_status.interrupt; + break; + + case 0x04: // interrupt enable register (16-bit) + if (value & 0xFFF0) + BX_ERROR(("write to interrupt enable register with bits 15:4 not zero: 0x%04x", value)); + + BX_USB_THIS hub[0].usb_enable.short_packet = (value & 0x08) ? 1: 0; + BX_USB_THIS hub[0].usb_enable.on_complete = (value & 0x04) ? 1: 0; + BX_USB_THIS hub[0].usb_enable.resume = (value & 0x02) ? 1: 0; + BX_USB_THIS hub[0].usb_enable.timeout_crc = (value & 0x01) ? 1: 0; + + // For now, we will just ignore these being set since we never raise the IRQ + + break; + + case 0x06: // frame number register (16-bit) + if (value & 0xF800) + BX_ERROR(("write to frame number register with bits 15:11 not zero: 0x%04x", value)); + + if (BX_USB_THIS hub[0].usb_status.host_halted) + BX_USB_THIS hub[0].usb_frame_num.frame_num = value; + else + // ignored by the hardward, but lets report it anyway + BX_ERROR(("write to frame number register with STATUS.HALTED == 0")); + + break; + + case 0x08: // frame base register (32-bit) + if (value & 0xFFF) + BX_PANIC(("write to frame base register with bits 11:0 not zero: 0x%08x", value)); + + BX_USB_THIS hub[0].usb_frame_base.frame_base = value; + break; + + case 0x0C: // start of Frame Modify register (8-bit) + if (value & 0x80) + BX_ERROR(("write to SOF Modify register with bit 7 not zero: 0x%04x", value)); + + BX_USB_THIS hub[0].usb_sof.sof_timing = value; + break; + + case 0x10: // port0 + case 0x12: // port1 + port = (offset & 0x0F) >> 1; + if (port < USB_NUM_PORTS) { + if (value & ((1<<5) | (1<<4) | (1<<0))) + BX_PANIC(("write to one or more read-only bits in port%d register: 0x%04x", port, value)); + if (!(value & (1<<7))) + BX_ERROR(("write to port%d register bit 7 = 0", port)); + if (value & (1<<8)) + BX_INFO(("write to bit 8 in port%d register ignored", port)); + if (value & (1<<2)) + BX_INFO(("port%d enabled ignored. Not implemented", port)); + if ((value & (1<<12)) && BX_USB_THIS hub[0].usb_command.suspend) + BX_ERROR(("write to port%d register bit 12 when in Global-Suspend", port)); + + BX_USB_THIS hub[0].usb_port[port].suspend = (value & (1<<12)) ? 1 : 0; + BX_USB_THIS hub[0].usb_port[port].reset = (value & (1<<9)) ? 1 : 0; + BX_USB_THIS hub[0].usb_port[port].resume = (value & (1<<6)) ? 1 : 0; + BX_USB_THIS hub[0].usb_port[port].able_changed = (value & (1<<3)) ? 0 : BX_USB_THIS hub[0].usb_port[0].able_changed; + BX_USB_THIS hub[0].usb_port[port].enabled = (value & (1<<2)) ? 1 : 0; + BX_USB_THIS hub[0].usb_port[port].connect_changed = (value & (1<<1)) ? 0 : BX_USB_THIS hub[0].usb_port[0].connect_changed; + break; + } + // else fall through to default + + default: + BX_PANIC(("unsupported io write to address=0x%04x!", (unsigned) address)); + break; + } +} + +void bx_pciusb_c::usb_timer_handler(void *this_ptr) +{ + bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr; + class_ptr->usb_timer(); +} + +// Called once every 1ms +void bx_pciusb_c::usb_timer(void) +{ + int i; + + // The Frame Number Register is incremented every 1ms ????????? + // Needs more work and investigation on this. + BX_USB_THIS hub[0].usb_frame_num.frame_num++; + BX_USB_THIS hub[0].usb_frame_num.frame_num &= (1024-1); + + // If the "global reset" bit was set by software, we need + // to set the reset bit in each "active" port for 10ms + if (BX_USB_THIS global_reset) { + for (i=0; i<USB_NUM_PORTS; i++) { + BX_USB_THIS hub[0].usb_port[i].able_changed = 0; + BX_USB_THIS hub[0].usb_port[i].connect_changed = 0; + BX_USB_THIS hub[0].usb_port[i].enabled = 0; + BX_USB_THIS hub[0].usb_port[i].line_dminus = 0; + BX_USB_THIS hub[0].usb_port[i].line_dplus = 0; + BX_USB_THIS hub[0].usb_port[i].low_speed = 0; + BX_USB_THIS hub[0].usb_port[i].reset = 1; + BX_USB_THIS hub[0].usb_port[i].resume = 0; + BX_USB_THIS hub[0].usb_port[i].status = 0; + BX_USB_THIS hub[0].usb_port[i].suspend = 0; + } + BX_USB_THIS global_reset--; + } else { + for (i=0; i<USB_NUM_PORTS; i++) + BX_USB_THIS hub[0].usb_port[i].reset = 0; + } + + // If command.schedule = 0, then we need to set Status.Halted + if (!BX_USB_THIS hub[0].usb_command.schedule) + BX_USB_THIS hub[0].usb_status.host_halted = 1; + + + // TODO: + // If ins Global_Suspend mode and any of usb_port[i] bits 6,3, or 1 are set, + // we need to issue a Global_Resume (set the global resume bit). + // However, since we don't do anything, let's not. + +} + + // static pci configuration space read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_pciusb_c::pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len) +{ +#if !BX_USE_PCIUSB_SMF + bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr; + + return class_ptr->pci_read(address, io_len); +} + + + Bit32u +bx_pciusb_c::pci_read(Bit8u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PCIUSB_SMF + + Bit32u value = 0; + + if (io_len > 4 || io_len == 0) { + BX_ERROR(("Experimental USB PCI read register 0x%02x, len=%u !", + (unsigned) address, (unsigned) io_len)); + return 0xffffffff; + } + + const char* pszName = " "; + switch (address) { + case 0x00: if (io_len == 2) { + pszName = "(vendor id) "; + } else if (io_len == 4) { + pszName = "(vendor + device) "; + } + break; + case 0x04: if (io_len == 2) { + pszName = "(command) "; + } else if (io_len == 4) { + pszName = "(command+status) "; + } + break; + case 0x08: if (io_len == 1) { + pszName = "(revision id) "; + } else if (io_len == 4) { + pszName = "(rev.+class code) "; + } + break; + case 0x0c: pszName = "(cache line size) "; break; + case 0x20: pszName = "(base address) "; break; + case 0x28: pszName = "(cardbus cis) "; break; + case 0x2c: pszName = "(subsys. vendor+) "; break; + case 0x30: pszName = "(rom base) "; break; + case 0x3c: pszName = "(interrupt line+) "; break; + case 0x3d: pszName = "(interrupt pin) "; break; + } + + // This odd code is to display only what bytes actually were read. + char szTmp[9]; + char szTmp2[3]; + szTmp[0] = '\0'; + szTmp2[0] = '\0'; + for (unsigned i=0; i<io_len; i++) { + value |= (BX_USB_THIS hub[0].pci_conf[address+i] << (i*8)); + sprintf(szTmp2, "%02x", (BX_USB_THIS hub[0].pci_conf[address+i])); + strrev(szTmp2); + strcat(szTmp, szTmp2); + } + strrev(szTmp); + BX_DEBUG(("Experimental USB PCI read register 0x%02x %svalue 0x%s", + address, pszName, szTmp)); + return value; +} + + + // static pci configuration space write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_pciusb_c::pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_PCIUSB_SMF + bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr; + + class_ptr->pci_write(address, value, io_len); +} + + void +bx_pciusb_c::pci_write(Bit8u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PCIUSB_SMF + + if (io_len > 4 || io_len == 0) { + BX_ERROR(("Experimental USB PCI write register 0x%02x, len=%u !", + (unsigned) address, (unsigned) io_len)); + return; + } + + // This odd code is to display only what bytes actually were written. + char szTmp[9]; + char szTmp2[3]; + szTmp[0] = '\0'; + szTmp2[0] = '\0'; + for (unsigned i=0; i<io_len; i++) { + const Bit8u value8 = (value >> (i*8)) & 0xFF; + switch (address+i) { + case 0x20: // Base address + BX_USB_THIS hub[0].pci_conf[address+i] = (value8 & 0xe0) | 0x01; + sprintf(szTmp2, "%02x", (value8 & 0xe0) | 0x01); + break; + case 0x10: // Reserved + case 0x11: // + case 0x12: // + case 0x13: // + case 0x14: // + case 0x15: // + case 0x16: // + case 0x17: // + case 0x18: // + case 0x19: // + case 0x1a: // + case 0x1b: // + case 0x1c: // + case 0x1d: // + case 0x1e: // + case 0x1f: // + case 0x22: // Always 0 + case 0x23: // + case 0x24: // Reserved + case 0x25: // + case 0x26: // + case 0x27: // + case 0x30: // Oh, no, you're not writing to rom_base! + case 0x31: // + case 0x32: // + case 0x33: // + case 0x3d: // + case 0x05: // disallowing write to command hi-byte + case 0x06: // disallowing write to status lo-byte (is that expected?) + strcpy(szTmp2, ".."); + break; + default: + BX_USB_THIS hub[0].pci_conf[address+i] = value8; + sprintf(szTmp2, "%02x", value8); + } + strrev(szTmp2); + strcat(szTmp, szTmp2); + } + strrev(szTmp); + BX_DEBUG(("Experimental USB PCI write register 0x%02x value 0x%s", address, szTmp)); +} + +#endif // BX_PCI_SUPPORT && BX_PCI_USB_SUPPORT diff --git a/tools/ioemu/iodev/pciusb.h b/tools/ioemu/iodev/pciusb.h new file mode 100644 index 0000000000..be2532c86f --- /dev/null +++ b/tools/ioemu/iodev/pciusb.h @@ -0,0 +1,195 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pciusb.h,v 1.1 2003/01/28 16:58:10 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2003 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 + +// Benjamin D Lunt (fys@cybertrails.com) coded most of this usb emulation. +// I hope to add to this code to make it more functionable. +// + +#if BX_USE_PCIUSB_SMF +# define BX_USB_THIS theUSBDevice-> +#else +# define BX_USB_THIS this-> +#endif + +#define BX_USB_MAXDEV 1 +#define BX_USB_CONFDEV 1 /* only 1 USB hub currently */ + +#define USB_NUM_PORTS 2 /* UHCI supports 2 ports per root hub */ + +typedef struct { + + Bit16u base_ioaddr; + Bit8u irq; + int timer_index; + + // Registers + // Base + 0x00 Command register + // Base + 0x02 Status register + // Base + 0x04 Interrupt Enable register + // Base + 0x06 Frame Number register + // Base + 0x08 Frame Base Register (32-bit) + // Base + 0x0C Start of Frame Modify register + // Base + 0x0D + // Base + 0x0E + // Base + 0x0F + // Base + 0x10 Eight(?) 16-bit ports (one for each port on hub) + + // Bit reps of registers above + // Command Register + // Bits 15-8 are reserved + // Bit 7 = Maximum packet size + // Bit 6 = Host Controller has been configured (set by software) + // Bit 5 = software debug mode + // Bit 4 = force global resume + // Bit 3 = enter global suspend mode + // Bit 2 = global reset + // Bit 1 = host controller reset + // Bit 0 = run/stop schedule + struct { + bx_bool max_packet_size; //(bit 7) 0 = 32 bytes, 1 = 64 bytes + bx_bool configured; //(bit 6) + bx_bool debug; //(bit 5) + bx_bool resume; //(bit 4) + bx_bool suspend; //(bit 3) + bx_bool reset; //(bit 2) + bx_bool host_reset; //(bit 1) + bx_bool schedule; //(bit 0) 0 = Stop, 1 = Run + } usb_command; + + // Status Register + // Bits 15-6 are reserved + // Bit 5 = Host controller halted + // Bit 4 = Host controller process error + // Bit 3 = PCI Bus error + // Bit 2 = resume received + // Bit 1 = USB error interrupt + // Bit 0 = USB interrupt + struct { + bx_bool host_halted; //(bit 5) + bx_bool host_error; //(bit 4) + bx_bool pci_error; //(bit 3) + bx_bool resume; //(bit 2) + bx_bool error_interrupt; //(bit 1) + bx_bool interrupt; //(bit 0) + } usb_status; + + // Interrupt Enable Register + // Bits 15-4 are reserved + // Bit 3 = enable short packet interrupts + // Bit 2 = enable interrupt On Complete + // Bit 1 = enable resume + // Bit 0 = enable timeout/crc + struct { + bx_bool short_packet; //(bit 3) + bx_bool on_complete; //(bit 2) + bx_bool resume; //(bit 1) + bx_bool timeout_crc; //(bit 0) + } usb_enable; + + // Frame Number Register + // Bits 15-11 are reserved + // Bits 10-0 Frame List Current Index/Frame Number + struct { + Bit16u frame_num; + } usb_frame_num; + + // Frame List Base Address Register + // Bits 31-12 Base + // Bits 11-0 *must* be zeros when written to + struct { + Bit32u frame_base; + } usb_frame_base; + + // Start of Frame Modify Register + // Bit 7 reserved + // Bits 6-0 SOF timing value (default 64) + // SOF cycle time equals 11936+timing value + struct { + Bit8u sof_timing; + } usb_sof; + + // Port Register (0-1) + // Bits 15-13 are reserved + // Bit 12 suspend port + // Bit 11-10 are reserved + // Bit 9 port in reset state + // Bit 8 low-speed device is attached (read-only) + // Bit 7 reserved + // Bit 6 resume detected (read-only) + // Bit 5 line-status D+ (read-only) + // Bit 4 line-status D- (read-only) + // Bit 3 port enabled/disable status has changed + // (write 1 to this bit to clear it) + // Bit 2 port is enabled + // Bit 1 connect status has changed + // (write 1 to this bit to clear it) + // Bit 0 current connect status (read-only) + // Can only write in WORD sizes (Read in byte sizes???) + struct { + bx_bool suspend; + bx_bool reset; + bx_bool low_speed; + bx_bool resume; + bx_bool line_dplus; + bx_bool line_dminus; + bx_bool able_changed; + bx_bool enabled; + bx_bool connect_changed; + bx_bool status; + } usb_port[USB_NUM_PORTS]; + + Bit8u pci_conf[256]; + +} bx_usb_t; + + +class bx_pciusb_c : public bx_devmodel_c +{ +public: + bx_pciusb_c(void); + ~bx_pciusb_c(void); + virtual void init(void); + virtual void reset(unsigned type); + +private: + + bx_usb_t hub[BX_USB_MAXDEV]; + Bit8u global_reset; + + static void usb_timer_handler(void *); + void usb_timer(void); + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); + static Bit32u pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len); + static void pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len); +#if !BX_USE_PCIUSB_SMF + Bit32u read(Bit32u address, unsigned io_len); + void write(Bit32u address, Bit32u value, unsigned io_len); + Bit32u pci_read(Bit8u address, unsigned io_len); + void pci_write(Bit8u address, Bit32u value, unsigned io_len); +#endif +}; diff --git a/tools/ioemu/iodev/pcivga.cc b/tools/ioemu/iodev/pcivga.cc new file mode 100644 index 0000000000..4d999be74d --- /dev/null +++ b/tools/ioemu/iodev/pcivga.cc @@ -0,0 +1,248 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pcivga.cc,v 1.2 2003/01/23 19:31:28 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002,2003 Mike Nordell +// +// 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 + +// +// Experimental PCI VGA adapter +// + +// Note: This "driver" was created for the SOLE PURPOSE of getting BeOS +// to boot. It currently does NOTHING more than presenting a generic VGA +// device on the PCI bus. ALL gfx in/out-put is still handled by the VGA code. +// Furthermore, almost all of the PCI registers are currently acting like RAM. + + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#if BX_PCI_SUPPORT && BX_PCI_VGA_SUPPORT + +#define LOG_THIS thePciVgaAdapter-> + +bx_pcivga_c* thePciVgaAdapter = 0; + + int +libpcivga_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + thePciVgaAdapter = new bx_pcivga_c (); + bx_devices.pluginPciVgaAdapter = thePciVgaAdapter; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, thePciVgaAdapter, BX_PLUGIN_PCIVGA); + return 0; // Success +} + + void +libpcivga_LTX_plugin_fini(void) +{ +} + + +bx_pcivga_c::bx_pcivga_c(void) +{ + put("PCIVGA"); + settype(PCIVGALOG); +} + +bx_pcivga_c::~bx_pcivga_c(void) +{ + // nothing for now + BX_DEBUG(("Exit.")); +} + + + void +bx_pcivga_c::init(void) +{ + // called once when bochs initializes + + DEV_register_pci_handlers(this, + pci_read_handler, + pci_write_handler, + BX_PCI_DEVICE(2,0), + "Experimental PCI VGA"); + + for (unsigned i=0; i<256; i++) { + BX_PCIVGA_THIS s.pci_conf[i] = 0x0; + } + + // readonly registers + static const struct init_vals_t { + unsigned addr; + unsigned char val; + } init_vals[] = { + // Note that the values for vendor and device id are selected at random! + // There might actually be "real" values for "experimental" vendor and + // device that should be used! + { 0x00, 0x34 }, { 0x01, 0x12 }, // 0x1234 - experimental vendor + { 0x02, 0x11 }, { 0x03, 0x11 }, // 0x1111 - experimental device + { 0x0a, 0x00 }, // class_sub VGA controller + { 0x0b, 0x03 }, // class_base display + { 0x0e, 0x00 } // header_type_generic + }; + for (unsigned i = 0; i < sizeof(init_vals) / sizeof(*init_vals); ++i) { + BX_PCIVGA_THIS s.pci_conf[init_vals[i].addr] = init_vals[i].val; + } +} + + void +bx_pcivga_c::reset(unsigned type) +{ + static const struct reset_vals_t { + unsigned addr; + unsigned char val; + } reset_vals[] = { + { 0x04, 0x01 }, { 0x05, 0x00 }, // command_io + { 0x06, 0x00 }, { 0x07, 0x02 } // status_devsel_medium + }; + for (unsigned i = 0; i < sizeof(reset_vals) / sizeof(*reset_vals); ++i) { + BX_PCIVGA_THIS s.pci_conf[reset_vals[i].addr] = reset_vals[i].val; + } +} + + + // static pci configuration space read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_pcivga_c::pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len) +{ +#if !BX_USE_PCIVGA_SMF + bx_pcivga_c *class_ptr = (bx_pcivga_c *) this_ptr; + + return class_ptr->pci_read(address, io_len); +} + + + Bit32u +bx_pcivga_c::pci_read(Bit8u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PCIVGA_SMF + + Bit32u value = 0; + + if (io_len > 4 || io_len == 0) { + BX_DEBUG(("Experimental PCIVGA read register 0x%02x, len=%u !", + (unsigned) address, (unsigned) io_len)); + return 0xffffffff; + } + + const char* pszName = " "; + switch (address) { + case 0x00: if (io_len == 2) { + pszName = "(vendor id) "; + } else if (io_len == 4) { + pszName = "(vendor + device) "; + } + break; + case 0x04: if (io_len == 2) { + pszName = "(command) "; + } else if (io_len == 4) { + pszName = "(command+status) "; + } + break; + case 0x08: if (io_len == 1) { + pszName = "(revision id) "; + } else if (io_len == 4) { + pszName = "(rev.+class code) "; + } + break; + case 0x0c: pszName = "(cache line size) "; break; + case 0x28: pszName = "(cardbus cis) "; break; + case 0x2c: pszName = "(subsys. vendor+) "; break; + case 0x30: pszName = "(rom base) "; break; + case 0x3c: pszName = "(interrupt line+) "; break; + case 0x3d: pszName = "(interrupt pin) "; break; + } + + // This odd code is to display only what bytes actually were read. + char szTmp[9]; + char szTmp2[3]; + szTmp[0] = '\0'; + szTmp2[0] = '\0'; + for (unsigned i=0; i<io_len; i++) { + value |= (BX_PCIVGA_THIS s.pci_conf[address+i] << (i*8)); + + sprintf(szTmp2, "%02x", (BX_PCIVGA_THIS s.pci_conf[address+i])); + strrev(szTmp2); + strcat(szTmp, szTmp2); + } + strrev(szTmp); + BX_DEBUG(("Experimental PCIVGA read register 0x%02x %svalue 0x%s", + address, pszName, szTmp)); + return value; +} + + + // static pci configuration space write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_pcivga_c::pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_PCIVGA_SMF + bx_pcivga_c *class_ptr = (bx_pcivga_c *) this_ptr; + + class_ptr->pci_write(address, value, io_len); +} + + void +bx_pcivga_c::pci_write(Bit8u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PCIVGA_SMF + + if (io_len > 4 || io_len == 0) { + BX_DEBUG(("Experimental PCIVGA write register 0x%02x, len=%u !", + (unsigned) address, (unsigned) io_len)); + return; + } + + // This odd code is to display only what bytes actually were written. + char szTmp[9]; + char szTmp2[3]; + szTmp[0] = '\0'; + szTmp2[0] = '\0'; + for (unsigned i=0; i<io_len; i++) { + const Bit8u value8 = (value >> (i*8)) & 0xFF; + switch (address+i) { + case 0x30: // Oh, no, you're not writing to rom_base! + case 0x31: // + case 0x32: // + case 0x33: // + case 0x04: // disallowing write to command + case 0x06: // disallowing write to status lo-byte (is that expected?) + strcpy(szTmp2, ".."); + break; + default: + BX_PCIVGA_THIS s.pci_conf[address+i] = value8; + sprintf(szTmp2, "%02x", value8); + } + strrev(szTmp2); + strcat(szTmp, szTmp2); + } + strrev(szTmp); + BX_DEBUG(("Experimental PCIVGA write register 0x%02x value 0x%s", address, szTmp)); +} + +#endif // BX_PCI_SUPPORT && BX_PCI_VGA_SUPPORT diff --git a/tools/ioemu/iodev/pcivga.h b/tools/ioemu/iodev/pcivga.h new file mode 100644 index 0000000000..15bd986a09 --- /dev/null +++ b/tools/ioemu/iodev/pcivga.h @@ -0,0 +1,48 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pcivga.h,v 1.3 2003/01/27 21:11:55 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002,2003 Mike Nordell +// +// 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 + +#if BX_USE_PCIVGA_SMF +# define BX_PCIVGA_THIS thePciVgaAdapter-> +#else +# define BX_PCIVGA_THIS this-> +#endif + + +class bx_pcivga_c : public bx_devmodel_c +{ +public: + bx_pcivga_c(void); + ~bx_pcivga_c(void); + virtual void init(void); + virtual void reset(unsigned type); + +private: + + struct { + Bit8u pci_conf[256]; + } s; + + static Bit32u pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len); + static void pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len); +#if !BX_USE_PCIVGA_SMF + Bit32u pci_read(Bit8u address, unsigned io_len); + void pci_write(Bit8u address, Bit32u value, unsigned io_len); +#endif +}; diff --git a/tools/ioemu/iodev/pic.cc b/tools/ioemu/iodev/pic.cc new file mode 100644 index 0000000000..f4455508b6 --- /dev/null +++ b/tools/ioemu/iodev/pic.cc @@ -0,0 +1,887 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pic.cc,v 1.33 2003/08/05 09:19:36 akrisak Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" + +#define LOG_THIS thePic-> + + + +bx_pic_c *thePic = NULL; + + int +libpic_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + thePic = new bx_pic_c (); + bx_devices.pluginPicDevice = thePic; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, thePic, BX_PLUGIN_PIC); + return(0); // Success +} + + void +libpic_LTX_plugin_fini(void) +{ +} + + +bx_pic_c::bx_pic_c(void) +{ + put("PIC"); + settype(PICLOG); +} + +bx_pic_c::~bx_pic_c(void) +{ + // nothing for now +} + + + void +bx_pic_c::init(void) +{ + /* 8259 PIC (Programmable Interrupt Controller) */ + DEV_register_ioread_handler(this, read_handler, 0x0020, "8259 PIC", 1); + DEV_register_ioread_handler(this, read_handler, 0x0021, "8259 PIC", 1); + DEV_register_ioread_handler(this, read_handler, 0x00A0, "8259 PIC", 1); + DEV_register_ioread_handler(this, read_handler, 0x00A1, "8259 PIC", 1); + + DEV_register_iowrite_handler(this, write_handler, 0x0020, "8259 PIC", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0021, "8259 PIC", 1); + DEV_register_iowrite_handler(this, write_handler, 0x00A0, "8259 PIC", 1); + DEV_register_iowrite_handler(this, write_handler, 0x00A1, "8259 PIC", 1); + + + BX_PIC_THIS s.master_pic.single_PIC = 0; + BX_PIC_THIS s.master_pic.interrupt_offset = 0x08; /* IRQ0 = INT 0x08 */ + /* slave PIC connected to IRQ2 of master */ + BX_PIC_THIS s.master_pic.u.slave_connect_mask = 0x04; + BX_PIC_THIS s.master_pic.sfnm = 0; /* normal nested mode */ + BX_PIC_THIS s.master_pic.buffered_mode = 0; /* unbuffered mode */ + BX_PIC_THIS s.master_pic.master_slave = 0; /* no meaning, buffered_mode=0 */ + BX_PIC_THIS s.master_pic.auto_eoi = 0; /* manual EOI from CPU */ + BX_PIC_THIS s.master_pic.imr = 0xFF; /* all IRQ's initially masked */ + BX_PIC_THIS s.master_pic.isr = 0x00; /* no IRQ's in service */ + BX_PIC_THIS s.master_pic.irr = 0x00; /* no IRQ's requested */ + BX_PIC_THIS s.master_pic.read_reg_select = 0; /* IRR */ + BX_PIC_THIS s.master_pic.irq = 0; + BX_PIC_THIS s.master_pic.INT = 0; + BX_PIC_THIS s.master_pic.init.in_init = 0; + BX_PIC_THIS s.master_pic.init.requires_4 = 0; + BX_PIC_THIS s.master_pic.init.byte_expected = 0; + BX_PIC_THIS s.master_pic.special_mask = 0; + BX_PIC_THIS s.master_pic.lowest_priority = 7; + BX_PIC_THIS s.master_pic.polled = 0; + BX_PIC_THIS s.master_pic.rotate_on_autoeoi = 0; + + BX_PIC_THIS s.slave_pic.single_PIC = 0; + BX_PIC_THIS s.slave_pic.interrupt_offset = 0x70; /* IRQ8 = INT 0x70 */ + BX_PIC_THIS s.slave_pic.u.slave_id = 0x02; /* slave PIC connected to IRQ2 of master */ + BX_PIC_THIS s.slave_pic.sfnm = 0; /* normal nested mode */ + BX_PIC_THIS s.slave_pic.buffered_mode = 0; /* unbuffered mode */ + BX_PIC_THIS s.slave_pic.master_slave = 0; /* no meaning, buffered_mode=0 */ + BX_PIC_THIS s.slave_pic.auto_eoi = 0; /* manual EOI from CPU */ + BX_PIC_THIS s.slave_pic.imr = 0xFF; /* all IRQ's initially masked */ + BX_PIC_THIS s.slave_pic.isr = 0x00; /* no IRQ's in service */ + BX_PIC_THIS s.slave_pic.irr = 0x00; /* no IRQ's requested */ + BX_PIC_THIS s.slave_pic.read_reg_select = 0; /* IRR */ + BX_PIC_THIS s.slave_pic.irq = 0; + BX_PIC_THIS s.slave_pic.INT = 0; + BX_PIC_THIS s.slave_pic.init.in_init = 0; + BX_PIC_THIS s.slave_pic.init.requires_4 = 0; + BX_PIC_THIS s.slave_pic.init.byte_expected = 0; + BX_PIC_THIS s.slave_pic.special_mask = 0; + BX_PIC_THIS s.slave_pic.lowest_priority = 7; + BX_PIC_THIS s.slave_pic.polled = 0; + BX_PIC_THIS s.slave_pic.rotate_on_autoeoi = 0; + + for (unsigned i=0; i<8; i++) { /* all IRQ lines low */ + BX_PIC_THIS s.master_pic.IRQ_line[i] = 0; + BX_PIC_THIS s.slave_pic.IRQ_line[i] = 0; + } +} + + void +bx_pic_c::reset(unsigned type) +{ +} + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_pic_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_PIC_SMF + bx_pic_c *class_ptr = (bx_pic_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + + Bit32u +bx_pic_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PIC_SMF + + BX_DEBUG(("IO read from %04x", (unsigned) address)); + + /* + 8259A PIC + */ + + if((address == 0x20 || address == 0x21) && BX_PIC_THIS s.master_pic.polled) { + // In polled mode. Treat this as an interrupt acknowledge + clear_highest_interrupt(& BX_PIC_THIS s.master_pic); + BX_PIC_THIS s.master_pic.polled = 0; + service_master_pic(); + return io_len==1?BX_PIC_THIS s.master_pic.irq:(BX_PIC_THIS s.master_pic.irq)<<8|(BX_PIC_THIS s.master_pic.irq); // Return the current irq requested + } + + if((address == 0xa0 || address == 0xa1) && BX_PIC_THIS s.slave_pic.polled) { + // In polled mode. Treat this as an interrupt acknowledge + clear_highest_interrupt(& BX_PIC_THIS s.slave_pic); + BX_PIC_THIS s.slave_pic.polled = 0; + service_slave_pic(); + return io_len==1?BX_PIC_THIS s.slave_pic.irq:(BX_PIC_THIS s.slave_pic.irq)<<8|(BX_PIC_THIS s.slave_pic.irq); // Return the current irq requested + } + + + switch (address) { + case 0x20: + if (BX_PIC_THIS s.master_pic.read_reg_select) { /* ISR */ + BX_DEBUG(("read master ISR = %02x", + (unsigned) BX_PIC_THIS s.master_pic.isr)); + return(BX_PIC_THIS s.master_pic.isr); + } + else { /* IRR */ + BX_DEBUG(("read master IRR = %02x", + (unsigned) BX_PIC_THIS s.master_pic.irr)); + return(BX_PIC_THIS s.master_pic.irr); + } + break; + case 0x21: + BX_DEBUG(("read master IMR = %02x", + (unsigned) BX_PIC_THIS s.master_pic.imr)); + return(BX_PIC_THIS s.master_pic.imr); + break; + case 0xA0: + if (BX_PIC_THIS s.slave_pic.read_reg_select) { /* ISR */ + BX_DEBUG(("read slave ISR = %02x", + (unsigned) BX_PIC_THIS s.slave_pic.isr)); + return(BX_PIC_THIS s.slave_pic.isr); + } + else { /* IRR */ + BX_DEBUG(("read slave IRR = %02x", + (unsigned) BX_PIC_THIS s.slave_pic.irr)); + return(BX_PIC_THIS s.slave_pic.irr); + } + break; + case 0xA1: + BX_DEBUG(("read slave IMR = %02x", + (unsigned) BX_PIC_THIS s.slave_pic.imr)); + return(BX_PIC_THIS s.slave_pic.imr); + break; + } + + BX_PANIC(("io read to address %04x", (unsigned) address)); + return(0); /* default if not found above */ +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_pic_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_PIC_SMF + bx_pic_c *class_ptr = (bx_pic_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + void +bx_pic_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PIC_SMF + + BX_DEBUG(("IO write to %04x = %02x", (unsigned) address, (unsigned) value)); + + /* + 8259A PIC + */ + + switch (address) { + case 0x20: + if (value & 0x10) { /* initialization command 1 */ + BX_DEBUG(("master: init command 1 found")); + BX_DEBUG((" requires 4 = %u", (unsigned) (value & 0x01) )); + BX_DEBUG((" cascade mode: [0=cascade,1=single] %u", + (unsigned) ((value & 0x02) >> 1))); + BX_PIC_THIS s.master_pic.init.in_init = 1; + BX_PIC_THIS s.master_pic.init.requires_4 = (value & 0x01); + BX_PIC_THIS s.master_pic.init.byte_expected = 2; /* operation command 2 */ + BX_PIC_THIS s.master_pic.imr = 0x00; /* clear the irq mask register */ + BX_PIC_THIS s.master_pic.isr = 0x00; /* no IRQ's in service */ + BX_PIC_THIS s.master_pic.irr = 0x00; /* no IRQ's requested */ + BX_PIC_THIS s.master_pic.lowest_priority = 7; + BX_PIC_THIS s.master_pic.INT = 0; /* reprogramming clears previous INTR request */ + BX_PIC_THIS s.master_pic.auto_eoi = 0; + BX_PIC_THIS s.master_pic.rotate_on_autoeoi = 0; + if (value & 0x02) + BX_PANIC(("master: ICW1: single mode not supported")); + if (value & 0x08) { + BX_PANIC(("master: ICW1: level sensitive mode not supported")); + } + else { + BX_DEBUG(("master: ICW1: edge triggered mode selected")); + } + BX_SET_INTR(0); + return; + } + + if ( (value & 0x18) == 0x08 ) { /* OCW3 */ + Bit8u special_mask, poll, read_op; + + special_mask = (value & 0x60) >> 5; + poll = (value & 0x04) >> 2; + read_op = (value & 0x03); + if (poll) { + BX_PIC_THIS s.master_pic.polled = 1; + return; + } + if (read_op == 0x02) /* read IRR */ + BX_PIC_THIS s.master_pic.read_reg_select = 0; + else if (read_op == 0x03) /* read ISR */ + BX_PIC_THIS s.master_pic.read_reg_select = 1; + if (special_mask == 0x02) { /* cancel special mask */ + BX_PIC_THIS s.master_pic.special_mask = 0; + } + else if (special_mask == 0x03) { /* set specific mask */ + BX_PIC_THIS s.master_pic.special_mask = 1; + service_master_pic(); + } + return; + } + + /* OCW2 */ + switch (value) { + case 0x00: // Rotate in auto eoi mode clear + case 0x80: // Rotate in auto eoi mode set + BX_PIC_THIS s.master_pic.rotate_on_autoeoi = (value != 0); + break; + case 0x0A: /* select read interrupt request register */ + BX_PIC_THIS s.master_pic.read_reg_select = 0; + break; + case 0x0B: /* select read interrupt in-service register */ + BX_PIC_THIS s.master_pic.read_reg_select = 1; + break; + + case 0xA0: // Rotate on non-specific end of interrupt + case 0x20: /* end of interrupt command */ + + clear_highest_interrupt(& BX_PIC_THIS s.master_pic); + + if(value == 0xA0) {// Rotate in Auto-EOI mode + BX_PIC_THIS s.master_pic.lowest_priority ++; + if(BX_PIC_THIS s.master_pic.lowest_priority > 7) + BX_PIC_THIS s.master_pic.lowest_priority = 0; + } + + service_master_pic(); + break; + + case 0x40: // Intel PIC spec-sheet seems to indicate this should be ignored + BX_INFO(("IRQ no-op")); + break; + + case 0x60: /* specific EOI 0 */ + case 0x61: /* specific EOI 1 */ + case 0x62: /* specific EOI 2 */ + case 0x63: /* specific EOI 3 */ + case 0x64: /* specific EOI 4 */ + case 0x65: /* specific EOI 5 */ + case 0x66: /* specific EOI 6 */ + case 0x67: /* specific EOI 7 */ + BX_PIC_THIS s.master_pic.isr &= ~(1 << (value-0x60)); + service_master_pic(); + break; + + // IRQ lowest priority commands + case 0xC0: // 0 7 6 5 4 3 2 1 + case 0xC1: // 1 0 7 6 5 4 3 2 + case 0xC2: // 2 1 0 7 6 5 4 3 + case 0xC3: // 3 2 1 0 7 6 5 4 + case 0xC4: // 4 3 2 1 0 7 6 5 + case 0xC5: // 5 4 3 2 1 0 7 6 + case 0xC6: // 6 5 4 3 2 1 0 7 + case 0xC7: // 7 6 5 4 3 2 1 0 + BX_INFO(("IRQ lowest command 0x%x", value)); + BX_PIC_THIS s.master_pic.lowest_priority = value - 0xC0; + break; + + case 0xE0: // specific EOI and rotate 0 + case 0xE1: // specific EOI and rotate 1 + case 0xE2: // specific EOI and rotate 2 + case 0xE3: // specific EOI and rotate 3 + case 0xE4: // specific EOI and rotate 4 + case 0xE5: // specific EOI and rotate 5 + case 0xE6: // specific EOI and rotate 6 + case 0xE7: // specific EOI and rotate 7 + BX_PIC_THIS s.master_pic.isr &= ~(1 << (value-0xE0)); + BX_PIC_THIS s.master_pic.lowest_priority = (value - 0xE0); + service_master_pic(); + + break; + + default: + BX_PANIC(("write to port 20h = %02x", value)); + } /* switch (value) */ + break; + + case 0x21: + /* initialization mode operation */ + if (BX_PIC_THIS s.master_pic.init.in_init) { + switch (BX_PIC_THIS s.master_pic.init.byte_expected) { + case 2: + BX_PIC_THIS s.master_pic.interrupt_offset = value & 0xf8; + BX_PIC_THIS s.master_pic.init.byte_expected = 3; + BX_DEBUG(("master: init command 2 = %02x", (unsigned) value)); + BX_DEBUG((" offset = INT %02x", + BX_PIC_THIS s.master_pic.interrupt_offset)); + return; + break; + case 3: + BX_DEBUG(("master: init command 3 = %02x", (unsigned) value)); + if (BX_PIC_THIS s.master_pic.init.requires_4) { + BX_PIC_THIS s.master_pic.init.byte_expected = 4; + } + else { + BX_PIC_THIS s.master_pic.init.in_init = 0; + } + return; + break; + case 4: + BX_DEBUG(("master: init command 4 = %02x", (unsigned) value)); + if (value & 0x02) { + BX_DEBUG((" auto EOI")); + BX_PIC_THIS s.master_pic.auto_eoi = 1; + } + else { + BX_DEBUG(("normal EOI interrupt")); + BX_PIC_THIS s.master_pic.auto_eoi = 0; + } + if (value & 0x01) { + BX_DEBUG((" 80x86 mode")); + } else + BX_PANIC((" not 80x86 mode")); + BX_PIC_THIS s.master_pic.init.in_init = 0; + return; + break; + default: + BX_PANIC(("master expecting bad init command")); + } + } + + /* normal operation */ + BX_DEBUG(("setting master pic IMR to %02x", value)); + BX_PIC_THIS s.master_pic.imr = value; + service_master_pic(); + return; + break; + + case 0xA0: + if (value & 0x10) { /* initialization command 1 */ + BX_DEBUG(("slave: init command 1 found")); + BX_DEBUG((" requires 4 = %u", + (unsigned) (value & 0x01) )); + BX_DEBUG((" cascade mode: [0=cascade,1=single] %u", + (unsigned) ((value & 0x02) >> 1))); + BX_PIC_THIS s.slave_pic.init.in_init = 1; + BX_PIC_THIS s.slave_pic.init.requires_4 = (value & 0x01); + BX_PIC_THIS s.slave_pic.init.byte_expected = 2; /* operation command 2 */ + BX_PIC_THIS s.slave_pic.imr = 0x00; /* clear irq mask */ + BX_PIC_THIS s.slave_pic.isr = 0x00; /* no IRQ's in service */ + BX_PIC_THIS s.slave_pic.irr = 0x00; /* no IRQ's requested */ + BX_PIC_THIS s.slave_pic.lowest_priority = 7; + BX_PIC_THIS s.slave_pic.INT = 0; /* reprogramming clears previous INTR request */ + BX_PIC_THIS s.slave_pic.auto_eoi = 0; + BX_PIC_THIS s.slave_pic.rotate_on_autoeoi = 0; + if (value & 0x02) + BX_PANIC(("slave: ICW1: single mode not supported")); + if (value & 0x08) { + BX_PANIC(("slave: ICW1: level sensitive mode not supported")); + } + else { + BX_DEBUG(("slave: ICW1: edge triggered mode selected")); + } + return; + } + + if ( (value & 0x18) == 0x08 ) { /* OCW3 */ + Bit8u special_mask, poll, read_op; + + special_mask = (value & 0x60) >> 5; + poll = (value & 0x04) >> 2; + read_op = (value & 0x03); + if (poll) { + BX_PIC_THIS s.slave_pic.polled = 1; + return; + } + if (read_op == 0x02) /* read IRR */ + BX_PIC_THIS s.slave_pic.read_reg_select = 0; + else if (read_op == 0x03) /* read ISR */ + BX_PIC_THIS s.slave_pic.read_reg_select = 1; + if (special_mask == 0x02) { /* cancel special mask */ + BX_PIC_THIS s.slave_pic.special_mask = 0; + } + else if (special_mask == 0x03) { /* set specific mask */ + BX_PIC_THIS s.slave_pic.special_mask = 1; + service_slave_pic(); + } + return; + } + + switch (value) { + case 0x00: // Rotate in auto eoi mode clear + case 0x80: // Rotate in auto eoi mode set + BX_PIC_THIS s.slave_pic.rotate_on_autoeoi = (value != 0); + break; + + case 0x0A: /* select read interrupt request register */ + BX_PIC_THIS s.slave_pic.read_reg_select = 0; + break; + case 0x0B: /* select read interrupt in-service register */ + BX_PIC_THIS s.slave_pic.read_reg_select = 1; + break; + + case 0xA0: // Rotate on non-specific end of interrupt + case 0x20: /* end of interrupt command */ + + clear_highest_interrupt(& BX_PIC_THIS s.slave_pic); + + if(value == 0xA0) {// Rotate in Auto-EOI mode + BX_PIC_THIS s.slave_pic.lowest_priority ++; + if(BX_PIC_THIS s.slave_pic.lowest_priority > 7) + BX_PIC_THIS s.slave_pic.lowest_priority = 0; + } + + service_slave_pic(); + break; + + case 0x40: // Intel PIC spec-sheet seems to indicate this should be ignored + BX_INFO(("IRQ no-op")); + break; + + case 0x60: /* specific EOI 0 */ + case 0x61: /* specific EOI 1 */ + case 0x62: /* specific EOI 2 */ + case 0x63: /* specific EOI 3 */ + case 0x64: /* specific EOI 4 */ + case 0x65: /* specific EOI 5 */ + case 0x66: /* specific EOI 6 */ + case 0x67: /* specific EOI 7 */ + BX_PIC_THIS s.slave_pic.isr &= ~(1 << (value-0x60)); + service_slave_pic(); + break; + + // IRQ lowest priority commands + case 0xC0: // 0 7 6 5 4 3 2 1 + case 0xC1: // 1 0 7 6 5 4 3 2 + case 0xC2: // 2 1 0 7 6 5 4 3 + case 0xC3: // 3 2 1 0 7 6 5 4 + case 0xC4: // 4 3 2 1 0 7 6 5 + case 0xC5: // 5 4 3 2 1 0 7 6 + case 0xC6: // 6 5 4 3 2 1 0 7 + case 0xC7: // 7 6 5 4 3 2 1 0 + BX_INFO(("IRQ lowest command 0x%x", value)); + BX_PIC_THIS s.slave_pic.lowest_priority = value - 0xC0; + break; + + case 0xE0: // specific EOI and rotate 0 + case 0xE1: // specific EOI and rotate 1 + case 0xE2: // specific EOI and rotate 2 + case 0xE3: // specific EOI and rotate 3 + case 0xE4: // specific EOI and rotate 4 + case 0xE5: // specific EOI and rotate 5 + case 0xE6: // specific EOI and rotate 6 + case 0xE7: // specific EOI and rotate 7 + BX_PIC_THIS s.slave_pic.isr &= ~(1 << (value-0xE0)); + BX_PIC_THIS s.slave_pic.lowest_priority = (value - 0xE0); + service_slave_pic(); + + break; + + default: + BX_PANIC(("write to port A0h = %02x", value)); + } /* switch (value) */ + break; + + case 0xA1: + /* initialization mode operation */ + if (BX_PIC_THIS s.slave_pic.init.in_init) { + switch (BX_PIC_THIS s.slave_pic.init.byte_expected) { + case 2: + BX_PIC_THIS s.slave_pic.interrupt_offset = value & 0xf8; + BX_PIC_THIS s.slave_pic.init.byte_expected = 3; + BX_DEBUG(("slave: init command 2 = %02x", (unsigned) value)); + BX_DEBUG((" offset = INT %02x", + BX_PIC_THIS s.slave_pic.interrupt_offset)); + return; + break; + case 3: + BX_DEBUG(("slave: init command 3 = %02x", (unsigned) value)); + if (BX_PIC_THIS s.slave_pic.init.requires_4) { + BX_PIC_THIS s.slave_pic.init.byte_expected = 4; + } else { + BX_PIC_THIS s.slave_pic.init.in_init = 0; + } + return; + break; + case 4: + BX_DEBUG(("slave: init command 4 = %02x", (unsigned) value)); + if (value & 0x02) { + BX_DEBUG((" auto EOI")); + BX_PIC_THIS s.slave_pic.auto_eoi = 1; + } + else { + BX_DEBUG(("normal EOI interrupt")); + BX_PIC_THIS s.slave_pic.auto_eoi = 0; + } + if (value & 0x01) { + BX_DEBUG((" 80x86 mode")); + } else + BX_PANIC((" not 80x86 mode")); + BX_PIC_THIS s.slave_pic.init.in_init = 0; + return; + break; + default: + BX_PANIC(("slave: expecting bad init command")); + } + } + + /* normal operation */ + BX_DEBUG(("setting slave pic IMR to %02x", value)); + BX_PIC_THIS s.slave_pic.imr = value; + service_slave_pic(); + return; + break; + } /* switch (address) */ + + return; +} + +// new IRQ signal handling routines + + void +bx_pic_c::lower_irq(unsigned irq_no) +{ +#if BX_SUPPORT_APIC + // forward this function call to the ioapic too + if (DEV_ioapic_present()) + bx_devices.ioapic->untrigger_irq (irq_no, -1); +#endif + + if ((irq_no <= 7) && (BX_PIC_THIS s.master_pic.IRQ_line[irq_no])) { + BX_DEBUG(("IRQ line %d now low", (unsigned) irq_no)); + BX_PIC_THIS s.master_pic.IRQ_line[irq_no] = 0; + BX_PIC_THIS s.master_pic.irr &= ~(1 << irq_no); + if ((BX_PIC_THIS s.master_pic.irr & ~BX_PIC_THIS s.master_pic.imr) == 0) { + BX_SET_INTR(0); + BX_PIC_THIS s.master_pic.INT = 0; + } + } else if ((irq_no > 7) && (irq_no <= 15) && + (BX_PIC_THIS s.slave_pic.IRQ_line[irq_no-8])) { + BX_DEBUG(("IRQ line %d now low", (unsigned) irq_no)); + BX_PIC_THIS s.slave_pic.IRQ_line[irq_no - 8] = 0; + BX_PIC_THIS s.slave_pic.irr &= ~(1 << (irq_no - 8)); + if ((BX_PIC_THIS s.slave_pic.irr & ~BX_PIC_THIS s.slave_pic.imr) == 0) { + BX_PIC_THIS s.slave_pic.INT = 0; + lower_irq(2); + } + } +} + + void +bx_pic_c::raise_irq(unsigned irq_no) +{ +#if BX_SUPPORT_APIC + // forward this function call to the ioapic too + bx_devices.ioapic->trigger_irq (irq_no, -1); +#endif + + if ((irq_no <= 7) && (!BX_PIC_THIS s.master_pic.IRQ_line[irq_no])) { + BX_DEBUG(("IRQ line %d now high", (unsigned) irq_no)); + BX_PIC_THIS s.master_pic.IRQ_line[irq_no] = 1; + BX_PIC_THIS s.master_pic.irr |= (1 << irq_no); + service_master_pic(); + } else if ((irq_no > 7) && (irq_no <= 15) && + (!BX_PIC_THIS s.slave_pic.IRQ_line[irq_no-8])) { + BX_DEBUG(("IRQ line %d now high", (unsigned) irq_no)); + BX_PIC_THIS s.slave_pic.IRQ_line[irq_no - 8] = 1; + BX_PIC_THIS s.slave_pic.irr |= (1 << (irq_no - 8)); + service_slave_pic(); + } +} + +void bx_pic_c::clear_highest_interrupt(bx_pic_t *pic) +{ + int irq; + int lowest_priority; + int highest_priority; + + /* clear highest current in service bit */ + lowest_priority = pic->lowest_priority; + highest_priority = lowest_priority + 1; + if(highest_priority > 7) + highest_priority = 0; + + irq = highest_priority; + do { + if (pic->isr & (1 << irq)) { + pic->isr &= ~(1 << irq); + break; /* Return mask of bit cleared. */ + } + + irq ++; + if(irq > 7) + irq = 0; + } while(irq != highest_priority); + +} + + /* */ + void +bx_pic_c::service_master_pic(void) +{ + Bit8u unmasked_requests; + int irq; + Bit8u isr, max_irq; + Bit8u highest_priority = BX_PIC_THIS s.master_pic.lowest_priority + 1; + if(highest_priority > 7) + highest_priority = 0; + + if (BX_PIC_THIS s.master_pic.INT) { /* last interrupt still not acknowleged */ + return; + } + + if (BX_PIC_THIS s.master_pic.special_mask) { + /* all priorities may be enabled. check all IRR bits except ones + * which have corresponding ISR bits set + */ + max_irq = highest_priority; + } + else { /* normal mode */ + /* Find the highest priority IRQ that is enabled due to current ISR */ + isr = BX_PIC_THIS s.master_pic.isr; + if (isr) { + max_irq = highest_priority; + while ( (isr & (1 << max_irq)) == 0) { + max_irq++; + if(max_irq > 7) + max_irq = 0; + } + if (max_irq == highest_priority ) return; /* Highest priority interrupt in-service, + * no other priorities allowed */ + if (max_irq > 7) BX_PANIC(("error in service_master_pic()")); + } + else + max_irq = highest_priority; /* 0..7 bits in ISR are cleared */ + } + + + /* now, see if there are any higher priority requests */ + if ((unmasked_requests = (BX_PIC_THIS s.master_pic.irr & ~BX_PIC_THIS s.master_pic.imr)) ) { + irq = highest_priority; + do { + /* for special mode, since we're looking at all IRQ's, skip if + * current IRQ is already in-service + */ + if ( ! (BX_PIC_THIS s.master_pic.special_mask && ((BX_PIC_THIS s.master_pic.isr >> irq) & 0x01)) ) { + if (unmasked_requests & (1 << irq)) { + BX_DEBUG(("signalling IRQ(%u)", (unsigned) irq)); + BX_PIC_THIS s.master_pic.INT = 1; + BX_SET_INTR(1); + BX_PIC_THIS s.master_pic.irq = irq; + return; + } /* if (unmasked_requests & ... */ + } + + irq ++; + if(irq > 7) + irq = 0; + } while(irq != max_irq); /* do ... */ + } /* if (unmasked_requests = ... */ +} + + + void +bx_pic_c::service_slave_pic(void) +{ + Bit8u unmasked_requests; + int irq; + Bit8u isr, max_irq; + Bit8u highest_priority = BX_PIC_THIS s.slave_pic.lowest_priority + 1; + if(highest_priority > 7) + highest_priority = 0; + + if (BX_PIC_THIS s.slave_pic.INT) { /* last interrupt still not acknowleged */ + return; + } + + if (BX_PIC_THIS s.slave_pic.special_mask) { + /* all priorities may be enabled. check all IRR bits except ones + * which have corresponding ISR bits set + */ + max_irq = highest_priority; + } + else { /* normal mode */ + /* Find the highest priority IRQ that is enabled due to current ISR */ + isr = BX_PIC_THIS s.slave_pic.isr; + if (isr) { + max_irq = highest_priority; + while ( (isr & (1 << max_irq)) == 0) { + max_irq++; + if(max_irq > 7) + max_irq = 0; + } + if (max_irq == highest_priority ) return; /* Highest priority interrupt in-service, + * no other priorities allowed */ + if (max_irq > 7) BX_PANIC(("error in service_master_pic()")); + } + else + max_irq = highest_priority; /* 0..7 bits in ISR are cleared */ + } + + + /* now, see if there are any higher priority requests */ + if ((unmasked_requests = (BX_PIC_THIS s.slave_pic.irr & ~BX_PIC_THIS s.slave_pic.imr)) ) { + irq = highest_priority; + do { + /* for special mode, since we're looking at all IRQ's, skip if + * current IRQ is already in-service + */ + if ( ! (BX_PIC_THIS s.slave_pic.special_mask && ((BX_PIC_THIS s.slave_pic.isr >> irq) & 0x01)) ) { + if (unmasked_requests & (1 << irq)) { + BX_DEBUG(("slave: signalling IRQ(%u)", (unsigned) 8 + irq)); + + BX_PIC_THIS s.slave_pic.INT = 1; + BX_PIC_THIS s.slave_pic.irq = irq; + BX_PIC_THIS raise_irq(2); /* request IRQ 2 on master pic */ + return; + } /* if (unmasked_requests & ... */ + } + + irq ++; + if(irq > 7) + irq = 0; + } while(irq != max_irq); /* do ... */ + } /* if (unmasked_requests = ... */ +} + + + /* CPU handshakes with PIC after acknowledging interrupt */ + Bit8u +bx_pic_c::IAC(void) +{ + Bit8u vector; + Bit8u irq; + + BX_SET_INTR(0); + BX_PIC_THIS s.master_pic.INT = 0; + BX_PIC_THIS s.master_pic.irr &= ~(1 << BX_PIC_THIS s.master_pic.irq); + // In autoeoi mode don't set the isr bit. + if(!BX_PIC_THIS s.master_pic.auto_eoi) + BX_PIC_THIS s.master_pic.isr |= (1 << BX_PIC_THIS s.master_pic.irq); + else if(BX_PIC_THIS s.master_pic.rotate_on_autoeoi) + BX_PIC_THIS s.master_pic.lowest_priority = BX_PIC_THIS s.master_pic.irq; + + if (BX_PIC_THIS s.master_pic.irq != 2) { + irq = BX_PIC_THIS s.master_pic.irq; + vector = irq + BX_PIC_THIS s.master_pic.interrupt_offset; + } + else { /* IRQ2 = slave pic IRQ8..15 */ + BX_PIC_THIS s.slave_pic.INT = 0; + BX_PIC_THIS s.master_pic.IRQ_line[2] = 0; + irq = BX_PIC_THIS s.slave_pic.irq; + vector = irq + BX_PIC_THIS s.slave_pic.interrupt_offset; + BX_PIC_THIS s.slave_pic.irr &= ~(1 << BX_PIC_THIS s.slave_pic.irq); + // In autoeoi mode don't set the isr bit. + if(!BX_PIC_THIS s.slave_pic.auto_eoi) + BX_PIC_THIS s.slave_pic.isr |= (1 << BX_PIC_THIS s.slave_pic.irq); + else if(BX_PIC_THIS s.slave_pic.rotate_on_autoeoi) + BX_PIC_THIS s.slave_pic.lowest_priority = BX_PIC_THIS s.slave_pic.irq; + service_slave_pic(); + irq += 8; // for debug printing purposes + } + + service_master_pic(); + + BX_DBG_IAC_REPORT(vector, irq); + return(vector); +} + + Bit8u +bx_pic_c::irq_to_vec(Bit8u irq) +{ + Bit8u vector = 0; + + if (irq >= 8 && irq <= 15) + vector = irq + BX_PIC_THIS s.slave_pic.interrupt_offset; + else if (irq != 2 && irq <= 7) + vector = irq + BX_PIC_THIS s.master_pic.interrupt_offset; + else + BX_ERROR(("invalid irq!\n")); + + return vector; +} + + void +bx_pic_c::show_pic_state(void) +{ +#if defined(BX_DEBUGGER) && (BX_DEBUGGER == 1) +dbg_printf("s.master_pic.imr = %02x\n", BX_PIC_THIS s.master_pic.imr); +dbg_printf("s.master_pic.isr = %02x\n", BX_PIC_THIS s.master_pic.isr); +dbg_printf("s.master_pic.irr = %02x\n", BX_PIC_THIS s.master_pic.irr); +dbg_printf("s.master_pic.irq = %02x\n", BX_PIC_THIS s.master_pic.irq); +dbg_printf("s.slave_pic.imr = %02x\n", BX_PIC_THIS s.slave_pic.imr); +dbg_printf("s.slave_pic.isr = %02x\n", BX_PIC_THIS s.slave_pic.isr); +dbg_printf("s.slave_pic.irr = %02x\n", BX_PIC_THIS s.slave_pic.irr); +dbg_printf("s.slave_pic.irq = %02x\n", BX_PIC_THIS s.slave_pic.irq); +#endif +} diff --git a/tools/ioemu/iodev/pic.h b/tools/ioemu/iodev/pic.h new file mode 100644 index 0000000000..378809d67d --- /dev/null +++ b/tools/ioemu/iodev/pic.h @@ -0,0 +1,97 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pic.h,v 1.11 2003/08/04 16:03:09 akrisak Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + +#if BX_USE_PIC_SMF +# define BX_PIC_SMF static +# define BX_PIC_THIS thePic-> +#else +# define BX_PIC_SMF +# define BX_PIC_THIS this-> +#endif + + + +typedef struct { + Bit8u single_PIC; /* 0=cascaded PIC, 1=master only */ + Bit8u interrupt_offset; /* programmable interrupt vector offset */ + union { + Bit8u slave_connect_mask; /* for master, a bit for each interrupt line + 0=not connect to a slave, 1=connected */ + Bit8u slave_id; /* for slave, id number of slave PIC */ + } u; + Bit8u sfnm; /* specially fully nested mode: 0=no, 1=yes*/ + Bit8u buffered_mode; /* 0=no buffered mode, 1=buffered mode */ + Bit8u master_slave; /* master/slave: 0=slave PIC, 1=master PIC */ + Bit8u auto_eoi; /* 0=manual EOI, 1=automatic EOI */ + Bit8u imr; /* interrupt mask register, 1=masked */ + Bit8u isr; /* in service register */ + Bit8u irr; /* interrupt request register */ + Bit8u read_reg_select; /* 0=IRR, 1=ISR */ + Bit8u irq; /* current IRQ number */ + Bit8u lowest_priority; /* current lowest priority irq */ + bx_bool INT; /* INT request pin of PIC */ + bx_bool IRQ_line[8]; /* IRQ pins of PIC */ + struct { + bx_bool in_init; + bx_bool requires_4; + int byte_expected; + } init; + bx_bool special_mask; + bx_bool polled; /* Set when poll command is issued. */ + bx_bool rotate_on_autoeoi; /* Set when should rotate in auto-eoi mode. */ + } bx_pic_t; + + +class bx_pic_c : public bx_pic_stub_c { +public: + bx_pic_c(void); + ~bx_pic_c(void); + virtual void init(void); + virtual void reset(unsigned type); + virtual void lower_irq(unsigned irq_no); + virtual void raise_irq(unsigned irq_no); + virtual Bit8u IAC(void); + virtual void show_pic_state(void); + Bit8u irq_to_vec(Bit8u); + +private: + struct { + bx_pic_t master_pic; + bx_pic_t slave_pic; + } s; + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#if !BX_USE_PIC_SMF + Bit32u read(Bit32u address, unsigned io_len); + void write(Bit32u address, Bit32u value, unsigned io_len); +#endif + + BX_PIC_SMF void service_master_pic(void); + BX_PIC_SMF void service_slave_pic(void); + BX_PIC_SMF void clear_highest_interrupt(bx_pic_t *pic); + }; diff --git a/tools/ioemu/iodev/pit.cc b/tools/ioemu/iodev/pit.cc new file mode 100644 index 0000000000..cf4777a759 --- /dev/null +++ b/tools/ioemu/iodev/pit.cc @@ -0,0 +1,856 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pit.cc,v 1.15 2003/07/31 12:04:48 vruppert 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 + + + +#include "bochs.h" + +#if (BX_USE_NEW_PIT==0) + +#define LOG_THIS bx_pit. + + +// NOTES ON THE 8253/8254 PIT MODES + +// MODE 0: Interrupt on Terminal Count +// =================================== +// Writing new count action: +// loaded upon next CLK pulse. counting doesn't start until GATE=1 +// GATE 0..1 transition: +// ??? +// GATE 1..0 transition: +// counter expiration action: +// wraps to FFFF +// * OUT rises until new count val or new control word for mode 0 written + +// MODE 1: Programmable Monoflop +// ============================= +// Writing new count action: +// not effective for current process +// GATE 0..1 transition: +// loads counter +// counter expiration action: +// wraps to FFFF +// NOTES: +// OUT rises until new count val or new control word for mode 0 written + +// MODE 2: Rate Generator +// ====================== +// Writing new count action: +// ??? +// GATE 0..1 transition: +// loads initial count val and starts counting +// counter expiration action: +// reloads after count expires +// NOTES: +// * after control word & initial count val N loaded, PIT starts +// counting upon next CLK pulse. +// * when counter reaches 1, OUT drops to a low level, for one +// CLK cycle. (short peak pulse generated) +// * afterwards, the initial count val is automatically reloaded +// and the PIT restarts the same counting operation again. +// * distance of two OUT pulses is N CLK cycles long. +// * GATE=1 enables, GATE=0 disables counter. +// * if GATE drops to low level during counting operation and rises +// to high level later, PIT loads initial count value at the +// rise and starts counting. +// * PIT starts counting after last data byte written if GATE=1 +// * if the output is low when the gate goes low, the output is +// immediately set high. + +// MODE 3: Square Wave Generator +// ============================= +// Writing new count action: +// ??? +// GATE 0..1 transition: +// ??? +// counter expiration action: +// reloads after count expires +// NOTES: +// * initially OUT at a high level +// * drop of GATE to a low level while OUT low, raises OUT to a high level +// * a rise from a low to a high level at GATE (trigger pulse), +// loads the counter with the initial count value and starts +// counting operation +// * a new count value supplied during the course of an active +// counting operation doesn't affect the current process. +// At the end of the current half cycle, the PIT loads the new value +// * if the GATE line goes low, count is temporarily halted until GATE +// returns high +// * if the OUT line is high when GATE goes low, OUT is forced low. +// ??? different for odd/even counts + +// MODE 4: Software Triggered Pulse +// ================================ +// Writing new count action: +// ??? +// GATE 0..1 transition: +// ??? +// counter expiration action: +// wraps to FFFF +// NOTES: + +// MODE 5: Hardware Triggered Pulse +// ================================ +// Writing new count action: +// ??? +// GATE 0..1 transition: +// ??? +// counter expiration action: +// wraps to FFFF +// NOTES: + + + +#define BX_PIT_LATCH_MODE_LSB 10 +#define BX_PIT_LATCH_MODE_MSB 11 +#define BX_PIT_LATCH_MODE_16BIT 12 + + +bx_pit_c bx_pit; +#if BX_USE_PIT_SMF +#define this (&bx_pit) +#endif + +#ifdef OUT +# undef OUT +#endif + + +bx_pit_c::bx_pit_c( void ) +{ + put("PIT"); + settype(PITLOG); + memset(&s, 0, sizeof(s)); + + /* 8254 PIT (Programmable Interval Timer) */ + + BX_PIT_THIS s.timer_handle[1] = BX_NULL_TIMER_HANDLE; + BX_PIT_THIS s.timer_handle[2] = BX_NULL_TIMER_HANDLE; +} + +bx_pit_c::~bx_pit_c( void ) +{ +} + + + int +bx_pit_c::init( void ) +{ + DEV_register_irq(0, "8254 PIT"); + DEV_register_ioread_handler(this, read_handler, 0x0040, "8254 PIT", 1); + DEV_register_ioread_handler(this, read_handler, 0x0041, "8254 PIT", 1); + DEV_register_ioread_handler(this, read_handler, 0x0042, "8254 PIT", 1); + DEV_register_ioread_handler(this, read_handler, 0x0043, "8254 PIT", 1); + DEV_register_ioread_handler(this, read_handler, 0x0061, "8254 PIT", 1); + + DEV_register_iowrite_handler(this, write_handler, 0x0040, "8254 PIT", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0041, "8254 PIT", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0042, "8254 PIT", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0043, "8254 PIT", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0061, "8254 PIT", 1); + + BX_PIT_THIS s.speaker_data_on = 0; + BX_PIT_THIS s.refresh_clock_div2 = 0; + + BX_PIT_THIS s.timer[0].mode = 3; /* periodic rate generator */ + BX_PIT_THIS s.timer[0].latch_mode = BX_PIT_LATCH_MODE_16BIT; + BX_PIT_THIS s.timer[0].input_latch_value = 0; + BX_PIT_THIS s.timer[0].input_latch_toggle = 0; + BX_PIT_THIS s.timer[0].output_latch_value = 0; + BX_PIT_THIS s.timer[0].output_latch_toggle = 0; + BX_PIT_THIS s.timer[0].output_latch_full = 0; + BX_PIT_THIS s.timer[0].counter_max = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */ + BX_PIT_THIS s.timer[0].counter = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */ + BX_PIT_THIS s.timer[0].bcd_mode = 0; /* binary counting mode */ + BX_PIT_THIS s.timer[0].GATE = 1; /* GATE tied to + logic */ + BX_PIT_THIS s.timer[0].OUT = 1; + BX_PIT_THIS s.timer[0].active = 0; + + BX_PIT_THIS s.timer[1].mode = 3; /* periodic rate generator */ + BX_PIT_THIS s.timer[1].latch_mode = BX_PIT_LATCH_MODE_16BIT; + BX_PIT_THIS s.timer[1].input_latch_value = 0; + BX_PIT_THIS s.timer[1].input_latch_toggle = 0; + BX_PIT_THIS s.timer[1].output_latch_value = 0; + BX_PIT_THIS s.timer[1].output_latch_toggle = 0; + BX_PIT_THIS s.timer[1].output_latch_full = 0; + BX_PIT_THIS s.timer[1].counter_max = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */ + BX_PIT_THIS s.timer[1].counter = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */ + BX_PIT_THIS s.timer[1].bcd_mode = 0; /* binary counting mode */ + BX_PIT_THIS s.timer[1].GATE = 1; /* GATE tied to + logic */ + BX_PIT_THIS s.timer[1].OUT = 1; + BX_PIT_THIS s.timer[1].active = 0; + + BX_PIT_THIS s.timer[2].mode = 3; /* periodic rate generator */ + BX_PIT_THIS s.timer[2].latch_mode = BX_PIT_LATCH_MODE_16BIT; + BX_PIT_THIS s.timer[2].input_latch_value = 0; + BX_PIT_THIS s.timer[2].input_latch_toggle = 0; + BX_PIT_THIS s.timer[2].output_latch_value = 0; + BX_PIT_THIS s.timer[2].output_latch_toggle = 0; + BX_PIT_THIS s.timer[2].output_latch_full = 0; + BX_PIT_THIS s.timer[2].counter_max = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */ + BX_PIT_THIS s.timer[2].counter = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */ + BX_PIT_THIS s.timer[2].bcd_mode = 0; /* binary counting mode */ + BX_PIT_THIS s.timer[2].GATE = 0; /* timer2 gate controlled by port 61h bit 0 */ + BX_PIT_THIS s.timer[2].OUT = 1; + BX_PIT_THIS s.timer[2].active = 0; + + return(1); +} + +void bx_pit_c::reset(unsigned type) { +} + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_pit_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_PIT_SMF + bx_pit_c *class_ptr = (bx_pit_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + Bit32u +bx_pit_c::read( Bit32u address, unsigned int io_len ) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PIT_SMF + if (bx_dbg.pit) + BX_INFO(("pit: io read from port %04x", (unsigned) address)); + + switch (address) { + case 0x40: /* timer 0 - system ticks */ + return( read_counter(0) ); + break; + + case 0x42: /* timer 2 read */ + return( read_counter(2) ); + break; + + case 0x61: + /* AT, port 61h */ + BX_PIT_THIS s.refresh_clock_div2 = !BX_PIT_THIS s.refresh_clock_div2; + return( (BX_PIT_THIS s.timer[2].OUT<<5) | + (BX_PIT_THIS s.refresh_clock_div2<<4) | + (BX_PIT_THIS s.speaker_data_on<<1) | + (BX_PIT_THIS s.timer[2].GATE) ); + break; + + default: + BX_PANIC(("pit: unsupported io read from port %04x", address)); + } + return(0); /* keep compiler happy */ +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_pit_c::write_handler(void *this_ptr, Bit32u address, Bit32u dvalue, unsigned io_len) +{ +#if !BX_USE_PIT_SMF + bx_pit_c *class_ptr = (bx_pit_c *) this_ptr; + + class_ptr->write(address, dvalue, io_len); +} + + void +bx_pit_c::write( Bit32u address, Bit32u dvalue, + unsigned int io_len ) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PIT_SMF + Bit8u command, mode, bcd_mode; + Bit8u value; + + value = (Bit8u ) dvalue; + + if (bx_dbg.pit) + BX_INFO(("pit: write to port %04x = %02x", + (unsigned) address, (unsigned) value)); + + switch (address) { + case 0x40: /* timer 0: write count register */ + write_count_reg( value, 0 ); + break; + + case 0x41: /* timer 1: write count register */ + write_count_reg( value, 1 ); + break; + + case 0x42: /* timer 2: write count register */ + write_count_reg( value, 2 ); + break; + + case 0x43: /* timer 0-2 mode control */ + /* |7 6 5 4|3 2 1|0| + * |-------|-----|-| + * |command|mode |bcd/binary| + */ + command = value >> 4; + mode = (value >> 1) & 0x07; + bcd_mode = value & 0x01; +#if 0 +BX_INFO(("timer 0-2 mode control: comm:%02x mode:%02x bcd_mode:%u", + (unsigned) command, (unsigned) mode, (unsigned) bcd_mode)); +#endif + + if ( (mode > 5) || (command > 0x0e) ) + BX_PANIC(("pit: outp(43h)=%02xh out of range", (unsigned) value)); + if (bcd_mode) + BX_PANIC(("pit: outp(43h)=%02xh: bcd mode unhandled", + (unsigned) bcd_mode)); + + switch (command) { + case 0x0: /* timer 0: counter latch */ + latch( 0 ); + break; + + case 0x1: /* timer 0: LSB mode */ + case 0x2: /* timer 0: MSB mode */ + BX_PANIC(("pit: outp(43h): command %02xh unhandled", + (unsigned) command)); + break; + case 0x3: /* timer 0: 16-bit mode */ + BX_PIT_THIS s.timer[0].mode = mode; + BX_PIT_THIS s.timer[0].latch_mode = BX_PIT_LATCH_MODE_16BIT; + BX_PIT_THIS s.timer[0].input_latch_value = 0; + BX_PIT_THIS s.timer[0].input_latch_toggle = 0; + BX_PIT_THIS s.timer[0].bcd_mode = bcd_mode; + if ( (mode!=3 && mode!=2 && mode!=0) || bcd_mode!=0 ) + BX_PANIC(("pit: outp(43h): comm 3, mode %02x, bcd %02x unhandled", + (unsigned) mode, bcd_mode)); + break; + case 0x4: /* timer 1: counter latch */ + latch( 1 ); + break; + + case 0x5: /* timer 1: LSB mode */ + case 0x6: /* timer 1: MSB mode */ + BX_INFO(("pit: outp(43h): command %02xh unhandled (ignored)", + (unsigned) command)); + break; + case 0x7: /* timer 1: 16-bit mode */ + BX_PIT_THIS s.timer[1].mode = mode; + BX_PIT_THIS s.timer[1].latch_mode = BX_PIT_LATCH_MODE_16BIT; + BX_PIT_THIS s.timer[1].input_latch_value = 0; + BX_PIT_THIS s.timer[1].input_latch_toggle = 0; + BX_PIT_THIS s.timer[1].bcd_mode = bcd_mode; + if ( (mode!=3 && mode!=2 && mode!=0) || bcd_mode!=0 ) + BX_PANIC(("pit: outp(43h): comm 7, mode %02x, bcd %02x unhandled", + (unsigned) mode, bcd_mode)); + break; + case 0x8: /* timer 2: counter latch */ + latch( 2 ); + break; + + case 0x9: /* timer 2: LSB mode */ + case 0xa: /* timer 2: MSB mode */ + BX_PANIC(("pit: outp(43h): command %02xh unhandled", + (unsigned) command)); + break; + case 0xb: /* timer 2: 16-bit mode */ + BX_PIT_THIS s.timer[2].mode = mode; + BX_PIT_THIS s.timer[2].latch_mode = BX_PIT_LATCH_MODE_16BIT; + BX_PIT_THIS s.timer[2].input_latch_value = 0; + BX_PIT_THIS s.timer[2].input_latch_toggle = 0; + BX_PIT_THIS s.timer[2].bcd_mode = bcd_mode; + if ( (mode!=3 && mode!=2 && mode!=0) || bcd_mode!=0 ) + BX_PANIC(("pit: outp(43h): comm Bh, mode %02x, bcd %02x unhandled", + (unsigned) mode, bcd_mode)); + break; +#if 0 + case 0xd: /* general counter latch */ + if (value & 0x08) /* select counter 2 */ + latch( 2 ); + if (value & 0x04) /* select counter 1 */ + latch( 1 ); + if (value & 0x02) /* select counter 0 */ + latch( 0 ); + break; + + case 0xe: /* latch status of timers */ + BX_PANIC(("pit: outp(43h): command %02xh unhandled", + (unsigned) command); + break; +#endif + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + BX_INFO(("pit: ignoring 8254 command %u", (unsigned) command)); + break; + + default: /* 0xc & 0xf */ + BX_PANIC(("pit: outp(43h) command %1xh unhandled", + (unsigned) command)); + break; + } + break; + + case 0x61: + BX_PIT_THIS s.speaker_data_on = (value >> 1) & 0x01; +/*??? only on AT+ */ + set_GATE(2, value & 0x01); +#if BX_CPU_LEVEL < 2 + /* ??? XT: */ + bx_kbd_port61h_write(value); +#endif + break; + + default: + BX_PANIC(("pit: unsupported io write to port %04x = %02x", + (unsigned) address, (unsigned) value)); + } +} + + + + + void +bx_pit_c::write_count_reg( Bit8u value, unsigned timerid ) +{ + bx_bool xfer_complete; + + switch ( BX_PIT_THIS s.timer[timerid].latch_mode ) { + case BX_PIT_LATCH_MODE_16BIT: /* write1=LSB, write2=MSB */ + if (BX_PIT_THIS s.timer[timerid].input_latch_toggle==0) { + BX_PIT_THIS s.timer[timerid].input_latch_value = value; + BX_PIT_THIS s.timer[timerid].input_latch_toggle = 1; + xfer_complete = 0; + if (bx_dbg.pit) + BX_INFO(("pit: BX_PIT_THIS s.timer[timerid] write L = %02x", (unsigned) value)); + } + else { + BX_PIT_THIS s.timer[timerid].input_latch_value |= (value << 8); + BX_PIT_THIS s.timer[timerid].input_latch_toggle = 0; + xfer_complete = 1; + if (bx_dbg.pit) + BX_INFO(("pit: BX_PIT_THIS s.timer[timerid] write H = %02x", (unsigned) value)); + } + break; + + case BX_PIT_LATCH_MODE_MSB: /* write1=MSB, LSB=0 */ + BX_PIT_THIS s.timer[timerid].input_latch_value = (value << 8); + xfer_complete = 1; + if (bx_dbg.pit) + BX_INFO(("pit: BX_PIT_THIS s.timer[timerid] write H = %02x", (unsigned) value)); + break; + + case BX_PIT_LATCH_MODE_LSB: /* write1=LSB, MSB=0 */ + BX_PIT_THIS s.timer[timerid].input_latch_value = value; + xfer_complete = 1; + if (bx_dbg.pit) + BX_INFO(("pit: BX_PIT_THIS s.timer[timerid] write L = %02x", (unsigned) value)); + break; + + default: + BX_PANIC(("write_count_reg: latch_mode unknown")); + xfer_complete = 0; + } + + if (xfer_complete) { + BX_PIT_THIS s.timer[timerid].counter_max = BX_PIT_THIS s.timer[timerid].input_latch_value; + + // reprogramming counter clears latch + BX_PIT_THIS s.timer[timerid].output_latch_full = 0; + + // counter bounds + // mode minimum maximum + // 0 1 0 + // 1 1 0 + // 2 2 0 + // 3 2 0 + // 4 1 0 + // 5 1 0 + switch (BX_PIT_THIS s.timer[timerid].mode) { + case 0: + BX_PIT_THIS s.timer[timerid].counter = BX_PIT_THIS s.timer[timerid].counter_max; + BX_PIT_THIS s.timer[timerid].active = 1; + if (BX_PIT_THIS s.timer[timerid].GATE) { + BX_PIT_THIS s.timer[timerid].OUT = 0; // OUT pin starts low + start( timerid ); + } + break; + case 1: + BX_PANIC(("pit:write_count_reg(%u): mode1 unsupported", + timerid)); + break; + case 2: + if ( BX_PIT_THIS s.timer[timerid].counter_max == 1 ) + BX_PANIC(("pit:write_count_reg(%u): mode %u counter_max=1", + timerid, (unsigned) BX_PIT_THIS s.timer[timerid].mode)); + if ( BX_PIT_THIS s.timer[timerid].GATE && !BX_PIT_THIS s.timer[timerid].active ) { + // software triggered + BX_PIT_THIS s.timer[timerid].counter = BX_PIT_THIS s.timer[timerid].counter_max; + BX_PIT_THIS s.timer[timerid].active = 1; + BX_PIT_THIS s.timer[timerid].OUT = 1; // initially set high + start( timerid ); + } + break; + case 3: + if ( BX_PIT_THIS s.timer[timerid].counter_max == 1 ) + BX_PANIC(("pit:write_count_reg(%u): mode %u counter_max=1", + timerid, (unsigned) BX_PIT_THIS s.timer[timerid].mode)); + BX_PIT_THIS s.timer[timerid].counter_max = BX_PIT_THIS s.timer[timerid].counter_max & 0xfffe; + if ( BX_PIT_THIS s.timer[timerid].GATE && !BX_PIT_THIS s.timer[timerid].active ) { + // software triggered + BX_PIT_THIS s.timer[timerid].counter = BX_PIT_THIS s.timer[timerid].counter_max; + BX_PIT_THIS s.timer[timerid].active = 1; + BX_PIT_THIS s.timer[timerid].OUT = 1; // initially set high + start( timerid ); + } + break; + case 4: + BX_PANIC(("pit:write_count_reg(%u): mode4 unsupported", + timerid)); + break; + case 5: + BX_PANIC(("pit:write_count_reg(%u): mode5 unsupported", + timerid)); + break; + } + } +} + + + Bit8u +bx_pit_c::read_counter( unsigned timerid ) +{ + Bit16u counter_value; + Bit8u retval; + + if (BX_PIT_THIS s.timer[timerid].output_latch_full) { /* latched read */ + counter_value = BX_PIT_THIS s.timer[timerid].output_latch_value; + } + else { /* direct unlatched read */ + counter_value = BX_PIT_THIS s.timer[timerid].counter; +BX_INFO(("CV=%04x", (unsigned) BX_PIT_THIS s.timer[timerid].counter)); + } + + switch (BX_PIT_THIS s.timer[timerid].latch_mode) { + case BX_PIT_LATCH_MODE_LSB: + retval = (Bit8u ) counter_value; + BX_PIT_THIS s.timer[timerid].output_latch_full = 0; + break; + case BX_PIT_LATCH_MODE_MSB: + retval = (Bit8u ) ( counter_value >> 8 ); + BX_PIT_THIS s.timer[timerid].output_latch_full = 0; + break; + case BX_PIT_LATCH_MODE_16BIT: + if (BX_PIT_THIS s.timer[timerid].output_latch_toggle==0) { /* LSB 1st */ + retval = (Bit8u ) counter_value; + } + else { /* MSB 2nd */ + retval = (Bit8u ) ( counter_value >> 8 ); + } + BX_PIT_THIS s.timer[timerid].output_latch_toggle = !BX_PIT_THIS s.timer[timerid].output_latch_toggle; + if (BX_PIT_THIS s.timer[timerid].output_latch_toggle == 0) + BX_PIT_THIS s.timer[timerid].output_latch_full = 0; + break; + default: + BX_PANIC(("pit: io read from port 40h: unknown latch mode")); + retval = 0; /* keep compiler happy */ + } + return( retval ); +} + + + void +bx_pit_c::latch( unsigned timerid ) +{ + /* subsequent counter latch commands are ignored until value read out */ + if (BX_PIT_THIS s.timer[timerid].output_latch_full) { + BX_INFO(("pit: pit(%u) latch: output latch full, ignoring", + timerid)); + return; + } + + BX_PIT_THIS s.timer[timerid].output_latch_value = BX_PIT_THIS s.timer[timerid].counter; + + if (bx_dbg.pit) + BX_INFO(("pit: latch_value = %u", (unsigned) BX_PIT_THIS s.timer[timerid].output_latch_value)); + BX_PIT_THIS s.timer[timerid].output_latch_toggle = 0; + BX_PIT_THIS s.timer[timerid].output_latch_full = 1; +} + + void +bx_pit_c::set_GATE(unsigned pit_id, unsigned value) +{ + // GATE's for Timer 0 & Timer 1 are tied high. + if (pit_id != 2) + BX_PANIC(("pit:set_GATE: pit_id != 2")); + + value = (value > 0); + + /* if no transition of GATE input line, then nothing to do */ + if (value == BX_PIT_THIS s.timer[2].GATE) + return; + + if (value) { /* PIT2: GATE transition from 0 to 1 */ + BX_PIT_THIS s.timer[2].GATE = 1; + switch ( BX_PIT_THIS s.timer[2].mode ) { + case 0: + BX_PIT_THIS s.timer[2].counter = BX_PIT_THIS s.timer[2].counter_max; + if (BX_PIT_THIS s.timer[2].active) { + BX_PIT_THIS s.timer[2].OUT = 0; + } + start( 2 ); + break; + case 2: + // begin counting, reload counter + BX_PIT_THIS s.timer[2].active = 1; + BX_PIT_THIS s.timer[2].OUT = 1; + BX_PIT_THIS s.timer[2].counter = BX_PIT_THIS s.timer[2].counter_max; + start( 2 ); + break; + case 3: + // begin counting, reload counter + BX_PIT_THIS s.timer[2].active = 1; + BX_PIT_THIS s.timer[2].OUT = 1; + BX_PIT_THIS s.timer[2].counter = BX_PIT_THIS s.timer[2].counter_max; + start( 2 ); + break; + case 1: + case 4: + case 5: + default: + BX_PANIC(("bx_pit_c::set_GATE: unhandled timer2 mode %u", + (unsigned) BX_PIT_THIS s.timer[2].mode)); + } + } + else { // PIT2: GATE transition from 1 to 0, deactivate + BX_PIT_THIS s.timer[2].GATE = 0; + switch ( BX_PIT_THIS s.timer[2].mode ) { + case 0: + break; + case 2: + // 1) stops count, 2) OUT goes immediately high + BX_PIT_THIS s.timer[2].active = 0; + BX_PIT_THIS s.timer[2].OUT = 1; + break; + case 3: + // 1) stops count, 2) OUT goes immediately high + BX_PIT_THIS s.timer[2].active = 0; + BX_PIT_THIS s.timer[2].OUT = 1; + break; + case 1: + case 4: + case 5: + default: + BX_PANIC(("bx_pit_c::set_GATE: unhandled timer2 mode %u", + (unsigned) BX_PIT_THIS s.timer[2].mode)); + } + } +} + + + void +bx_pit_c::start(unsigned timerid) +{ + unsigned long period_hz; + + if (BX_PIT_THIS s.timer[timerid].counter_max == 0x0000) { + period_hz = 1193182 / 65536; + } + else { + period_hz = 1193182 / BX_PIT_THIS s.timer[timerid].counter_max; + } + BX_INFO(("timer%u period set to %lu hz", timerid, period_hz)); + + + switch (BX_PIT_THIS s.timer[timerid].mode) { + case 0: /* single timeout */ + break; + case 1: /* retriggerable one-shot */ + BX_PANIC(("start: mode %u unhandled", + (unsigned) BX_PIT_THIS s.timer[timerid].mode)); + break; + case 2: /* rate generator */ + break; + case 3: /* square wave mode */ + break; + case 4: /* software triggered strobe */ + BX_PANIC(("start: mode %u unhandled", + (unsigned) BX_PIT_THIS s.timer[timerid].mode)); + break; + case 5: /* hardware retriggerable strobe */ + BX_PANIC(("start: mode %u unhandled", + (unsigned) BX_PIT_THIS s.timer[timerid].mode)); + break; + default: + BX_PANIC(("start: timer%u has bad mode", + (unsigned) BX_PIT_THIS s.timer[timerid].mode)); + } +} + + + + + int +bx_pit_c::SaveState( class state_file *fd ) +{ + fd->write_check ("8254 start"); + fd->write (&BX_PIT_THIS s, sizeof (BX_PIT_THIS s)); + fd->write_check ("8254 end"); + return(0); +} + + + int +bx_pit_c::LoadState( class state_file *fd ) +{ + fd->read_check ("8254 start"); + fd->read (&BX_PIT_THIS s, sizeof (BX_PIT_THIS s)); + fd->read_check ("8254 end"); + return(0); +} + + +#if 0 + void +bx_kbd_port61h_write(Bit8u value) +{ +// PcError("KBD_PORT61H_WRITE(): not implemented yet"); + UNUSED( value ); +} +#endif + + + bx_bool +bx_pit_c::periodic( Bit32u usec_delta ) +{ + bx_bool prev_timer0_out; + + prev_timer0_out = BX_PIT_THIS s.timer[0].OUT; + + for (unsigned i = 0; i < 3; i++) { + // is timer enabled and active? + if ( BX_PIT_THIS s.timer[i].GATE && BX_PIT_THIS s.timer[i].active ) { + switch ( BX_PIT_THIS s.timer[i].mode ) { + case 0: // Mode 0: Single Timeout + // wraps after count expires + if ( BX_PIT_THIS s.timer[i].counter == 0 ) { + // counter previously expired, wrap counter + BX_PIT_THIS s.timer[i].counter = 0xffff; + } + else if ( usec_delta >= BX_PIT_THIS s.timer[i].counter ) { + // counter expired + BX_PIT_THIS s.timer[i].counter = 0; + BX_PIT_THIS s.timer[i].OUT = 1; + } + else { + // decrement counter by elapsed useconds + BX_PIT_THIS s.timer[i].counter -= (Bit16u ) usec_delta; + } + break; + + case 1: // Mode 1: Retriggerable One-Shot + // wraps after count expires + BX_PANIC(("bx_pit_c::periodic: bad mode: timer[%u], mode %u", + i, (unsigned) BX_PIT_THIS s.timer[i].mode)); + break; + + case 2: // Mode 2: Rate Generator + // reloads after count expires + // OUT is low when counter=1, high otherwise + // min count=2, max count=0 + if ( BX_PIT_THIS s.timer[i].counter == 0 ) { + // max counter val, just wrap + BX_PIT_THIS s.timer[i].counter = 0xffff; + BX_PIT_THIS s.timer[i].OUT = 1; + } + else if ( BX_PIT_THIS s.timer[i].counter == 1 ) { + // counter previously expired, reload + BX_PIT_THIS s.timer[i].counter = BX_PIT_THIS s.timer[i].counter_max; + BX_PIT_THIS s.timer[i].OUT = 1; + } + else if ( (BX_PIT_THIS s.timer[i].counter == 2) || + (usec_delta >= (Bit32u(BX_PIT_THIS s.timer[i].counter) - 1)) ) { + // in either case, counter will reach 1 + BX_PIT_THIS s.timer[i].counter = 1; + BX_PIT_THIS s.timer[i].OUT = 0; + } + else { + // decrement counter by elapsed useconds + BX_PIT_THIS s.timer[i].counter -= (Bit16u ) usec_delta; + } + break; + + case 3: // Mode 3: Square Wave Mode + // reloads after count expires + // min count=2, max count=0 + if ( BX_PIT_THIS s.timer[i].counter == 0 ) { + // max count, dec by 2 + BX_PIT_THIS s.timer[i].counter = 0xfffe; + } + else if ( (BX_PIT_THIS s.timer[i].counter <= 2) || + ( (usec_delta*2) >= BX_PIT_THIS s.timer[i].counter ) ) { + // counter expired, reload + BX_PIT_THIS s.timer[i].counter = BX_PIT_THIS s.timer[i].counter_max; + BX_PIT_THIS s.timer[i].OUT = !BX_PIT_THIS s.timer[i].OUT; + //BX_INFO(("CV: reload t%u to %04x", (unsigned) i, (unsigned) + // BX_PIT_THIS s.timer[i].counter)); + } + else { + // decrement counter by elapsed useconds + BX_PIT_THIS s.timer[i].counter -= (Bit16u ) ( 2*usec_delta ); + //BX_INFO(("CV: dec count to %04x", + // (unsigned) BX_PIT_THIS s.timer[i].counter)); + } + break; + + case 4: // Mode 4: Software Triggered Strobe + // wraps after count expires + BX_PANIC(("bx_pit_c::periodic: bad mode: timer[%u], mode %u", + i, (unsigned) BX_PIT_THIS s.timer[i].mode)); + break; + + case 5: // Mode 5: Hardware Retriggerable Strobe + // wraps after count expires + BX_PANIC(("bx_pit_c::periodic: bad mode: timer[%u], mode %u", + i, (unsigned) BX_PIT_THIS s.timer[i].mode)); + break; + default: + BX_PANIC(("bx_pit_c::periodic: bad mode: timer[%u], mode %u", + i, (unsigned) BX_PIT_THIS s.timer[i].mode)); + break; + } // switch ( BX_PIT_THIS s.tim... + } // if ( BX_PIT_THIS s.timer[i]... + } // for (unsigned i... + + // see if there's a rising edge on timer0's output to trigger an IRQ0. + if ( (prev_timer0_out==0) && (BX_PIT_THIS s.timer[0].OUT==1) ) + return(1); // request IRQ 0 + else + return(0); +} + +#endif // #if (BX_USE_NEW_PIT==0) diff --git a/tools/ioemu/iodev/pit.h b/tools/ioemu/iodev/pit.h new file mode 100644 index 0000000000..49b663bf9d --- /dev/null +++ b/tools/ioemu/iodev/pit.h @@ -0,0 +1,103 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pit.h,v 1.10 2002/10/25 11:44:40 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 + +#ifndef _BX_PIT_H +#define _BX_PIT_H + +#include "config.h" + +#if (BX_USE_NEW_PIT==0) + +#if BX_USE_PIT_SMF +# define BX_PIT_SMF static +# define BX_PIT_THIS bx_pit. +#else +# define BX_PIT_SMF +# define BX_PIT_THIS this-> +#endif + +#ifdef OUT +# undef OUT +#endif + + +typedef struct { + Bit8u mode; + Bit8u latch_mode; + Bit16u input_latch_value; + bx_bool input_latch_toggle; + Bit16u output_latch_value; + bx_bool output_latch_toggle; + bx_bool output_latch_full; + Bit16u counter_max; + Bit16u counter; + bx_bool bcd_mode; + bx_bool active; + bx_bool GATE; // GATE input pin + bx_bool OUT; // OUT output pin + } bx_pit_t; + + + + +class bx_pit_c : public logfunctions { +public: + bx_pit_c( void ); + ~bx_pit_c( void ); + BX_PIT_SMF int init(void); + BX_PIT_SMF void reset( unsigned type); + BX_PIT_SMF bx_bool periodic( Bit32u usec_delta ); + + BX_PIT_SMF int SaveState( class state_file *fd ); + BX_PIT_SMF int LoadState( class state_file *fd ); + +private: + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#if !BX_USE_PIT_SMF + Bit32u read( Bit32u addr, unsigned int len ); + void write( Bit32u addr, Bit32u Value, unsigned int len ); +#endif + + struct s_type { + bx_pit_t timer[3]; + Bit8u speaker_data_on; + bx_bool refresh_clock_div2; + int timer_handle[3]; + } s; + + BX_PIT_SMF void write_count_reg( Bit8u value, unsigned timerid ); + BX_PIT_SMF Bit8u read_counter( unsigned timerid ); + BX_PIT_SMF void latch( unsigned timerid ); + BX_PIT_SMF void set_GATE(unsigned pit_id, unsigned value); + BX_PIT_SMF void start(unsigned timerid); + }; + +extern bx_pit_c bx_pit; + +#endif // #if (BX_USE_NEW_PIT==0) +#endif // #ifndef _BX_PIT_H diff --git a/tools/ioemu/iodev/pit82c54.cc b/tools/ioemu/iodev/pit82c54.cc new file mode 100644 index 0000000000..493faf6ee1 --- /dev/null +++ b/tools/ioemu/iodev/pit82c54.cc @@ -0,0 +1,930 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pit82c54.cc,v 1.23 2003/06/29 17:24:52 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +/* + * Emulator of an Intel 8254/82C54 Programmable Interval Timer. + * Greg Alexander <yakovlev@usa.com> + * + * + * Things I am unclear on (greg): + * 1.)What happens if both the status and count registers are latched, + * but the first of the two count registers has already been read? + * I.E.: + * latch count 0 (16-bit) + * Read count 0 (read LSByte) + * READ_BACK status of count 0 + * Read count 0 - do you get MSByte or status? + * This will be flagged as an error. + * 2.)What happens when we latch the output in the middle of a 2-part + * unlatched read? + * 3.)I assumed that programming a counter removes a latched status. + * 4.)I implemented the 8254 description of mode 0, not the 82C54 one. + * 5.)clock() calls represent a rising clock edge followed by a falling + * clock edge. + * 6.)What happens when we trigger mode 1 in the middle of a 2-part + * write? + */ + +#include "bochs.h" +#include "pit82c54.h" +#define LOG_THIS this-> + + +void pit_82C54::print_counter(counter_type & thisctr) { +#if 1 + BX_INFO(("Printing Counter")); + BX_INFO(("count: %d",thisctr.count)); + BX_INFO(("count_binary: %x",thisctr.count_binary)); + BX_INFO(("counter gate: %x",thisctr.GATE)); + BX_INFO(("counter OUT: %x",thisctr.OUTpin)); + BX_INFO(("next_change_time: %d",thisctr.next_change_time)); + BX_INFO(("End Counter Printout")); +#endif +} + +void pit_82C54::print_cnum(Bit8u cnum) { + if(cnum>MAX_COUNTER) { + BX_ERROR(("Bad counter index to print_cnum")); + } else { + print_counter(counter[cnum]); + } +} + + void pit_82C54::latch_counter(counter_type & thisctr) { + if(thisctr.count_LSB_latched || thisctr.count_MSB_latched) { + //Do nothing because previous latch has not been read.; + } else { + switch(thisctr.read_state) { + case MSByte: + thisctr.outlatch=thisctr.count & 0xFFFF; + thisctr.count_MSB_latched=1; + break; + case LSByte: + thisctr.outlatch=thisctr.count & 0xFFFF; + thisctr.count_LSB_latched=1; + break; + case LSByte_multiple: + thisctr.outlatch=thisctr.count & 0xFFFF; + thisctr.count_LSB_latched=1; + thisctr.count_MSB_latched=1; + break; + case MSByte_multiple: + if(!(seen_problems & UNL_2P_READ)) { +// seen_problems|=UNL_2P_READ; + BX_ERROR(("Unknown behavior when latching during 2-part read.")); + BX_ERROR((" This message will not be repeated.")); + } + //I guess latching and resetting to LSB first makes sense; + BX_DEBUG(("Setting read_state to LSB_mult")); + thisctr.read_state=LSByte_multiple; + thisctr.outlatch=thisctr.count & 0xFFFF; + thisctr.count_LSB_latched=1; + thisctr.count_MSB_latched=1; + break; + default: + BX_ERROR(("Unknown read mode found during latch command.")); + break; + } + } + } + + void pit_82C54::set_OUT (counter_type & thisctr, bool data) { + //This will probably have a callback, so I put it here. + thisctr.OUTpin=data; + } + + void BX_CPP_AttrRegparmN(2) +pit_82C54::set_count (counter_type & thisctr, Bit32u data) { + thisctr.count=data & 0xFFFF; + set_binary_to_count(thisctr); + } + + void BX_CPP_AttrRegparmN(1) +pit_82C54::set_count_to_binary(counter_type & thisctr) { + if(thisctr.bcd_mode) { + thisctr.count= + (((thisctr.count_binary/1)%10)<<0) | + (((thisctr.count_binary/10)%10)<<4) | + (((thisctr.count_binary/100)%10)<<8) | + (((thisctr.count_binary/1000)%10)<<12) + ; + } else { + thisctr.count=thisctr.count_binary; + } + } + + void BX_CPP_AttrRegparmN(1) +pit_82C54::set_binary_to_count(counter_type & thisctr) { + if(thisctr.bcd_mode) { + thisctr.count_binary= + (1*((thisctr.count>>0)&0xF)) + + (10*((thisctr.count>>4)&0xF)) + + (100*((thisctr.count>>8)&0xF)) + + (1000*((thisctr.count>>12)&0xF)) + ; + } else { + thisctr.count_binary=thisctr.count; + } + } + + void BX_CPP_AttrRegparmN(1) +pit_82C54::decrement (counter_type & thisctr) { + if(!thisctr.count) { + if(thisctr.bcd_mode) { + thisctr.count=0x9999; + thisctr.count_binary=9999; + } else { + thisctr.count=0xFFFF; + thisctr.count_binary=0xFFFF; + } + } else { + thisctr.count_binary--; + set_count_to_binary(thisctr); + } + } + + void pit_82C54::init (void) { + Bit8u i; + + put("PIT81"); + settype(PIT81LOG); + + for(i=0;i<3;i++) { + BX_DEBUG(("Setting read_state to LSB")); + counter[i].read_state=LSByte; + counter[i].write_state=LSByte; + counter[i].GATE=1; + counter[i].OUTpin=1; + counter[i].triggerGATE=0; + counter[i].mode=4; + counter[i].first_pass=0; + counter[i].bcd_mode=0; + counter[i].count=0; + counter[i].count_binary=0; + counter[i].state_bit_1=0; + counter[i].state_bit_2=0; + counter[i].null_count=0; + counter[i].rw_mode=1; + counter[i].count_written=1; + counter[i].count_LSB_latched=0; + counter[i].count_MSB_latched=0; + counter[i].status_latched=0; + counter[i].next_change_time=0; + } + seen_problems=0; + } + + pit_82C54::pit_82C54 (void) { + init(); + } + + void pit_82C54::reset (unsigned type) { + } + +void BX_CPP_AttrRegparmN(2) +pit_82C54::decrement_multiple(counter_type & thisctr, Bit32u cycles) { + while(cycles>0) { + if(cycles<=thisctr.count_binary) { + thisctr.count_binary-=cycles; + cycles-=cycles; + set_count_to_binary(thisctr); + } else { + cycles-=(thisctr.count_binary+1); + thisctr.count_binary-=thisctr.count_binary; + set_count_to_binary(thisctr); + decrement(thisctr); + } + } +} + +void pit_82C54::clock_multiple(Bit8u cnum, Bit32u cycles) { + if(cnum>MAX_COUNTER) { + BX_ERROR(("Counter number too high in clock")); + } else { + counter_type & thisctr = counter[cnum]; + while(cycles>0) { + if(thisctr.next_change_time==0) { + if(thisctr.count_written) { + switch(thisctr.mode) { + case 0: + if(thisctr.GATE && (thisctr.write_state!=MSByte_multiple)) { + decrement_multiple(thisctr, cycles); + } + break; + case 1: + decrement_multiple(thisctr, cycles); + break; + case 2: + if( (!thisctr.first_pass) && thisctr.GATE ) { + decrement_multiple(thisctr, cycles); + } + break; + case 3: + if( (!thisctr.first_pass) && thisctr.GATE ) { + decrement_multiple(thisctr, 2*cycles); + } + break; + case 4: + if(thisctr.GATE) { + decrement_multiple(thisctr, cycles); + } + break; + case 5: + decrement_multiple(thisctr, cycles); + break; + default: + break; + } + } + cycles-=cycles; + } else { + switch(thisctr.mode) { + case 0: + case 1: + case 2: + case 4: + case 5: + if( thisctr.next_change_time > cycles ) { + decrement_multiple(thisctr,cycles); + thisctr.next_change_time-=cycles; + cycles-=cycles; + } else { + decrement_multiple(thisctr,(thisctr.next_change_time-1)); + cycles-=thisctr.next_change_time; + clock(cnum); + } + break; + case 3: + if( thisctr.next_change_time > cycles ) { + decrement_multiple(thisctr,cycles*2); + thisctr.next_change_time-=cycles; + cycles-=cycles; + } else { + decrement_multiple(thisctr,(thisctr.next_change_time-1)*2); + cycles-=thisctr.next_change_time; + clock(cnum); + } + break; + default: + cycles-=cycles; + break; + } + } + } +#if 0 + print_counter(thisctr); +#endif + } +} + + void BX_CPP_AttrRegparmN(1) +pit_82C54::clock(Bit8u cnum) { + if(cnum>MAX_COUNTER) { + BX_ERROR(("Counter number too high in clock")); + } else { + counter_type & thisctr = counter[cnum]; + switch(thisctr.mode) { + case 0: + if(thisctr.count_written) { + if(thisctr.null_count) { + set_count(thisctr, thisctr.inlatch); + if(thisctr.GATE) { + if(thisctr.count_binary==0) { + thisctr.next_change_time=1; + } else { + thisctr.next_change_time=thisctr.count_binary & 0xFFFF; + } + } else { + thisctr.next_change_time=0; + } + thisctr.null_count=0; + } else { + if(thisctr.GATE && (thisctr.write_state!=MSByte_multiple)) { + decrement(thisctr); + if(!thisctr.OUTpin) { + thisctr.next_change_time=thisctr.count_binary & 0xFFFF; + if(!thisctr.count) { + set_OUT(thisctr,1); + } + } else { + thisctr.next_change_time=0; + } + } else { + thisctr.next_change_time=0; //if the clock isn't moving. + } + } + } else { + thisctr.next_change_time=0; //default to 0. + } + thisctr.triggerGATE=0; + break; + case 1: + if(thisctr.count_written) { + if(thisctr.triggerGATE) { + set_count(thisctr, thisctr.inlatch); + if(thisctr.count_binary==0) { + thisctr.next_change_time=1; + } else { + thisctr.next_change_time=thisctr.count_binary & 0xFFFF; + } + thisctr.null_count=0; + set_OUT(thisctr,0); + if(thisctr.write_state==MSByte_multiple) { + BX_ERROR(("Undefined behavior when loading a half loaded count.")); + } + } else { + decrement(thisctr); + if(!thisctr.OUTpin) { + if(thisctr.count_binary==0) { + thisctr.next_change_time=1; + } else { + thisctr.next_change_time=thisctr.count_binary & 0xFFFF; + } + if(thisctr.count==0) { + set_OUT(thisctr,1); + } + } else { + thisctr.next_change_time=0; + } + } + } else { + thisctr.next_change_time=0; //default to 0. + } + thisctr.triggerGATE=0; + break; + case 2: + if(thisctr.count_written) { + if(thisctr.triggerGATE || thisctr.first_pass) { + set_count(thisctr, thisctr.inlatch); + thisctr.next_change_time=(thisctr.count_binary-1) & 0xFFFF; + thisctr.null_count=0; + if(thisctr.inlatch==1) { + BX_ERROR(("ERROR: count of 1 is invalid in pit mode 2.")); + } + if(!thisctr.OUTpin) { + set_OUT(thisctr,1); + } + if(thisctr.write_state==MSByte_multiple) { + BX_ERROR(("Undefined behavior when loading a half loaded count.")); + } + thisctr.first_pass=0; + } else { + if(thisctr.GATE) { + decrement(thisctr); + thisctr.next_change_time=(thisctr.count_binary-1) & 0xFFFF; + if(thisctr.count==1) { + thisctr.next_change_time=1; + set_OUT(thisctr,0); + thisctr.first_pass=1; + } + } else { + thisctr.next_change_time=0; + } + } + } else { + thisctr.next_change_time=0; + } + thisctr.triggerGATE=0; + break; + case 3: + if(thisctr.count_written) { + if( (thisctr.triggerGATE || thisctr.first_pass + || thisctr.state_bit_2) && thisctr.GATE ) { + set_count(thisctr, thisctr.inlatch & 0xFFFE); + thisctr.state_bit_1=thisctr.inlatch & 0x1; + if( (!thisctr.OUTpin) || (!(thisctr.state_bit_1))) { + if(((thisctr.count_binary/2)-1)==0) { + thisctr.next_change_time=1; + } else { + thisctr.next_change_time=((thisctr.count_binary/2)-1) & 0xFFFF; + } + } else { + if((thisctr.count_binary/2)==0) { + thisctr.next_change_time=1; + } else { + thisctr.next_change_time=(thisctr.count_binary/2) & 0xFFFF; + } + } + thisctr.null_count=0; + if(thisctr.inlatch==1) { + BX_ERROR(("Count of 1 is invalid in pit mode 3.")); + } + if(!thisctr.OUTpin) { + set_OUT(thisctr,1); + } else if(thisctr.OUTpin && !thisctr.first_pass) { + set_OUT(thisctr,0); + } + if(thisctr.write_state==MSByte_multiple) { + BX_ERROR(("Undefined behavior when loading a half loaded count.")); + } + thisctr.state_bit_2=0; + thisctr.first_pass=0; + } else { + if(thisctr.GATE) { + decrement(thisctr); + decrement(thisctr); + if( (!thisctr.OUTpin) || (!(thisctr.state_bit_1))) { + thisctr.next_change_time=((thisctr.count_binary/2)-1) & 0xFFFF; + } else { + thisctr.next_change_time=(thisctr.count_binary/2) & 0xFFFF; + } + if(thisctr.count==0) { + thisctr.state_bit_2=1; + thisctr.next_change_time=1; + } + if( (thisctr.count==2) && + ( (!thisctr.OUTpin) || (!(thisctr.state_bit_1))) + ) { + thisctr.state_bit_2=1; + thisctr.next_change_time=1; + } + } else { + thisctr.next_change_time=0; + } + } + } else { + thisctr.next_change_time=0; + } + thisctr.triggerGATE=0; + break; + case 4: + if(thisctr.count_written) { + if(!thisctr.OUTpin) { + set_OUT(thisctr,1); + } + if(thisctr.null_count) { + set_count(thisctr, thisctr.inlatch); + if(thisctr.GATE) { + if(thisctr.count_binary==0) { + thisctr.next_change_time=1; + } else { + thisctr.next_change_time=thisctr.count_binary & 0xFFFF; + } + } else { + thisctr.next_change_time=0; + } + thisctr.null_count=0; + if(thisctr.write_state==MSByte_multiple) { + BX_ERROR(("Undefined behavior when loading a half loaded count.")); + } + thisctr.first_pass=1; + } else { + if(thisctr.GATE) { + decrement(thisctr); + if(thisctr.first_pass) { + thisctr.next_change_time=thisctr.count_binary & 0xFFFF; + if(!thisctr.count) { + set_OUT(thisctr,0); + thisctr.next_change_time=1; + thisctr.first_pass=0; + } + } else { + thisctr.next_change_time=0; + } + } else { + thisctr.next_change_time=0; + } + } + } else { + thisctr.next_change_time=0; + } + thisctr.triggerGATE=0; + break; + case 5: + if(thisctr.count_written) { + if(!thisctr.OUTpin) { + set_OUT(thisctr,1); + } + if(thisctr.triggerGATE) { + set_count(thisctr, thisctr.inlatch); + if(thisctr.count_binary==0) { + thisctr.next_change_time=1; + } else { + thisctr.next_change_time=thisctr.count_binary & 0xFFFF; + } + thisctr.null_count=0; + if(thisctr.write_state==MSByte_multiple) { + BX_ERROR(("Undefined behavior when loading a half loaded count.")); + } + thisctr.first_pass=1; + } else { + decrement(thisctr); + if(thisctr.first_pass) { + thisctr.next_change_time=thisctr.count_binary & 0xFFFF; + if(!thisctr.count) { + set_OUT(thisctr,0); + thisctr.next_change_time=1; + thisctr.first_pass=0; + } + } else { + thisctr.next_change_time=0; + } + } + } else { + thisctr.next_change_time=0; + } + thisctr.triggerGATE=0; + break; + default: + BX_ERROR(("Mode not implemented.")); + thisctr.next_change_time=0; + thisctr.triggerGATE=0; + break; + } + } + } + + void pit_82C54::clock_all(Bit32u cycles) { + BX_DEBUG(("clock_all: cycles=%d",cycles)); + clock_multiple(0,cycles); + clock_multiple(1,cycles); + clock_multiple(2,cycles); + } + + Bit8u pit_82C54::read(Bit8u address) { + if(address>MAX_ADDRESS) { + BX_ERROR(("Counter address incorrect in data read.")); + } else if(address==CONTROL_ADDRESS) { + BX_DEBUG(("PIT Read: Control Word Register.")); + //Read from control word register; + /* This might be okay. If so, 0 seems the most logical + * return value from looking at the docs. + */ + BX_ERROR(("Read from control word register not defined.")); + return 0; + } else { + //Read from a counter; + BX_DEBUG(("PIT Read: Counter %d.",address)); + counter_type & thisctr=counter[address]; + if(thisctr.status_latched) { + //Latched Status Read; + if(thisctr.count_MSB_latched && + (thisctr.read_state==MSByte_multiple) ) { + BX_ERROR(("Undefined output when status latched and count half read.")); + } else { + thisctr.status_latched=0; + return thisctr.status_latch; + } + } else { + //Latched Count Read; + if(thisctr.count_LSB_latched) { + //Read Least Significant Byte; + if(thisctr.read_state==LSByte_multiple) { + BX_DEBUG(("Setting read_state to MSB_mult")); + thisctr.read_state=MSByte_multiple; + } + thisctr.count_LSB_latched=0; + return (thisctr.outlatch & 0xFF); + } else if(thisctr.count_MSB_latched) { + //Read Most Significant Byte; + if(thisctr.read_state==MSByte_multiple) { + BX_DEBUG(("Setting read_state to LSB_mult")); + thisctr.read_state=LSByte_multiple; + } + thisctr.count_MSB_latched=0; + return ((thisctr.outlatch>>8) & 0xFF); + } else { + //Unlatched Count Read; + if(!(thisctr.read_state & 0x1)) { + //Read Least Significant Byte; + if(thisctr.read_state==LSByte_multiple) { + thisctr.read_state=MSByte_multiple; + BX_DEBUG(("Setting read_state to MSB_mult")); + } + return (thisctr.count & 0xFF); + } else { + //Read Most Significant Byte; + if(thisctr.read_state==MSByte_multiple) { + BX_DEBUG(("Setting read_state to LSB_mult")); + thisctr.read_state=LSByte_multiple; + } + return ((thisctr.count>>8) & 0xFF); + } + } + } + } + //Should only get here on errors; + return 0; + } + +#ifdef BX_VMX_PIT +//extra operations when use vmx pit device model + void pit_82C54::write_initcount_vmx(Bit8u cnum) { + if(cnum>MAX_COUNTER) { + BX_ERROR(("Counter number incorrect\n")); + } + + ioreq_t *req = &((vcpu_iodata_t *) shared_page)->vp_ioreq; + extern bx_pic_c *thePic; + counter_type & thisctr = counter[cnum]; + if(req->pdata_valid) { + BX_ERROR(("VMX_PIT:err!pit is port io!\n")); + } + + if (thisctr.mode == 2) {//periodic mode, need HV to help send interrupt + req->state = STATE_IORESP_HOOK; + +// req->u.data = thisctr.inlatch * 1000 / PIT_FREQ;//init count:16 bit + req->u.data = thisctr.inlatch; //init count:16 bit + //get the pit irq(0)'s vector from pic DM + req->u.data |= ((thePic->irq_to_vec(0)) << 16 ); //timer vec:8 bit + req->u.data |= (cnum << 24); //PIT channel(0~2):2 bit + req->u.data |= ((thisctr.rw_mode) << 26); //rw mode:2 bit + + BX_INFO(("VMX_PIT:whole pit hook packet = 0x%llx \n", (req->u.data ) )); + BX_INFO(("VMX_PIT:init counter = %d ms\n", (req->u.data & 0xFFFF) )); + } + + } +#endif + + void pit_82C54::write(Bit8u address, Bit8u data) { + if(address>MAX_ADDRESS) { + BX_ERROR(("Counter address incorrect in data write.")); + } else if(address==CONTROL_ADDRESS) { + Bit8u SC, RW, M, BCD; + controlword=data; + BX_DEBUG(("Control Word Write.")); + SC = (controlword>>6) & 0x3; + RW = (controlword>>4) & 0x3; + M = (controlword>>1) & 0x7; + BCD = controlword & 0x1; + if(SC == 3) { + //READ_BACK command; + int i; + BX_DEBUG(("READ_BACK command.")); + for(i=0;i<=MAX_COUNTER;i++) { + if((M>>i) & 0x1) { + //If we are using this counter; + counter_type & thisctr=counter[i]; + if(!((controlword>>5) & 1)) { + //Latch Count; + latch_counter(thisctr); + } + if(!((controlword>>4) & 1)) { + //Latch Status; + if(thisctr.status_latched) { + //Do nothing because latched status has not been read.; + } else { + thisctr.status_latch= + ((thisctr.OUTpin & 0x1) << 7) | + ((thisctr.null_count & 0x1) << 6) | + ((thisctr.rw_mode & 0x3) << 4) | + ((thisctr.mode & 0x7) << 1) | + (thisctr.bcd_mode&0x1) + ; + thisctr.status_latched=1; + } + } + } + } + } else { + counter_type & thisctr = counter[SC]; + if(!RW) { + //Counter Latch command; + BX_DEBUG(("Counter Latch command. SC=%d",SC)); + latch_counter(thisctr); + } else { + //Counter Program Command; + BX_DEBUG(("Counter Program command. SC=%d, RW=%d, M=%d, BCD=%d",SC,RW,M,BCD)); + thisctr.null_count=1; + thisctr.count_LSB_latched=0; + thisctr.count_MSB_latched=0; + thisctr.status_latched=0; + thisctr.inlatch=0; + thisctr.count_written=0; + thisctr.first_pass=1; + thisctr.rw_mode=RW; + thisctr.bcd_mode=(BCD > 0); + thisctr.mode=M; + switch(RW) { + case 0x1: + BX_DEBUG(("Setting read_state to LSB")); + thisctr.read_state=LSByte; + thisctr.write_state=LSByte; + break; + case 0x2: + BX_DEBUG(("Setting read_state to MSB")); + thisctr.read_state=MSByte; + thisctr.write_state=MSByte; + break; + case 0x3: + BX_DEBUG(("Setting read_state to LSB_mult")); + thisctr.read_state=LSByte_multiple; + thisctr.write_state=LSByte_multiple; + break; + default: + BX_ERROR(("RW field invalid in control word write.")); + break; + } + //All modes except mode 0 have initial output of 1.; + if(M) { + set_OUT(thisctr, 1); + } else { + set_OUT(thisctr, 0); + } + thisctr.next_change_time=0; + } + } + } else { + //Write to counter initial value. + counter_type & thisctr = counter[address]; + BX_DEBUG(("Write Initial Count: counter=%d, count=%d",address,data)); + switch(thisctr.write_state) { + case LSByte_multiple: + thisctr.inlatch=(thisctr.inlatch & (0xFF<<8)) | data; + thisctr.write_state=MSByte_multiple; + break; + case LSByte: + thisctr.inlatch=(thisctr.inlatch & (0xFF<<8)) | data; + thisctr.null_count=1; + thisctr.count_written=1; +#ifdef BX_VMX_PIT + write_initcount_vmx(address); +#endif + break; + case MSByte_multiple: + thisctr.write_state=LSByte_multiple; + case MSByte: //shared between MSB_multiple and MSByte + thisctr.inlatch=(thisctr.inlatch & 0xFF) | (data<<8); + thisctr.null_count=1; + thisctr.count_written=1; +#ifdef BX_VMX_PIT + write_initcount_vmx(address); +#endif + break; + default: + BX_ERROR(("write counter in invalid write state.")); + break; + } + switch(thisctr.mode) { + case 0: + if(thisctr.write_state==MSByte_multiple) { + set_OUT(thisctr,0); + } + thisctr.next_change_time=1; + break; + case 1: + if(thisctr.triggerGATE) { //for initial writes, if already saw trigger. + thisctr.next_change_time=1; + } //Otherwise, no change. + break; + case 6: + case 2: + thisctr.next_change_time=1; //FIXME: this could be loosened. + break; + case 7: + case 3: + thisctr.next_change_time=1; //FIXME: this could be loosened. + break; + case 4: + thisctr.next_change_time=1; + break; + case 5: + if(thisctr.triggerGATE) { //for initial writes, if already saw trigger. + thisctr.next_change_time=1; + } //Otherwise, no change. + break; + } + } + } + + void pit_82C54::set_GATE(Bit8u cnum, bool data) { + if(cnum>MAX_COUNTER) { + BX_ERROR(("Counter number incorrect in 82C54 set_GATE")); + } else { + counter_type & thisctr = counter[cnum]; + if(!( (thisctr.GATE&&data) || (!(thisctr.GATE||data)) )) { + BX_INFO(("Changing GATE %d to: %d",cnum,data)); + thisctr.GATE=data; + if(thisctr.GATE) { + thisctr.triggerGATE=1; + } + switch(thisctr.mode) { + case 0: + if(data && thisctr.count_written) { + if(thisctr.null_count) { + thisctr.next_change_time=1; + } else { + if( (!thisctr.OUTpin) && + (thisctr.write_state!=MSByte_multiple) + ) { + if(thisctr.count_binary==0) { + thisctr.next_change_time=1; + } else { + thisctr.next_change_time=thisctr.count_binary & 0xFFFF; + } + } else { + thisctr.next_change_time=0; + } + } + } else { + if(thisctr.null_count) { + thisctr.next_change_time=1; + } else { + thisctr.next_change_time=0; + } + } + break; + case 1: + if(data && thisctr.count_written) { //only triggers cause a change. + thisctr.next_change_time=1; + } + break; + case 2: + if(!data) { + set_OUT(thisctr,1); + thisctr.next_change_time=0; + } else { + if(thisctr.count_written) { + thisctr.next_change_time=1; + } else { + thisctr.next_change_time=0; + } + } + break; + case 3: + if(!data) { + set_OUT(thisctr,1); + thisctr.first_pass=1; + thisctr.next_change_time=0; + } else { + if(thisctr.count_written) { + thisctr.next_change_time=1; + } else { + thisctr.next_change_time=0; + } + } + break; + case 4: + if(!thisctr.OUTpin || thisctr.null_count) { + thisctr.next_change_time=1; + } else { + if(data && thisctr.count_written) { + if(thisctr.first_pass) { + if(thisctr.count_binary==0) { + thisctr.next_change_time=1; + } else { + thisctr.next_change_time=thisctr.count_binary & 0xFFFF; + } + } else { + thisctr.next_change_time=0; + } + } else { + thisctr.next_change_time=0; + } + } + break; + case 5: + if(data && thisctr.count_written) { //only triggers cause a change. + thisctr.next_change_time=1; + } + break; + default: + break; + } + } + } + } + + bool pit_82C54::read_OUT(Bit8u cnum) { + if(cnum>MAX_COUNTER) { + BX_ERROR(("Counter number incorrect in 82C54 read_OUT")); + return 0; + } else { + return counter[cnum].OUTpin; + } + } + + bool pit_82C54::read_GATE(Bit8u cnum) { + if(cnum>MAX_COUNTER) { + BX_ERROR(("Counter number incorrect in 82C54 read_GATE")); + return 0; + } else { + return counter[cnum].GATE; + } + } + +Bit32u pit_82C54::get_clock_event_time(Bit8u cnum) { + if(cnum>MAX_COUNTER) { + BX_ERROR(("Counter number incorrect in 82C54 read_GATE")); + return 0; + } else { + return counter[cnum].next_change_time; + } +} + +Bit32u pit_82C54::get_next_event_time(void) { + Bit32u out; + Bit32u time0=get_clock_event_time(0); + Bit32u time1=get_clock_event_time(1); + Bit32u time2=get_clock_event_time(2); + + out=time0; + if(time1 && (time1<out)) + out=time1; + if(time2 && (time2<out)) + out=time2; + return out; +} diff --git a/tools/ioemu/iodev/pit82c54.h b/tools/ioemu/iodev/pit82c54.h new file mode 100644 index 0000000000..95d36b3973 --- /dev/null +++ b/tools/ioemu/iodev/pit82c54.h @@ -0,0 +1,143 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pit82c54.h,v 1.12 2003/03/02 23:59:11 cbothamy Exp $ +///////////////////////////////////////////////////////////////////////// +// +/* + * Emulator of an Intel 8254/82C54 Programmable Interval Timer. + * Greg Alexander <yakovlev@usa.com> + * + * This code is not yet linked into Bochs, but has been included so + * that you can experiment with it. (bbd) + */ + +#ifndef _PIT_82C54_H_ +#define _PIT_82C54_H_ 1 + +#include "bochs.h" + +#ifdef BX_USE_VMX +#define BX_VMX_PIT 1 +#define PIT_FREQ 1193181 +#endif + + +class pit_82C54 : public logfunctions { + +public: + //Please do not use these. They are public because they have to be + // to compile on some platforms. They are not to be used by other + // classes. + + enum rw_status { + LSByte=0, + MSByte=1, + LSByte_multiple=2, + MSByte_multiple=3 + }; + +private: + + enum { + MAX_COUNTER=2, + MAX_ADDRESS=3, + CONTROL_ADDRESS=3, + MAX_MODE=5 + }; + + enum real_RW_status { + LSB_real=1, + MSB_real=2, + BOTH_real=3 + }; + + enum problem_type { + UNL_2P_READ=1 + }; + + struct counter_type { + //Chip IOs; + bool GATE; //GATE Input value at end of cycle + bool OUTpin; //OUT output this cycle + + //Architected state; + Bit32u count; //Counter value this cycle + Bit16u outlatch; //Output latch this cycle + Bit16u inlatch; //Input latch this cycle + Bit8u status_latch; + + //Status Register data; + Bit8u rw_mode; //2-bit R/W mode from command word register. + Bit8u mode; //3-bit mode from command word register. + bool bcd_mode; //1-bit BCD vs. Binary setting. + bool null_count; //Null count bit of status register. + + //Latch status data; + bool count_LSB_latched; + bool count_MSB_latched; + bool status_latched; + + //Miscelaneous State; + Bit32u count_binary; //Value of the count in binary. + bool triggerGATE; //Whether we saw GATE rise this cycle. + rw_status write_state; //Read state this cycle + rw_status read_state; //Read state this cycle + bool count_written; //Whether a count written since programmed + bool first_pass; //Whether or not this is the first loaded count. + bool state_bit_1; //Miscelaneous state bits. + bool state_bit_2; + Bit32u next_change_time; //Next time something besides count changes. + //0 means never. + }; + + counter_type counter[3]; + + Bit8u controlword; + + int seen_problems; + + void latch_counter(counter_type & thisctr); + + void set_OUT (counter_type & thisctr, bool data); + + void set_count (counter_type & thisctr, Bit32u data) BX_CPP_AttrRegparmN(2); + + void set_count_to_binary (counter_type & thisctr) BX_CPP_AttrRegparmN(1); + + void set_binary_to_count (counter_type & thisctr) BX_CPP_AttrRegparmN(1); + + void decrement (counter_type & thisctr) BX_CPP_AttrRegparmN(1); + + void decrement_multiple(counter_type & thisctr, Bit32u cycles) BX_CPP_AttrRegparmN(2); + + void clock(Bit8u cnum) BX_CPP_AttrRegparmN(1); + + void print_counter(counter_type & thisctr); + +#ifdef BX_USE_VMX + void write_initcount_vmx(Bit8u cnum); +#endif + +public: + void init (void); + void reset (unsigned type); + pit_82C54 (void); + + void clock_all(Bit32u cycles); + void clock_multiple(Bit8u cnum, Bit32u cycles); + + Bit8u read(Bit8u address); + void write(Bit8u address, Bit8u data); + + void set_GATE(Bit8u cnum, bool data); + bool read_GATE(Bit8u cnum); + + bool read_OUT(Bit8u cnum); + + Bit32u get_clock_event_time(Bit8u cnum); + Bit32u get_next_event_time(void); + + void print_cnum(Bit8u cnum); + +}; + +#endif diff --git a/tools/ioemu/iodev/pit_wrap.cc b/tools/ioemu/iodev/pit_wrap.cc new file mode 100644 index 0000000000..5fd1721073 --- /dev/null +++ b/tools/ioemu/iodev/pit_wrap.cc @@ -0,0 +1,444 @@ +//////////////////////////////////////////////////////////////////////// +// $Id: pit_wrap.cc,v 1.52 2003/08/19 00:10:38 cbothamy Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + +#include "bochs.h" + +#if BX_USE_NEW_PIT + +#include "pit_wrap.h" + + +//Important constant #defines: +#define USEC_PER_SECOND (1000000) +//1.193181MHz Clock +#define TICKS_PER_SECOND (1193181) + + +// define a macro to convert floating point numbers into 64-bit integers. +// In MSVC++ you can convert a 64-bit float into a 64-bit signed integer, +// but it will not convert a 64-bit float into a 64-bit unsigned integer. +// This macro works around that. +#define F2I(x) ((Bit64u)(Bit64s) (x)) +#define I2F(x) ((double)(Bit64s) (x)) + +//DEBUG configuration: + +//Set up Logging. +#define LOG_THIS bx_pit. + +//A single instance. +bx_pit_c bx_pit; +#if BX_USE_PIT_SMF +#define this (&bx_pit) +#endif + +//Workaround for environments where OUT is defined. +#ifdef OUT +# undef OUT +#endif + + +//Generic MAX and MIN Functions +#define BX_MAX(a,b) ( ((a)>(b))?(a):(b) ) +#define BX_MIN(a,b) ( ((a)>(b))?(b):(a) ) + + +//USEC_ALPHA is multiplier for the past. +//USEC_ALPHA_B is 1-USEC_ALPHA, or multiplier for the present. +#define USEC_ALPHA ((double)(.8)) +#define USEC_ALPHA_B ((double)(((double)1)-USEC_ALPHA)) +#define USEC_ALPHA2 ((double)(.5)) +#define USEC_ALPHA2_B ((double)(((double)1)-USEC_ALPHA2)) +#define ALPHA_LOWER(old,new) ((Bit64u)((old<new)?((USEC_ALPHA*(I2F(old)))+(USEC_ALPHA_B*(I2F(new)))):((USEC_ALPHA2*(I2F(old)))+(USEC_ALPHA2_B*(I2F(new)))))) + + +//PIT tick to usec conversion functions: +//Direct conversions: +#define TICKS_TO_USEC(a) ( ((a)*USEC_PER_SECOND)/TICKS_PER_SECOND ) +#define USEC_TO_TICKS(a) ( ((a)*TICKS_PER_SECOND)/USEC_PER_SECOND ) + + +bx_pit_c::bx_pit_c( void ) +{ + put("PIT"); + settype(PITLOG); + s.speaker_data_on=0; + + /* 8254 PIT (Programmable Interval Timer) */ + + BX_PIT_THIS s.timer_handle[1] = BX_NULL_TIMER_HANDLE; + BX_PIT_THIS s.timer_handle[2] = BX_NULL_TIMER_HANDLE; + BX_PIT_THIS s.timer_handle[0] = BX_NULL_TIMER_HANDLE; +} + +bx_pit_c::~bx_pit_c( void ) +{ +} + + int +bx_pit_c::init( void ) +{ + DEV_register_irq(0, "8254 PIT"); + DEV_register_ioread_handler(this, read_handler, 0x0040, "8254 PIT", 1); + DEV_register_ioread_handler(this, read_handler, 0x0041, "8254 PIT", 1); + DEV_register_ioread_handler(this, read_handler, 0x0042, "8254 PIT", 1); + DEV_register_ioread_handler(this, read_handler, 0x0043, "8254 PIT", 1); + DEV_register_ioread_handler(this, read_handler, 0x0061, "8254 PIT", 1); + + DEV_register_iowrite_handler(this, write_handler, 0x0040, "8254 PIT", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0041, "8254 PIT", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0042, "8254 PIT", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0043, "8254 PIT", 1); + DEV_register_iowrite_handler(this, write_handler, 0x0061, "8254 PIT", 1); + + BX_DEBUG(("pit: starting init")); + + BX_PIT_THIS s.speaker_data_on = 0; + BX_PIT_THIS s.refresh_clock_div2 = 0; + + BX_PIT_THIS s.timer.init(); + + Bit64u my_time_usec = bx_virt_timer.time_usec(); + + if (BX_PIT_THIS s.timer_handle[0] == BX_NULL_TIMER_HANDLE) { + BX_PIT_THIS s.timer_handle[0] = bx_virt_timer.register_timer(this, timer_handler, (unsigned) 100 , 1, 1, "pit_wrap"); + } + BX_DEBUG(("pit: RESETting timer.")); + bx_virt_timer.deactivate_timer(BX_PIT_THIS s.timer_handle[0]); + BX_DEBUG(("deactivated timer.")); + if(BX_PIT_THIS s.timer.get_next_event_time()) { + bx_virt_timer.activate_timer(BX_PIT_THIS s.timer_handle[0], + (Bit32u)BX_MAX(1,TICKS_TO_USEC(BX_PIT_THIS s.timer.get_next_event_time())), + 0); + BX_DEBUG(("activated timer.")); + } + BX_PIT_THIS s.last_next_event_time = BX_PIT_THIS s.timer.get_next_event_time(); + BX_PIT_THIS s.last_usec=my_time_usec; + + BX_PIT_THIS s.total_ticks=0; + BX_PIT_THIS s.total_usec=0; + + BX_DEBUG(("pit: finished init")); + + BX_DEBUG(("s.last_usec="FMT_LL"d",BX_PIT_THIS s.last_usec)); + BX_DEBUG(("s.timer_id=%d",BX_PIT_THIS s.timer_handle[0])); + BX_DEBUG(("s.timer.get_next_event_time=%d",BX_PIT_THIS s.timer.get_next_event_time())); + BX_DEBUG(("s.last_next_event_time=%d",BX_PIT_THIS s.last_next_event_time)); + + return(1); +} + + void +bx_pit_c::reset(unsigned type) +{ +} + +void +bx_pit_c::timer_handler(void *this_ptr) { + bx_pit_c * class_ptr = (bx_pit_c *) this_ptr; + + class_ptr->handle_timer(); +} + +void +bx_pit_c::handle_timer() { + Bit64u my_time_usec = bx_virt_timer.time_usec(); + Bit64u time_passed = my_time_usec-BX_PIT_THIS s.last_usec; + Bit32u time_passed32 = (Bit32u)time_passed; + + BX_DEBUG(("pit: entering timer handler")); + + if(time_passed32) { + periodic(time_passed32); + } + BX_PIT_THIS s.last_usec=BX_PIT_THIS s.last_usec + time_passed; + if(time_passed || + (BX_PIT_THIS s.last_next_event_time + != BX_PIT_THIS s.timer.get_next_event_time()) + ) { + BX_DEBUG(("pit: RESETting timer.")); + bx_virt_timer.deactivate_timer(BX_PIT_THIS s.timer_handle[0]); + BX_DEBUG(("deactivated timer.")); + if(BX_PIT_THIS s.timer.get_next_event_time()) { + bx_virt_timer.activate_timer(BX_PIT_THIS s.timer_handle[0], + (Bit32u)BX_MAX(1,TICKS_TO_USEC(BX_PIT_THIS s.timer.get_next_event_time())), + 0); + BX_DEBUG(("activated timer.")); + } + BX_PIT_THIS s.last_next_event_time = BX_PIT_THIS s.timer.get_next_event_time(); + } + BX_DEBUG(("s.last_usec="FMT_LL"d",BX_PIT_THIS s.last_usec)); + BX_DEBUG(("s.timer_id=%d",BX_PIT_THIS s.timer_handle[0])); + BX_DEBUG(("s.timer.get_next_event_time=%x",BX_PIT_THIS s.timer.get_next_event_time())); + BX_DEBUG(("s.last_next_event_time=%d",BX_PIT_THIS s.last_next_event_time)); +} + + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_pit_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_PIT_SMF + bx_pit_c *class_ptr = (bx_pit_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + Bit32u +bx_pit_c::read( Bit32u address, unsigned int io_len ) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PIT_SMF + BX_DEBUG(("pit: entering read handler")); + + handle_timer(); + + Bit64u my_time_usec = bx_virt_timer.time_usec(); + + if (bx_dbg.pit) + BX_INFO(("pit: io read from port %04x", (unsigned) address)); + + switch (address) { + + case 0x40: /* timer 0 - system ticks */ + return(BX_PIT_THIS s.timer.read(0)); + break; + case 0x41: /* timer 1 read */ + return(BX_PIT_THIS s.timer.read(1)); + break; + case 0x42: /* timer 2 read */ + return(BX_PIT_THIS s.timer.read(2)); + break; + case 0x43: /* timer 1 read */ + return(BX_PIT_THIS s.timer.read(3)); + break; + + case 0x61: + /* AT, port 61h */ + BX_PIT_THIS s.refresh_clock_div2 = (bx_bool)((my_time_usec / 15) & 1); + return( (BX_PIT_THIS s.timer.read_OUT(2)<<5) | + (BX_PIT_THIS s.refresh_clock_div2<<4) | + (BX_PIT_THIS s.speaker_data_on<<1) | + (BX_PIT_THIS s.timer.read_GATE(2)?1:0) ); + break; + + default: + BX_PANIC(("pit: unsupported io read from port %04x", address)); + } + return(0); /* keep compiler happy */ +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_pit_c::write_handler(void *this_ptr, Bit32u address, Bit32u dvalue, unsigned io_len) +{ +#if !BX_USE_PIT_SMF + bx_pit_c *class_ptr = (bx_pit_c *) this_ptr; + + class_ptr->write(address, dvalue, io_len); +} + + void +bx_pit_c::write( Bit32u address, Bit32u dvalue, + unsigned int io_len ) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_PIT_SMF + Bit8u value; + Bit64u my_time_usec = bx_virt_timer.time_usec(); + Bit64u time_passed = my_time_usec-BX_PIT_THIS s.last_usec; + Bit32u time_passed32 = (Bit32u)time_passed; + + BX_DEBUG(("pit: entering write handler")); + + if(time_passed32) { + periodic(time_passed32); + } + BX_PIT_THIS s.last_usec=BX_PIT_THIS s.last_usec + time_passed; + + value = (Bit8u ) dvalue; + + if (bx_dbg.pit) + BX_INFO(("pit: write to port %04x = %02x", + (unsigned) address, (unsigned) value)); + + switch (address) { + case 0x40: /* timer 0: write count register */ + BX_PIT_THIS s.timer.write(0,value); + break; + + case 0x41: /* timer 1: write count register */ + BX_PIT_THIS s.timer.write( 1,value ); + break; + + case 0x42: /* timer 2: write count register */ + BX_PIT_THIS s.timer.write( 2,value ); + break; + + case 0x43: /* timer 0-2 mode control */ + BX_PIT_THIS s.timer.write( 3,value ); + break; + + case 0x61: + BX_PIT_THIS s.speaker_data_on = (value >> 1) & 0x01; +/*??? only on AT+ */ + BX_PIT_THIS s.timer.set_GATE(2, value & 0x01); +#if BX_CPU_LEVEL < 2 + /* ??? XT: */ + bx_kbd_port61h_write(value); +#endif + break; + + default: + BX_PANIC(("pit: unsupported io write to port %04x = %02x", + (unsigned) address, (unsigned) value)); + } + +#ifndef BX_VMX_PIT + if ((BX_PIT_THIS s.timer.read_OUT(0))==1) { + DEV_pic_raise_irq(0); + } else { + DEV_pic_lower_irq(0); + } +#endif + + if(time_passed || + (BX_PIT_THIS s.last_next_event_time + != BX_PIT_THIS s.timer.get_next_event_time()) + ) { + BX_DEBUG(("pit: RESETting timer.")); + bx_virt_timer.deactivate_timer(BX_PIT_THIS s.timer_handle[0]); + BX_DEBUG(("deactivated timer.")); + if(BX_PIT_THIS s.timer.get_next_event_time()) { + bx_virt_timer.activate_timer(BX_PIT_THIS s.timer_handle[0], + (Bit32u)BX_MAX(1,TICKS_TO_USEC(BX_PIT_THIS s.timer.get_next_event_time())), + 0); + BX_DEBUG(("activated timer.")); + } + BX_PIT_THIS s.last_next_event_time = BX_PIT_THIS s.timer.get_next_event_time(); + } + BX_DEBUG(("s.last_usec="FMT_LL"d",BX_PIT_THIS s.last_usec)); + BX_DEBUG(("s.timer_id=%d",BX_PIT_THIS s.timer_handle[0])); + BX_DEBUG(("s.timer.get_next_event_time=%x",BX_PIT_THIS s.timer.get_next_event_time())); + BX_DEBUG(("s.last_next_event_time=%d",BX_PIT_THIS s.last_next_event_time)); + +} + + + + + int +bx_pit_c::SaveState( class state_file *fd ) +{ + fd->write_check ("8254 start"); + fd->write (&BX_PIT_THIS s, sizeof (BX_PIT_THIS s)); + fd->write_check ("8254 end"); + return(0); +} + + + int +bx_pit_c::LoadState( class state_file *fd ) +{ + fd->read_check ("8254 start"); + fd->read (&BX_PIT_THIS s, sizeof (BX_PIT_THIS s)); + fd->read_check ("8254 end"); + return(0); +} + + +#if 0 + void +bx_kbd_port61h_write(Bit8u value) +{ +// PcError("KBD_PORT61H_WRITE(): not implemented yet"); + UNUSED( value ); +} +#endif + + + bx_bool +bx_pit_c::periodic( Bit32u usec_delta ) +{ + bx_bool prev_timer0_out = BX_PIT_THIS s.timer.read_OUT(0); + bx_bool want_interrupt = 0; + Bit32u ticks_delta = 0; + +#ifdef BX_SCHEDULED_DIE_TIME + if (bx_pc_system.time_ticks() > BX_SCHEDULED_DIE_TIME) { + BX_ERROR (("ticks exceeded scheduled die time, quitting")); + BX_EXIT (2); + } +#endif + + BX_PIT_THIS s.total_usec += usec_delta; + ticks_delta=(Bit32u)((USEC_TO_TICKS((Bit64u)(BX_PIT_THIS s.total_usec)))-BX_PIT_THIS s.total_ticks); + BX_PIT_THIS s.total_ticks += ticks_delta; + + while ((BX_PIT_THIS s.total_ticks >= TICKS_PER_SECOND) && (BX_PIT_THIS s.total_usec >= USEC_PER_SECOND)) { + BX_PIT_THIS s.total_ticks -= TICKS_PER_SECOND; + BX_PIT_THIS s.total_usec -= USEC_PER_SECOND; + } + + while(ticks_delta>0) { + Bit32u maxchange=BX_PIT_THIS s.timer.get_next_event_time(); + Bit32u timedelta=maxchange; + if((maxchange==0) || (maxchange>ticks_delta)) { + timedelta=ticks_delta; + } + BX_PIT_THIS s.timer.clock_all(timedelta); + if ( (prev_timer0_out==0) ) { + if ((BX_PIT_THIS s.timer.read_OUT(0))==1) { +#ifndef BX_VMX_PIT + DEV_pic_raise_irq(0); +#endif + prev_timer0_out=1; + } + } else { + if ((BX_PIT_THIS s.timer.read_OUT(0))==0) { +#ifndef BX_VMX_PIT + DEV_pic_lower_irq(0); +#endif + prev_timer0_out=0; + } + } + prev_timer0_out=BX_PIT_THIS s.timer.read_OUT(0); + ticks_delta-=timedelta; + } + + return(want_interrupt); +} + +#endif // #if BX_USE_NEW_PIT diff --git a/tools/ioemu/iodev/pit_wrap.h b/tools/ioemu/iodev/pit_wrap.h new file mode 100644 index 0000000000..e45e1e6b57 --- /dev/null +++ b/tools/ioemu/iodev/pit_wrap.h @@ -0,0 +1,104 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: pit_wrap.h,v 1.17 2003/08/19 00:10:38 cbothamy 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 + +#ifndef _BX_PIT_WRAP_H +#define _BX_PIT_WRAP_H + +#include "bochs.h" + +#if BX_USE_NEW_PIT + +#include "pit82c54.h" + +#if BX_USE_PIT_SMF +# define BX_PIT_SMF static +# define BX_PIT_THIS bx_pit. +#else +# define BX_PIT_SMF +# define BX_PIT_THIS this-> +#endif + +#ifdef OUT +# undef OUT +#endif + +class bx_pit_c : public logfunctions { +public: + bx_pit_c( void ); + ~bx_pit_c( void ); + BX_PIT_SMF int init( void ); + BX_PIT_SMF void reset( unsigned type); + BX_PIT_SMF bx_bool periodic( Bit32u usec_delta ); + + BX_PIT_SMF int SaveState( class state_file *fd ); + BX_PIT_SMF int LoadState( class state_file *fd ); + +private: + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#if !BX_USE_PIT_SMF + Bit32u read( Bit32u addr, unsigned int len ); + void write( Bit32u addr, Bit32u Value, unsigned int len ); +#endif + + struct s_type { + pit_82C54 timer; + Bit8u speaker_data_on; + bx_bool refresh_clock_div2; + int timer_handle[3]; + Bit64u last_usec; + Bit32u last_next_event_time; + Bit64u total_ticks; + Bit64u usec_per_second; + Bit64u ticks_per_second; + Bit64u total_sec; + Bit64u last_time; + Bit64u last_sec_usec; + Bit64u max_ticks; + Bit64u stored_delta; + Bit64u total_usec; + Bit64u em_last_realtime; + Bit64u last_realtime_delta; + Bit64u last_realtime_ticks; + } s; + + static void timer_handler(void *this_ptr); + BX_PIT_SMF void handle_timer(); + + BX_PIT_SMF void write_count_reg( Bit8u value, unsigned timerid ); + BX_PIT_SMF Bit8u read_counter( unsigned timerid ); + BX_PIT_SMF void latch( unsigned timerid ); + BX_PIT_SMF void set_GATE(unsigned pit_id, unsigned value); + BX_PIT_SMF void start(unsigned timerid); + + BX_PIT_SMF void second_update_data(void); +}; + +extern bx_pit_c bx_pit; + +#endif // #if BX_USE_NEW_PIT +#endif // #ifndef _BX_PIT_WRAP_H diff --git a/tools/ioemu/iodev/plugin.cc b/tools/ioemu/iodev/plugin.cc new file mode 100644 index 0000000000..136065d9d8 --- /dev/null +++ b/tools/ioemu/iodev/plugin.cc @@ -0,0 +1,554 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: plugin.cc,v 1.8 2003/07/31 12:04:47 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// This file defines the plugin and plugin-device registration functions and +// the device registration functions. It handles dynamic loading of modules, +// using the LTDL library for cross-platform support. +// +// This file is based on the plugin.c file from plex86, but with significant +// changes to make it work in Bochs. +// Plex86 is Copyright (C) 1999-2000 The plex86 developers team +// +///////////////////////////////////////////////////////////////////////// + +#include "bochs.h" +#include "plugin.h" + +#define LOG_THIS genlog-> + +#define PLUGIN_INIT_FMT_STRING "lib%s_LTX_plugin_init" +#define PLUGIN_FINI_FMT_STRING "lib%s_LTX_plugin_fini" +#define PLUGIN_PATH "" + +#ifndef WIN32 +#define PLUGIN_FILENAME_FORMAT "libbx_%s.la" +#else +#define PLUGIN_FILENAME_FORMAT "bx_%s.dll" +#endif + + + +void (*pluginRegisterIRQ)(unsigned irq, const char* name) = 0; +void (*pluginUnregisterIRQ)(unsigned irq, const char* name) = 0; + +void (* pluginResetSignal)(unsigned sig) = 0; + +void (*pluginSetHRQ)(unsigned val) = 0; +void (*pluginSetHRQHackCallback)( void (*callback)(void) ) = 0; + +int (*pluginRegisterIOReadHandler)(void *thisPtr, ioReadHandler_t callback, + unsigned base, const char *name, Bit8u mask) = 0; +int (*pluginRegisterIOWriteHandler)(void *thisPtr, ioWriteHandler_t callback, + unsigned base, const char *name, Bit8u mask) = 0; +int (*pluginRegisterDefaultIOReadHandler)(void *thisPtr, ioReadHandler_t callback, + const char *name, Bit8u mask) = 0; +int (*pluginRegisterDefaultIOWriteHandler)(void *thisPtr, ioWriteHandler_t callback, + const char *name, Bit8u mask) = 0; +int (*pluginRegisterTimer)(void *this_ptr, void (*funct)(void *), + Bit32u useconds, bx_bool continuous, + bx_bool active, const char* name) = 0; +void (*pluginActivateTimer)(unsigned id, Bit32u usec, bx_bool continuous) = 0; + +void (*pluginHRQHackCallback)(void); +unsigned pluginHRQ = 0; + +plugin_t *plugins = NULL; /* Head of the linked list of plugins */ +#if BX_PLUGINS +static void plugin_init_one(plugin_t *plugin); +#endif + +device_t *devices = NULL; /* Head of the linked list of registered devices */ + +plugin_t *current_plugin_context = NULL; + +/************************************************************************/ +/* Builtins declarations */ +/************************************************************************/ + + static void +builtinRegisterIRQ(unsigned irq, const char* name) +{ +#if 0 + pluginlog->panic("builtinRegisterIRQ called, no pic plugin loaded?"); +#else + bx_devices.register_irq(irq, name); +#endif +} + + static void +builtinUnregisterIRQ(unsigned irq, const char* name) +{ +#if 0 + pluginlog->panic("builtinUnregisterIRQ called, no pic plugin loaded?"); +#else + bx_devices.unregister_irq(irq, name); +#endif +} + + static void +builtinSetHRQ(unsigned val) +{ +#if 0 + pluginlog->panic("builtinSetHRQ called, no plugin loaded?"); +#else + pluginHRQ = val; +#endif +} + + static void +builtinSetHRQHackCallback( void (*callback)(void) ) +{ +#if 0 + pluginlog->panic("builtinSetHRQHackCallback called, no plugin loaded?"); +#else + pluginHRQHackCallback = callback; +#endif +} + + static void +builtinResetSignal( unsigned ) +{ + pluginlog->panic("builtinResetSignal called, no plugin loaded?"); +} + + static int +builtinRegisterIOReadHandler(void *thisPtr, ioReadHandler_t callback, + unsigned base, const char *name, Bit8u mask) +{ + BX_ASSERT (mask<8); + bx_devices.register_io_read_handler (thisPtr, callback, base, name, mask); + pluginlog->ldebug("plugin %s registered I/O read address at %04x", name, base); + return 0; +} + + static int +builtinRegisterIOWriteHandler(void *thisPtr, ioWriteHandler_t callback, + unsigned base, const char *name, Bit8u mask) +{ + BX_ASSERT (mask<8); + bx_devices.register_io_write_handler (thisPtr, callback, base, name, mask); + pluginlog->ldebug("plugin %s registered I/O write address at %04x", name, base); + return 0; +} + + static int +builtinRegisterDefaultIOReadHandler(void *thisPtr, ioReadHandler_t callback, + const char *name, Bit8u mask) +{ + BX_ASSERT (mask<8); + bx_devices.register_default_io_read_handler (thisPtr, callback, name, mask); + pluginlog->ldebug("plugin %s registered default I/O read ", name); + return 0; +} + + static int +builtinRegisterDefaultIOWriteHandler(void *thisPtr, ioWriteHandler_t callback, + const char *name, Bit8u mask) +{ + BX_ASSERT (mask<8); + bx_devices.register_default_io_write_handler (thisPtr, callback, name, mask); + pluginlog->ldebug("plugin %s registered default I/O write ", name); + return 0; +} + + static int +builtinRegisterTimer(void *this_ptr, void (*funct)(void *), + Bit32u useconds, bx_bool continuous, + bx_bool active, const char* name) +{ + int id = bx_pc_system.register_timer (this_ptr, funct, useconds, continuous, active, name); + pluginlog->ldebug("plugin %s registered timer %d", name, id); + return id; +} + + static void +builtinActivateTimer(unsigned id, Bit32u usec, bx_bool continuous) +{ + bx_pc_system.activate_timer (id, usec, continuous); + pluginlog->ldebug("plugin activated timer %d", id); +} + +#if BX_PLUGINS +/************************************************************************/ +/* Plugin initialization / deinitialization */ +/************************************************************************/ + + void +plugin_init_all (void) +{ + plugin_t *plugin; + + pluginlog->info("Initializing plugins"); + + for (plugin = plugins; plugin; plugin = plugin->next) + { + char *arg_ptr = plugin->args; + + /* process the command line */ + plugin->argc = 0; + while (plugin->argc < MAX_ARGC) + { + while (*arg_ptr && isspace (*arg_ptr)) + arg_ptr++; + + if (!*arg_ptr) + break; + plugin->argv[plugin->argc++] = arg_ptr; + + while (*arg_ptr && !isspace (*arg_ptr)) + arg_ptr++; + + if (!*arg_ptr) + break; + *arg_ptr++ = '\0'; + } + + /* initialize the plugin */ + if (plugin->plugin_init (plugin, plugin->type, plugin->argc, plugin->argv)) + { + pluginlog->panic("Plugin initialization failed for %s", plugin->name); + plugin_abort(); + } + + plugin->initialized = 1; + } + + return; +} + +void +plugin_init_one(plugin_t *plugin) +{ + char *arg_ptr = plugin->args; + + /* process the command line */ + plugin->argc = 0; + while (plugin->argc < MAX_ARGC) + { + while (*arg_ptr && isspace (*arg_ptr)) + arg_ptr++; + + if (!*arg_ptr) + break; + plugin->argv[plugin->argc++] = arg_ptr; + + while (*arg_ptr && !isspace (*arg_ptr)) + arg_ptr++; + + if (!*arg_ptr) + break; + *arg_ptr++ = '\0'; + } + + /* initialize the plugin */ + if (plugin->plugin_init (plugin, plugin->type, plugin->argc, plugin->argv)) + { + pluginlog->info("Plugin initialization failed for %s", plugin->name); + plugin_abort(); + } + + plugin->initialized = 1; +} + + + plugin_t * +plugin_unload(plugin_t *plugin) +{ + plugin_t *dead_plug; + + if (plugin->initialized) + plugin->plugin_fini (); + + lt_dlclose (plugin->handle); + free (plugin->name); + free (plugin->args); + + dead_plug = plugin; + plugin = plugin->next; + free (dead_plug); + + return plugin; +} + + +void +plugin_fini_all (void) +{ + plugin_t *plugin; + + for (plugin = plugins; plugin; plugin = plugin_unload (plugin)); + + return; +} + + void +plugin_load (char *name, char *args, plugintype_t type) +{ + plugin_t *plugin; + + plugin = (plugin_t *)malloc (sizeof (plugin_t)); + if (!plugin) + { + BX_PANIC (("malloc plugin_t failed")); + } + + plugin->type = type; + plugin->name = name; + plugin->args = args; + plugin->initialized = 0; + + char plugin_filename[BX_PATHNAME_LEN], buf[BX_PATHNAME_LEN]; + sprintf (buf, PLUGIN_FILENAME_FORMAT, name); + sprintf(plugin_filename, "%s%s", PLUGIN_PATH, buf); + + // Set context so that any devices that the plugin registers will + // be able to see which plugin created them. The registration will + // be called from either dlopen (global constructors) or plugin_init. + BX_ASSERT (current_plugin_context == NULL); + current_plugin_context = plugin; + plugin->handle = lt_dlopen (plugin_filename); + BX_INFO (("lt_dlhandle is %p", plugin->handle)); + if (!plugin->handle) + { + current_plugin_context = NULL; + BX_PANIC (("dlopen failed for module '%s': %s", name, lt_dlerror ())); + free (plugin); + return; + } + + sprintf (buf, PLUGIN_INIT_FMT_STRING, name); + plugin->plugin_init = + (int (*)(struct _plugin_t *, enum plugintype_t, int, char *[])) /* monster typecast */ + lt_dlsym (plugin->handle, buf); + if (plugin->plugin_init == NULL) { + pluginlog->panic("could not find plugin_init: %s", lt_dlerror ()); + plugin_abort (); + } + + sprintf (buf, PLUGIN_FINI_FMT_STRING, name); + plugin->plugin_fini = (void (*)(void)) lt_dlsym (plugin->handle, buf); + if (plugin->plugin_init == NULL) { + pluginlog->panic("could not find plugin_fini: %s", lt_dlerror ()); + plugin_abort (); + } + pluginlog->info("loaded plugin %s",plugin_filename); + + + /* Insert plugin at the _end_ of the plugin linked list. */ + plugin->next = NULL; + + if (!plugins) + { + /* Empty list, this become the first entry. */ + plugins = plugin; + } + else + { + /* Non-empty list. Add to end. */ + plugin_t *temp = plugins; + + while (temp->next) + temp = temp->next; + + temp->next = plugin; + } + + plugin_init_one(plugin); + + // check that context didn't change. This should only happen if we + // need a reentrant plugin_load. + BX_ASSERT (current_plugin_context == plugin); + current_plugin_context = NULL; + + return; +} + +void +plugin_abort (void) +{ + pluginlog->panic("plugin load aborted"); +} + +#endif /* end of #if BX_PLUGINS */ + +/************************************************************************/ +/* Plugin system: initialisation of plugins entry points */ +/************************************************************************/ + + void +plugin_startup(void) +{ + pluginRegisterIRQ = builtinRegisterIRQ; + pluginUnregisterIRQ = builtinUnregisterIRQ; + + pluginResetSignal = builtinResetSignal; + + pluginSetHRQHackCallback = builtinSetHRQHackCallback; + pluginSetHRQ = builtinSetHRQ; + + pluginRegisterIOReadHandler = builtinRegisterIOReadHandler; + pluginRegisterIOWriteHandler = builtinRegisterIOWriteHandler; + + pluginRegisterDefaultIOReadHandler = builtinRegisterDefaultIOReadHandler; + pluginRegisterDefaultIOWriteHandler = builtinRegisterDefaultIOWriteHandler; + + pluginRegisterTimer = builtinRegisterTimer; + pluginActivateTimer = builtinActivateTimer; + +#if BX_PLUGINS + pluginlog = new logfunctions(); + pluginlog->put("PLGIN"); + pluginlog->settype(PLUGINLOG); + int status = lt_dlinit (); + if (status != 0) { + BX_ERROR (("initialization error in ltdl library (for loading plugins)")); + BX_PANIC (("error message was: %s", lt_dlerror ())); + } +#endif +} + + +/************************************************************************/ +/* Plugin system: Device registration */ +/************************************************************************/ + +void pluginRegisterDeviceDevmodel(plugin_t *plugin, plugintype_t type, bx_devmodel_c *devmodel, char *name) +{ + device_t *device; + + device = (device_t *)malloc (sizeof (device_t)); + if (!device) + { + pluginlog->panic("can't allocate device_t"); + } + + device->name = name; + BX_ASSERT (devmodel != NULL); + device->devmodel = devmodel; + device->plugin = plugin; // this can be NULL + device->use_devmodel_interface = 1; + device->device_init_mem = NULL; // maybe should use 1 to detect any use? + device->device_init_dev = NULL; + device->device_reset = NULL; + device->device_load_state = NULL; + device->device_save_state = NULL; + device->next = NULL; + + // Don't add every kind of device to the list. + switch (type) { + case PLUGTYPE_CORE: + // Core devices are present whether or not we are using plugins, so + // they are managed by the same code in iodev/devices.cc whether + // plugins are on or off. + return; // Do not add core devices to the devices list. + case PLUGTYPE_OPTIONAL: + case PLUGTYPE_USER: + default: + // The plugin system will manage optional and user devices only. + break; + } + + if (!devices) + { + /* Empty list, this become the first entry. */ + devices = device; + } + else + { + /* Non-empty list. Add to end. */ + device_t *temp = devices; + + while (temp->next) + temp = temp->next; + + temp->next = device; + } +} + +/************************************************************************/ +/* Plugin system: Check if a plugin is loaded */ +/************************************************************************/ + +bx_bool pluginDevicePresent(char *name) +{ + device_t *device; + + for (device = devices; device; device = device->next) + { + if (strcmp(device->name,name)==0) return true; + } + + return false; +} + +#if BX_PLUGINS +/************************************************************************/ +/* Plugin system: Load one plugin */ +/************************************************************************/ + +int bx_load_plugin (const char *name, plugintype_t type) +{ + char *namecopy = new char[1+strlen(name)]; + strcpy (namecopy, name); + plugin_load (namecopy, "", type); + return 0; +} +#endif /* end of #if BX_PLUGINS */ + +/*************************************************************************/ +/* Plugin system: Execute init function of all registered plugin-devices */ +/*************************************************************************/ + +void bx_init_plugins() +{ + device_t *device; + + // two loops + for (device = devices; device; device = device->next) + { + if (!device->use_devmodel_interface) { + if (device->device_init_mem != NULL) { + pluginlog->info("init_mem of '%s' plugin device by function pointer",device->name); + device->device_init_mem(BX_MEM(0)); + } + } else { + pluginlog->info("init_mem of '%s' plugin device by virtual method",device->name); + device->devmodel->init_mem (BX_MEM(0)); + } + } + + for (device = devices; device; device = device->next) + { + if (!device->use_devmodel_interface) { + if (device->device_init_dev != NULL) { + pluginlog->info("init_dev of '%s' plugin device by function pointer",device->name); + device->device_init_dev(); + } + } else { + pluginlog->info("init_dev of '%s' plugin device by virtual method",device->name); + device->devmodel->init (); + } + } +} + +/**************************************************************************/ +/* Plugin system: Execute reset function of all registered plugin-devices */ +/**************************************************************************/ + +void bx_reset_plugins(unsigned signal) +{ + device_t *device; + for (device = devices; device; device = device->next) + { + if (!device->use_devmodel_interface) { + if (device->device_reset != NULL) { + pluginlog->info("reset of '%s' plugin device by function pointer",device->name); + device->device_reset(signal); + } + } else { + pluginlog->info("reset of '%s' plugin device by virtual method",device->name); + device->devmodel->reset (signal); + } + } +} diff --git a/tools/ioemu/iodev/scancodes.cc b/tools/ioemu/iodev/scancodes.cc new file mode 100644 index 0000000000..63efed45f6 --- /dev/null +++ b/tools/ioemu/iodev/scancodes.cc @@ -0,0 +1,770 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: scancodes.cc,v 1.5 2002/10/24 21:07:51 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 MandrakeSoft S.A. +// +// 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 BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#include "scancodes.h" + +unsigned char translation8042[256] = { + 0xff,0x43,0x41,0x3f,0x3d,0x3b,0x3c,0x58,0x64,0x44,0x42,0x40,0x3e,0x0f,0x29,0x59, + 0x65,0x38,0x2a,0x70,0x1d,0x10,0x02,0x5a,0x66,0x71,0x2c,0x1f,0x1e,0x11,0x03,0x5b, + 0x67,0x2e,0x2d,0x20,0x12,0x05,0x04,0x5c,0x68,0x39,0x2f,0x21,0x14,0x13,0x06,0x5d, + 0x69,0x31,0x30,0x23,0x22,0x15,0x07,0x5e,0x6a,0x72,0x32,0x24,0x16,0x08,0x09,0x5f, + 0x6b,0x33,0x25,0x17,0x18,0x0b,0x0a,0x60,0x6c,0x34,0x35,0x26,0x27,0x19,0x0c,0x61, + 0x6d,0x73,0x28,0x74,0x1a,0x0d,0x62,0x6e,0x3a,0x36,0x1c,0x1b,0x75,0x2b,0x63,0x76, + 0x55,0x56,0x77,0x78,0x79,0x7a,0x0e,0x7b,0x7c,0x4f,0x7d,0x4b,0x47,0x7e,0x7f,0x6f, + 0x52,0x53,0x50,0x4c,0x4d,0x48,0x01,0x45,0x57,0x4e,0x51,0x4a,0x37,0x49,0x46,0x54, + 0x80,0x81,0x82,0x41,0x54,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f, + 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf, + 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf, + 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf, + 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf, + 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef, + 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff + }; + + +// Definition of scancodes make and break, +// for each set (mf1/xt , mf2/at , mf3/ps2) +// The table must be in BX_KEY order +// +scancode scancodes[BX_KEY_NBKEYS][3] = +{ + { // BX_KEY_CTRL_L ( ibm 58) + { "\x1D" , "\x9D" }, + { "\x14" , "\xF0\x14" }, + { "\x11" , "\xF0\x11" }, + }, + + { // BX_KEY_SHIFT_L ( ibm 44) + { "\x2A" , "\xAA" }, + { "\x12" , "\xF0\x12" }, + { "\x12" , "\xF0\x12" }, + }, + + { // BX_KEY_F1 ( ibm 112 ) + { "\x3B" , "\xBB" }, + { "\x05" , "\xF0\x05" }, + { "\x07" , "\xF0\x07" }, + }, + + { // BX_KEY_F2 ( ibm 113 ) + { "\x3C" , "\xBC" }, + { "\x06" , "\xF0\x06" }, + { "\x0F" , "\xF0\x0F" }, + }, + + { // BX_KEY_F3 ( ibm 114 ) + { "\x3D" , "\xBD" }, + { "\x04" , "\xF0\x04" }, + { "\x17" , "\xF0\x17" }, + }, + + { // BX_KEY_F4 ( ibm 115 ) + { "\x3E" , "\xBE" }, + { "\x0C" , "\xF0\x0C" }, + { "\x1F" , "\xF0\x1F" }, + }, + + { // BX_KEY_F5 ( ibm 116 ) + { "\x3F" , "\xBF" }, + { "\x03" , "\xF0\x03" }, + { "\x27" , "\xF0\x27" }, + }, + + { // BX_KEY_F6 ( ibm 117 ) + { "\x40" , "\xC0" }, + { "\x0B" , "\xF0\x0B" }, + { "\x2F" , "\xF0\x2F" }, + }, + + { // BX_KEY_F7 ( ibm 118 ) + { "\x41" , "\xC1" }, + { "\x83" , "\xF0\x83" }, + { "\x37" , "\xF0\x37" }, +}, + + { // BX_KEY_F8 ( ibm 119 ) + { "\x42" , "\xC2" }, + { "\x0A" , "\xF0\x0A" }, + { "\x3F" , "\xF0\x3F" }, + }, + + { // BX_KEY_F9 ( ibm 120 ) + { "\x43" , "\xC3" }, + { "\x01" , "\xF0\x01" }, + { "\x47" , "\xF0\x47" }, + }, + + { // BX_KEY_F10 ( ibm 121 ) + { "\x44" , "\xC4" }, + { "\x09" , "\xF0\x09" }, + { "\x4F" , "\xF0\x4F" }, + }, + + { // BX_KEY_F11 ( ibm 122 ) + { "\x57" , "\xD7" }, + { "\x78" , "\xF0\x78" }, + { "\x56" , "\xF0\x56" }, + }, + + { // BX_KEY_F12 ( ibm 123 ) + { "\x58" , "\xD8" }, + { "\x07" , "\xF0\x07" }, + { "\x5E" , "\xF0\x5E" }, + }, + + { // BX_KEY_CTRL_R ( ibm 64 ) + { "\xE0\x1D" , "\xE0\x9D" }, + { "\xE0\x14" , "\xE0\xF0\x14" }, + { "\x58" , "\xF0x58" }, + }, + + { // BX_KEY_SHIFT_R ( ibm 57 ) + { "\x36" , "\xB6" }, + { "\x59" , "\xF0\x59" }, + { "\x59" , "\xF0\x59" }, + }, + + { // BX_KEY_CAPS_LOCK ( ibm 30 ) + { "\x3A" , "\xBA" }, + { "\x58" , "\xF0\x58" }, + { "\x14" , "\xF0\x14" }, + }, + + { // BX_KEY_NUM_LOCK ( ibm 90 ) + { "\x45" , "\xC5" }, + { "\x77" , "\xF0\x77" }, + { "\x76" , "\xF0\x76" }, + }, + + { // BX_KEY_ALT_L ( ibm 60 ) + { "\x38" , "\xB8" }, + { "\x11" , "\xF0\x11" }, + { "\x19" , "\xF0\x19" }, + }, + + { // BX_KEY_ALT_R ( ibm 62 ) + { "\xE0\x38" , "\xE0\xB8" }, + { "\xE0\x11" , "\xE0\xF0\x11" }, + { "\x39" , "\xF0\x39" }, + }, + + { // BX_KEY_A ( ibm 31 ) + { "\x1E" , "\x9E" }, + { "\x1C" , "\xF0\x1C" }, + { "\x1C" , "\xF0\x1C" }, + }, + + { // BX_KEY_B ( ibm 50 ) + { "\x30" , "\xB0" }, + { "\x32" , "\xF0\x32" }, + { "\x32" , "\xF0\x32" }, + }, + + { // BX_KEY_C ( ibm 48 ) + { "\x2E" , "\xAE" }, + { "\x21" , "\xF0\x21" }, + { "\x21" , "\xF0\x21" }, + }, + + { // BX_KEY_D ( ibm 33 ) + { "\x20" , "\xA0" }, + { "\x23" , "\xF0\x23" }, + { "\x23" , "\xF0\x23" }, + }, + + { // BX_KEY_E ( ibm 19 ) + { "\x12" , "\x92" }, + { "\x24" , "\xF0\x24" }, + { "\x24" , "\xF0\x24" }, + }, + + { // BX_KEY_F ( ibm 34 ) + { "\x21" , "\xA1" }, + { "\x2B" , "\xF0\x2B" }, + { "\x2B" , "\xF0\x2B" }, + }, + + { // BX_KEY_G ( ibm 35 ) + { "\x22" , "\xA2" }, + { "\x34" , "\xF0\x34" }, + { "\x34" , "\xF0\x34" }, + }, + + { // BX_KEY_H ( ibm 36 ) + { "\x23" , "\xA3" }, + { "\x33" , "\xF0\x33" }, + { "\x33" , "\xF0\x33" }, + }, + + { // BX_KEY_I ( ibm 24 ) + { "\x17" , "\x97" }, + { "\x43" , "\xF0\x43" }, + { "\x43" , "\xF0\x43" }, + }, + + { // BX_KEY_J ( ibm 37 ) + { "\x24" , "\xA4" }, + { "\x3B" , "\xF0\x3B" }, + { "\x3B" , "\xF0\x3B" }, + }, + + { // BX_KEY_K ( ibm 38 ) + { "\x25" , "\xA5" }, + { "\x42" , "\xF0\x42" }, + { "\x42" , "\xF0\x42" }, + }, + + { // BX_KEY_L ( ibm 39 ) + { "\x26" , "\xA6" }, + { "\x4B" , "\xF0\x4B" }, + { "\x4B" , "\xF0\x4B" }, + }, + + { // BX_KEY_M ( ibm 52 ) + { "\x32" , "\xB2" }, + { "\x3A" , "\xF0\x3A" }, + { "\x3A" , "\xF0\x3A" }, + }, + + { // BX_KEY_N ( ibm 51 ) + { "\x31" , "\xB1" }, + { "\x31" , "\xF0\x31" }, + { "\x31" , "\xF0\x31" }, + }, + + { // BX_KEY_O ( ibm 25 ) + { "\x18" , "\x98" }, + { "\x44" , "\xF0\x44" }, + { "\x44" , "\xF0\x44" }, + }, + + { // BX_KEY_P ( ibm 26 ) + { "\x19" , "\x99" }, + { "\x4D" , "\xF0\x4D" }, + { "\x4D" , "\xF0\x4D" }, + }, + + { // BX_KEY_Q ( ibm 17 ) + { "\x10" , "\x90" }, + { "\x15" , "\xF0\x15" }, + { "\x15" , "\xF0\x15" }, + }, + + { // BX_KEY_R ( ibm 20 ) + { "\x13" , "\x93" }, + { "\x2D" , "\xF0\x2D" }, + { "\x2D" , "\xF0\x2D" }, + }, + + { // BX_KEY_S ( ibm 32 ) + { "\x1F" , "\x9F" }, + { "\x1B" , "\xF0\x1B" }, + { "\x1B" , "\xF0\x1B" }, + }, + + { // BX_KEY_T ( ibm 21 ) + { "\x14" , "\x94" }, + { "\x2C" , "\xF0\x2C" }, + { "\x2C" , "\xF0\x2C" }, + }, + + { // BX_KEY_U ( ibm 23 ) + { "\x16" , "\x96" }, + { "\x3C" , "\xF0\x3C" }, + { "\x3C" , "\xF0\x3C" }, + }, + + { // BX_KEY_V ( ibm 49 ) + { "\x2F" , "\xAF" }, + { "\x2A" , "\xF0\x2A" }, + { "\x2A" , "\xF0\x2A" }, + }, + + { // BX_KEY_W ( ibm 18 ) + { "\x11" , "\x91" }, + { "\x1D" , "\xF0\x1D" }, + { "\x1D" , "\xF0\x1D" }, + }, + + { // BX_KEY_X ( ibm 47 ) + { "\x2D" , "\xAD" }, + { "\x22" , "\xF0\x22" }, + { "\x22" , "\xF0\x22" }, + }, + + { // BX_KEY_Y ( ibm 22 ) + { "\x15" , "\x95" }, + { "\x35" , "\xF0\x35" }, + { "\x35" , "\xF0\x35" }, + }, + + { // BX_KEY_Z ( ibm 46 ) + { "\x2C" , "\xAC" }, + { "\x1A" , "\xF0\x1A" }, + { "\x1A" , "\xF0\x1A" }, + }, + + { // BX_KEY_0 ( ibm 11 ) + { "\x0B" , "\x8B" }, + { "\x45" , "\xF0\x45" }, + { "\x45" , "\xF0\x45" }, + }, + + { // BX_KEY_1 ( ibm 2 ) + { "\x02" , "\x82" }, + { "\x16" , "\xF0\x16" }, + { "\x16" , "\xF0\x16" }, + }, + + { // BX_KEY_2 ( ibm 3 ) + { "\x03" , "\x83" }, + { "\x1E" , "\xF0\x1E" }, + { "\x1E" , "\xF0\x1E" }, + }, + + { // BX_KEY_3 ( ibm 4 ) + { "\x04" , "\x84" }, + { "\x26" , "\xF0\x26" }, + { "\x26" , "\xF0\x26" }, + }, + + { // BX_KEY_4 ( ibm 5 ) + { "\x05" , "\x85" }, + { "\x25" , "\xF0\x25" }, + { "\x25" , "\xF0\x25" }, + }, + + { // BX_KEY_5 ( ibm 6 ) + { "\x06" , "\x86" }, + { "\x2E" , "\xF0\x2E" }, + { "\x2E" , "\xF0\x2E" }, + }, + + { // BX_KEY_6 ( ibm 7 ) + { "\x07" , "\x87" }, + { "\x36" , "\xF0\x36" }, + { "\x36" , "\xF0\x36" }, + }, + + { // BX_KEY_7 ( ibm 8 ) + { "\x08" , "\x88" }, + { "\x3D" , "\xF0\x3D" }, + { "\x3D" , "\xF0\x3D" }, + }, + + { // BX_KEY_8 ( ibm 9 ) + { "\x09" , "\x89" }, + { "\x3E" , "\xF0\x3E" }, + { "\x3E" , "\xF0\x3E" }, + }, + + { // BX_KEY_9 ( ibm 10 ) + { "\x0A" , "\x8A" }, + { "\x46" , "\xF0\x46" }, + { "\x46" , "\xF0\x46" }, + }, + + { // BX_KEY_ESC ( ibm 110 ) + { "\x01" , "\x81" }, + { "\x76" , "\xF0\x76" }, + { "\x08" , "\xF0\x08" }, + }, + + { // BX_KEY_SPACE ( ibm 61 ) + { "\x39" , "\xB9" }, + { "\x29" , "\xF0\x29" }, + { "\x29" , "\xF0\x29" }, + }, + + { // BX_KEY_SINGLE_QUOTE ( ibm 41 ) + { "\x28" , "\xA8" }, + { "\x52" , "\xF0\x52" }, + { "\x52" , "\xF0\x52" }, + }, + + { // BX_KEY_COMMA ( ibm 53 ) + { "\x33" , "\xB3" }, + { "\x41" , "\xF0\x41" }, + { "\x41" , "\xF0\x41" }, + }, + + { // BX_KEY_PERIOD ( ibm 54 ) + { "\x34" , "\xB4" }, + { "\x49" , "\xF0\x49" }, + { "\x49" , "\xF0\x49" }, + }, + + { // BX_KEY_SLASH ( ibm 55 ) + { "\x35" , "\xB5" }, + { "\x4A" , "\xF0\x4A" }, + { "\x4A" , "\xF0\x4A" }, + }, + + { // BX_KEY_SEMICOLON ( ibm 40 ) + { "\x27" , "\xA7" }, + { "\x4C" , "\xF0\x4C" }, + { "\x4C" , "\xF0\x4C" }, + }, + + { // BX_KEY_EQUALS ( ibm 13 ) + { "\x0D" , "\x8D" }, + { "\x55" , "\xF0\x55" }, + { "\x55" , "\xF0\x55" }, + }, + + { // BX_KEY_LEFT_BRACKET ( ibm 27 ) + { "\x1A" , "\x9A" }, + { "\x54" , "\xF0\x54" }, + { "\x54" , "\xF0\x54" }, + }, + + { // BX_KEY_BACKSLASH ( ibm 42, 29) + { "\x2B" , "\xAB" }, + { "\x5D" , "\xF0\x5D" }, + { "\x53" , "\xF0\x53" }, + }, + + { // BX_KEY_RIGHT_BRACKET ( ibm 28 ) + { "\x1B" , "\x9B" }, + { "\x5B" , "\xF0\x5B" }, + { "\x5B" , "\xF0\x5B" }, + }, + + { // BX_KEY_MINUS ( ibm 12 ) + { "\x0C" , "\x8C" }, + { "\x4E" , "\xF0\x4E" }, + { "\x4E" , "\xF0\x4E" }, + }, + + { // BX_KEY_GRAVE ( ibm 1 ) + { "\x29" , "\xA9" }, + { "\x0E" , "\xF0\x0E" }, + { "\x0E" , "\xF0\x0E" }, + }, + + { // BX_KEY_BACKSPACE ( ibm 15 ) + { "\x0E" , "\x8E" }, + { "\x66" , "\xF0\x66" }, + { "\x66" , "\xF0\x66" }, + }, + + { // BX_KEY_ENTER ( ibm 43 ) + { "\x1C" , "\x9C" }, + { "\x5A" , "\xF0\x5A" }, + { "\x5A" , "\xF0\x5A" }, + }, + + { // BX_KEY_TAB ( ibm 16 ) + { "\x0F" , "\x8F" }, + { "\x0D" , "\xF0\x0D" }, + { "\x0D" , "\xF0\x0D" }, + }, + + { // BX_KEY_LEFT_BACKSLASH ( ibm 45 ) + { "\x56" , "\xD6" }, + { "\x61" , "\xF0\x61" }, + { "\x13" , "\xF0\x13" }, + }, + + { // BX_KEY_PRINT ( ibm 124 ) + { "\xE0\x37" , "\xE0\xB7" }, + { "\xE0\x7C" , "\xE0\xF0\x7C" }, + { "\x57" , "\xF0\x57" }, + }, + + { // BX_KEY_SCRL_LOCK ( ibm 125 ) + { "\x46" , "\xC6" }, + { "\x7E" , "\xF0\x7E" }, + { "\x5F" , "\xF0\x5F" }, + }, + + { // BX_KEY_PAUSE ( ibm 126 ) + { "\xE1\x1D\x45\xE1\x9D\xC5" , "" }, + { "\xE1\x14\x77\xE1\xF0\x14\xF0\x77" , "" }, + { "\x62" , "\xF0\x62" }, + }, + + { // BX_KEY_INSERT ( ibm 75 ) + { "\xE0\x52" , "\xE0\xD2" }, + { "\xE0\x70" , "\xE0\xF0\x70" }, + { "\x67" , "\xF0\x67" }, + }, + + { // BX_KEY_DELETE ( ibm 76 ) + { "\xE0\x53" , "\xE0\xD3" }, + { "\xE0\x71" , "\xE0\xF0\x71" }, + { "\x64" , "\xF0\x64" }, + }, + + { // BX_KEY_HOME ( ibm 80 ) + { "\xE0\x47" , "\xE0\xC7" }, + { "\xE0\x6C" , "\xE0\xF0\x6C" }, + { "\x6E" , "\xF0\x6E" }, + }, + + { // BX_KEY_END ( ibm 81 ) + { "\xE0\x4F" , "\xE0\xCF" }, + { "\xE0\x69" , "\xE0\xF0\x69" }, + { "\x65" , "\xF0\x65" }, + }, + + { // BX_KEY_PAGE_UP ( ibm 85 ) + { "\xE0\x49" , "\xE0\xC9" }, + { "\xE0\x7D" , "\xE0\xF0\x7D" }, + { "\x6F" , "\xF0\x6F" }, + }, + + { // BX_KEY_PAGE_DOWN ( ibm 86 ) + { "\xE0\x51" , "\xE0\xD1" }, + { "\xE0\x7A" , "\xE0\xF0\x7A" }, + { "\x6D" , "\xF0\x6D" }, + }, + + { // BX_KEY_KP_ADD ( ibm 106 ) + { "\x4E" , "\xCE" }, + { "\x79" , "\xF0\x79" }, + { "\x7C" , "\xF0\x7C" }, + }, + + { // BX_KEY_KP_SUBTRACT ( ibm 105 ) + { "\x4A" , "\xCA" }, + { "\x7B" , "\xF0\x7B" }, + { "\x84" , "\xF0\x84" }, + }, + + { // BX_KEY_KP_END ( ibm 93 ) + { "\x4F" , "\xCF" }, + { "\x69" , "\xF0\x69" }, + { "\x69" , "\xF0\x69" }, + }, + + { // BX_KEY_KP_DOWN ( ibm 98 ) + { "\x50" , "\xD0" }, + { "\x72" , "\xF0\x72" }, + { "\x72" , "\xF0\x72" }, + }, + + { // BX_KEY_KP_PAGE_DOWN ( ibm 103 ) + { "\x51" , "\xD1" }, + { "\x7A" , "\xF0\x7A" }, + { "\x7A" , "\xF0\x7A" }, + }, + + { // BX_KEY_KP_LEFT ( ibm 92 ) + { "\x4B" , "\xCB" }, + { "\x6B" , "\xF0\x6B" }, + { "\x6B" , "\xF0\x6B" }, + }, + + { // BX_KEY_KP_RIGHT ( ibm 102 ) + { "\x4D" , "\xCD" }, + { "\x74" , "\xF0\x74" }, + { "\x74" , "\xF0\x74" }, + }, + + { // BX_KEY_KP_HOME ( ibm 91 ) + { "\x47" , "\xC7" }, + { "\x6C" , "\xF0\x6C" }, + { "\x6C" , "\xF0\x6C" }, + }, + + { // BX_KEY_KP_UP ( ibm 96 ) + { "\x48" , "\xC8" }, + { "\x75" , "\xF0\x75" }, + { "\x75" , "\xF0\x75" }, + }, + + { // BX_KEY_KP_PAGE_UP ( ibm 101 ) + { "\x49" , "\xC9" }, + { "\x7D" , "\xF0\x7D" }, + { "\x7D" , "\xF0\x7D" }, + }, + + { // BX_KEY_KP_INSERT ( ibm 99 ) + { "\x52" , "\xD2" }, + { "\x70" , "\xF0\x70" }, + { "\x70" , "\xF0\x70" }, + }, + + { // BX_KEY_KP_DELETE ( ibm 104 ) + { "\x53" , "\xD3" }, + { "\x71" , "\xF0\x71" }, + { "\x71" , "\xF0\x71" }, + }, + + { // BX_KEY_KP_5 ( ibm 97 ) + { "\x4C" , "\xCC" }, + { "\x73" , "\xF0\x73" }, + { "\x73" , "\xF0\x73" }, + }, + + { // BX_KEY_UP ( ibm 83 ) + { "\xE0\x48" , "\xE0\xC8" }, + { "\xE0\x75" , "\xE0\xF0\x75" }, + { "\x63" , "\xF0\x63" }, + }, + + { // BX_KEY_DOWN ( ibm 84 ) + { "\xE0\x50" , "\xE0\xD0" }, + { "\xE0\x72" , "\xE0\xF0\x72" }, + { "\x60" , "\xF0\x60" }, + }, + + { // BX_KEY_LEFT ( ibm 79 ) + { "\xE0\x4B" , "\xE0\xCB" }, + { "\xE0\x6B" , "\xE0\xF0\x6B" }, + { "\x61" , "\xF0\x61" }, + }, + + { // BX_KEY_RIGHT ( ibm 89 ) + { "\xE0\x4D" , "\xE0\xCD" }, + { "\xE0\x74" , "\xE0\xF0\x74" }, + { "\x6A" , "\xF0\x6A" }, + }, + + { // BX_KEY_KP_ENTER ( ibm 108 ) + { "\xE0\x1C" , "\xE0\x9C" }, + { "\xE0\x5A" , "\xE0\xF0\x5A" }, + { "\x79" , "\xF0\x79" }, + }, + + { // BX_KEY_KP_MULTIPLY ( ibm 100 ) + { "\x37" , "\xB7" }, + { "\x7C" , "\xF0\x7C" }, + { "\x7E" , "\xF0\x7E" }, + }, + + { // BX_KEY_KP_DIVIDE ( ibm 95 ) + { "\xE0\x35" , "\xE0\xB5" }, + { "\xE0\x4A" , "\xE0\xF0\x4A" }, + { "\x77" , "\xF0\x77" }, + }, + + { // BX_KEY_WIN_L + { "\xE0\x5B" , "\xE0\xDB" }, + { "\xE0\x1F" , "\xE0\xF0\x1F" }, + { "\x8B" , "\xF0\x8B" }, + }, + + { // BX_KEY_WIN_R + { "\xE0\x5C" , "\xE0\xDC" }, + { "\xE0\x27" , "\xE0\xF0\x27" }, + { "\x8C" , "\xF0\x8C" }, + }, + + { // BX_KEY_MENU + { "\xE0\x5D" , "\xE0\xDD" }, + { "\xE0\x2F" , "\xE0\xF0\x2F" }, + { "\x8D" , "\xF0\x8D" }, + }, + + { // BX_KEY_ALT_SYSREQ + { "\x54" , "\xD4" }, + { "\x84" , "\xF0\x84" }, + { "\x57" , "\xF0\x57" }, + }, + + { // BX_KEY_CTRL_BREAK + { "\xE0\x46" , "\xE0\xC6" }, + { "\xE0\x7E" , "\xE0\xF0\x7E" }, + { "\x62" , "\xF0\x62" }, + }, + + { // BX_KEY_INT_BACK + { "\xE0\x6A" , "\xE0\xEA" }, + { "\xE0\x38" , "\xE0\xF0\x38" }, + { "\x38" , "\xF0\x38" }, + }, + + { // BX_KEY_INT_FORWARD + { "\xE0\x69" , "\xE0\xE9" }, + { "\xE0\x30" , "\xE0\xF0\x30" }, + { "\x30" , "\xF0\x30" }, + }, + + { // BX_KEY_INT_STOP + { "\xE0\x68" , "\xE0\xE8" }, + { "\xE0\x28" , "\xE0\xF0\x28" }, + { "\x28" , "\xF0\x28" }, + }, + + { // BX_KEY_INT_MAIL + { "\xE0\x6C" , "\xE0\xEC" }, + { "\xE0\x48" , "\xE0\xF0\x48" }, + { "\x48" , "\xF0\x48" }, + }, + + { // BX_KEY_INT_SEARCH + { "\xE0\x65" , "\xE0\xE5" }, + { "\xE0\x10" , "\xE0\xF0\x10" }, + { "\x10" , "\xF0\x10" }, + }, + + { // BX_KEY_INT_FAV + { "\xE0\x66" , "\xE0\xE6" }, + { "\xE0\x18" , "\xE0\xF0\x18" }, + { "\x18" , "\xF0\x18" }, + }, + + { // BX_KEY_INT_HOME + { "\xE0\x32" , "\xE0\xB2" }, + { "\xE0\x3A" , "\xE0\xF0\x3A" }, + { "\x97" , "\xF0\x97" }, + }, + + { // BX_KEY_POWER_MYCOMP + { "\xE0\x6B" , "\xE0\xEB" }, + { "\xE0\x40" , "\xE0\xF0\x40" }, + { "\x40" , "\xF0\x40" }, + }, + + { // BX_KEY_POWER_CALC + { "\xE0\x21" , "\xE0\xA1" }, + { "\xE0\x2B" , "\xE0\xF0\x2B" }, + { "\x99" , "\xF0\x99" }, + }, + + { // BX_KEY_POWER_SLEEP + { "\xE0\x5F" , "\xE0\xDF" }, + { "\xE0\x3F" , "\xE0\xF0\x3F" }, + { "\x7F" , "\xF0\x7F" }, + }, + + { // BX_KEY_POWER_POWER + { "\xE0\x5E" , "\xE0\xDE" }, + { "\xE0\x37" , "\xE0\xF0\x37" }, + { "" , "" }, + }, + + { // BX_KEY_POWER_WAKE + { "\xE0\x63" , "\xE0\xE3" }, + { "\xE0\x5E" , "\xE0\xF0\x5E" }, + { "" , "" }, + }, + +}; diff --git a/tools/ioemu/iodev/scancodes.h b/tools/ioemu/iodev/scancodes.h new file mode 100644 index 0000000000..f26491b56e --- /dev/null +++ b/tools/ioemu/iodev/scancodes.h @@ -0,0 +1,31 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: scancodes.h,v 1.4 2002/10/24 21:07:51 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 MandrakeSoft S.A. +// +// 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 + + +// Translation table of the 8042 +extern unsigned char translation8042[256]; + +typedef struct { + const char *make; + const char *brek; + }scancode; + +// Scancodes table +extern scancode scancodes[BX_KEY_NBKEYS][3]; diff --git a/tools/ioemu/iodev/scsi_commands.h b/tools/ioemu/iodev/scsi_commands.h new file mode 100644 index 0000000000..c516fde31c --- /dev/null +++ b/tools/ioemu/iodev/scsi_commands.h @@ -0,0 +1,418 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: scsi_commands.h,v 1.3 2001/10/03 13:10:38 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// +/* scsi/commands.h + Used only in cdrom_amigaos.cc. + + Operation codes for SCSI-2 commands + + 30 Nov 94 Peter Urbanec Created file + 10 Jan 95 Peter Urbanec Added SCSI_ prefix to all commands + 31 Jan 95 Peter Urbanec Released to public + +*/ + + +/* All device types */ + +#define SCSI_CHANGE_DEFINITION 0x40 +#define SCSI_COMPARE 0x39 +#define SCSI_COPY 0x18 +#define SCSI_COPY_AND_VERIFY 0x3a +#define SCSI_INQUIRY 0x12 +#define SCSI_LOG_SELECT 0x4c +#define SCSI_LOG_SENSE 0x4d +#define SCSI_MODE_SELECT_6 0x15 +#define SCSI_MODE_SELECT_10 0x55 +#define SCSI_MODE_SENSE_6 0x1a +#define SCSI_MODE_SENSE_10 0x5a +#define SCSI_READ_BUFFER 0x3c +#define SCSI_RECEIVE_DIAGNOSTIC_RESULTS 0x1c +#define SCSI_REQUEST_SENSE 0x03 +#define SCSI_SEND_DIAGNOSTIC 0x1d +#define SCSI_TEST_UNIT_READY 0x00 +#define SCSI_WRITE_BUFFER 0x3b + + +/* Direct Access devices */ + +#define SCSI_DA_CHANGE_DEFINITION 0x40 +#define SCSI_DA_COMPARE 0x39 +#define SCSI_DA_COPY 0x18 +#define SCSI_DA_COPY_AND_VERIFY 0x3a +#define SCSI_DA_FORMAT_UNIT 0x04 +#define SCSI_DA_INQUIRY 0x12 +#define SCSI_DA_LOCK_UNLOCK_CACHE 0x36 +#define SCSI_DA_LOG_SELECT 0x4c +#define SCSI_DA_LOG_SENSE 0x4d +#define SCSI_DA_MODE_SELECT_6 0x15 +#define SCSI_DA_MODE_SELECT_10 0x55 +#define SCSI_DA_MODE_SENSE_6 0x1a +#define SCSI_DA_MODE_SENSE_10 0x5a +#define SCSI_DA_PRE_FETCH 0x34 +#define SCSI_DA_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e +#define SCSI_DA_READ_6 0x08 +#define SCSI_DA_READ_10 0x28 +#define SCSI_DA_READ_BUFFER 0x3c +#define SCSI_DA_READ_CAPACITY 0x25 +#define SCSI_DA_READ_DEFECT_DATA 0x37 +#define SCSI_DA_READ_LONG 0x3e +#define SCSI_DA_REASSIGN_BLOCKS 0x07 +#define SCSI_DA_RECEIVE_DIAGNOSTIC_RESULTS 0x1c +#define SCSI_DA_RELEASE 0x17 +#define SCSI_DA_REQUEST_SENSE 0x03 +#define SCSI_DA_RESERVE 0x16 +#define SCSI_DA_REZERO_UNIT 0x01 +#define SCSI_DA_SEARCH_DATA_EQUAL 0x31 +#define SCSI_DA_SEARCH_DATA_HIGH 0x30 +#define SCSI_DA_SEARCH_DATA_LOW 0x32 +#define SCSI_DA_SEEK_6 0x0b +#define SCSI_DA_SEEK_10 0x2b +#define SCSI_DA_SEND_DIAGNOSTIC 0x1d +#define SCSI_DA_SET_LIMITS 0x33 +#define SCSI_DA_START_STOP_UNIT 0x1b +#define SCSI_DA_SYNCHRONIZE_CACHE 0x35 +#define SCSI_DA_TEST_UNIT_READY 0x00 +#define SCSI_DA_VERIFY 0x2f + + +/* Sequential access devices */ + +#define SCSI_SA_CHANGE_DEFINITION 0x40 +#define SCSI_SA_COMPARE 0x39 +#define SCSI_SA_COPY 0x18 +#define SCSI_SA_COPY_AND_VERIFY 0x3a +#define SCSI_SA_ERASE 0x19 +#define SCSI_SA_INQUIRY 0x12 +#define SCSI_SA_LOAD_UNLOAD 0x1b +#define SCSI_SA_LOCATE 0x2b +#define SCSI_SA_LOG_SELECT 0x4c +#define SCSI_SA_LOG_SENSE 0x4d +#define SCSI_SA_MODE_SELECT_6 0x15 +#define SCSI_SA_MODE_SELECT_10 0x55 +#define SCSI_SA_MODE_SENSE_6 0x1a +#define SCSI_SA_MODE_SENSE_10 0x5a +#define SCSI_SA_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e +#define SCSI_SA_READ 0x08 +#define SCSI_SA_READ_BLOCK_LIMITS 0x05 +#define SCSI_SA_READ_BUFFER 0x3c +#define SCSI_SA_READ_POSITION 0x34 +#define SCSI_SA_READ_REVERSE 0x0f +#define SCSI_SA_RECEIVE_DIAGNOSTIC_RESULTS 0x1c +#define SCSI_SA_RECOVER_BUFFERED_DATA 0x14 +#define SCSI_SA_RELEASE_UNIT 0x17 +#define SCSI_SA_REQUEST_SENSE 0x03 +#define SCSI_SA_RESERVE_UNIT 0x16 +#define SCSI_SA_REWIND 0x01 +#define SCSI_SA_SEND_DIAGNOSTIC 0x1d +#define SCSI_SA_SPACE 0x11 +#define SCSI_SA_TEST_UNIT_READY 0x00 +#define SCSI_SA_VERIFY 0x13 +#define SCSI_SA_WRITE 0x0a +#define SCSI_SA_WRITE_BUFFER 0x3b +#define SCSI_SA_WRITE_FILEMARKS 0x10 + + +/* Printer devices */ + +#define SCSI_PRT_CHANGE_DEFINITION 0x40 +#define SCSI_PRT_COMPARE 0x39 +#define SCSI_PRT_COPY 0x18 +#define SCSI_PRT_COPY_AND_VERIFY 0x3a +#define SCSI_PRT_FORMAT 0x04 +#define SCSI_PRT_INQUIRY 0x12 +#define SCSI_PRT_LOG_SELECT 0x4c +#define SCSI_PRT_LOG_SENSE 0x4d +#define SCSI_PRT_MODE_SELECT_6 0x15 +#define SCSI_PRT_MODE_SELECT_10 0x55 +#define SCSI_PRT_MODE_SENSE_6 0x1a +#define SCSI_PRT_MODE_SENSE_10 0x5a +#define SCSI_PRT_PRINT 0x0a +#define SCSI_PRT_READ_BUFFER 0x3c +#define SCSI_PRT_RECEIVE_DIAGNOSTIC_RESULTS 0x1c +#define SCSI_PRT_RECOVER_BUFFERED_DATA 0x14 +#define SCSI_PRT_RELEASE_UNIT 0x17 +#define SCSI_PRT_REQUEST_SENSE 0x03 +#define SCSI_PRT_RESERVE_UNIT 0x16 +#define SCSI_PRT_SEND_DIAGNOSTIC 0x1d +#define SCSI_PRT_SLEW_AND_PRINT 0x0b +#define SCSI_PRT_STOP_PRINT 0x1b +#define SCSI_PRT_SYNCHRONIZE_BUFFER 0x10 +#define SCSI_PRT_TEST_UNIT_READY 0x00 +#define SCSI_PRT_WRITE_BUFFER 0x3b + + +/* Processor devices */ + +#define SCSI_CPU_CHANGE_DEFINITION 0x40 +#define SCSI_CPU_COMPARE 0x39 +#define SCSI_CPU_COPY 0x18 +#define SCSI_CPU_COPY_AND_VERIFY 0x3a +#define SCSI_CPU_INQUIRY 0x12 +#define SCSI_CPU_LOG_SELECT 0x4c +#define SCSI_CPU_LOG_SENSE 0x4d +#define SCSI_CPU_READ_BUFFER 0x3c +#define SCSI_CPU_RECEIVE 0x08 +#define SCSI_CPU_RECEIVE_DIAGNOSTIC_RESULTS 0x1c +#define SCSI_CPU_REQUEST_SENSE 0x03 +#define SCSI_CPU_SEND 0x0a +#define SCSI_CPU_SEND_DIAGNOSTIC 0x1d +#define SCSI_CPU_TEST_UNIT_READY 0x00 +#define SCSI_CPU_WRITE_BUFFER 0x3b + + +/* Write Once devices */ + +#define SCSI_WO_CHANGE_DEFINITION 0x40 +#define SCSI_WO_COMPARE 0x39 +#define SCSI_WO_COPY 0x18 +#define SCSI_WO_COPY_AND_VERIFY 0x3a +#define SCSI_WO_INQUIRY 0x12 +#define SCSI_WO_LOCK_UNLOCK_CACHE 0x36 +#define SCSI_WO_LOG_SELECT 0x4c +#define SCSI_WO_LOG_SENSE 0x4d +#define SCSI_WO_MEDIUM_SCAN 0x38 +#define SCSI_WO_MODE_SELECT_6 0x15 +#define SCSI_WO_MODE_SELECT_10 0x55 +#define SCSI_WO_MODE_SENSE_6 0x1a +#define SCSI_WO_MODE_SENSE_10 0x5a +#define SCSI_WO_PRE_FETCH 0x34 +#define SCSI_WO_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e +#define SCSI_WO_READ_6 0x08 +#define SCSI_WO_READ_10 0x28 +#define SCSI_WO_READ_12 0xa8 +#define SCSI_WO_READ_BUFFER 0x3c +#define SCSI_WO_READ_CAPACITY 0x25 +#define SCSI_WO_READ_LONG 0x3e +#define SCSI_WO_REASSIGN_BLOCKS 0x07 +#define SCSI_WO_RECEIVE_DIAGNOSTIC_RESULTS 0x1c +#define SCSI_WO_RELEASE 0x17 +#define SCSI_WO_REQUEST_SENSE 0x03 +#define SCSI_WO_RESERVE 0x16 +#define SCSI_WO_REZERO_UNIT 0x01 +#define SCSI_WO_SEARCH_DATA_EQUAL_10 0x31 +#define SCSI_WO_SEARCH_DATA_EQUAL_12 0xb1 +#define SCSI_WO_SEARCH_DATA_HIGH_10 0x30 +#define SCSI_WO_SEARCH_DATA_HIGH_12 0xb0 +#define SCSI_WO_SEARCH_DATA_LOW_10 0x32 +#define SCSI_WO_SEARCH_DATA_LOW_12 0xb2 +#define SCSI_WO_SEEK_6 0x0b +#define SCSI_WO_SEEK_10 0x2b +#define SCSI_WO_SEND_DIAGNOSTIC 0x1d +#define SCSI_WO_SET_LIMITS_10 0x33 +#define SCSI_WO_SET_LIMITS_12 0xb3 +#define SCSI_WO_START_STOP_UNIT 0x1b +#define SCSI_WO_SYNCHRONIZE_CACHE 0x35 +#define SCSI_WO_TEST_UNIT_READY 0x00 +#define SCSI_WO_VERIFY_10 0x2f +#define SCSI_WO_VERIFY_12 0xaf +#define SCSI_WO_WRITE_6 0x0a +#define SCSI_WO_WRITE_10 0x2a +#define SCSI_WO_WRITE_12 0xaa +#define SCSI_WO_WRITE_AND_VERIFY_10 0x2e +#define SCSI_WO_WRITE_AND_VERIFY_12 0xae +#define SCSI_WO_WRITE_BUFFER 0x3b +#define SCSI_WO_WRITE_LONG 0x3f + + +/* CD-ROM devices */ + +#define SCSI_CD_CHANGE_DEFINITION 0x40 +#define SCSI_CD_COMPARE 0x39 +#define SCSI_CD_COPY 0x18 +#define SCSI_CD_COPY_AND_VERIFY 0x3a +#define SCSI_CD_INQUIRY 0x12 +#define SCSI_CD_LOCK_UNLOCK_CACHE 0x36 +#define SCSI_CD_LOG_SELECT 0x4c +#define SCSI_CD_LOG_SENSE 0x4d +#define SCSI_CD_MODE_SELECT_6 0x15 +#define SCSI_CD_MODE_SELECT_10 0x55 +#define SCSI_CD_MODE_SENSE_6 0x1a +#define SCSI_CD_MODE_SENSE_10 0x5a +#define SCSI_CD_PAUSE_RESUME 0x4b +#define SCSI_CD_PLAY_AUDIO_10 0x45 +#define SCSI_CD_PLAY_AUDIO_12 0xa5 +#define SCSI_CD_PLAY_AUDIO_MSF 0x47 +#define SCSI_CD_PLAY_AUDIO_TRACK_INDEX 0x48 +#define SCSI_CD_PLAY_TRACK_RELATIVE_10 0x49 +#define SCSI_CD_PLAY_TRACK_RELATIVE_12 0xa9 +#define SCSI_CD_PRE_FETCH 0x34 +#define SCSI_CD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e +#define SCSI_CD_READ_6 0x08 +#define SCSI_CD_READ_10 0x28 +#define SCSI_CD_READ_12 0xa8 +#define SCSI_CD_READ_BUFFER 0x3c +#define SCSI_CD_READ_CD_ROM_CAPACITY 0x25 +#define SCSI_CD_READ_HEADER 0x44 +#define SCSI_CD_READ_LONG 0x3e +#define SCSI_CD_READ_SUB_CHANNEL 0x42 +#define SCSI_CD_READ_TOC 0x43 +#define SCSI_CD_RECEIVE_DIAGNOSTIC_RESULT 0x1c +#define SCSI_CD_RELEASE 0x17 +#define SCSI_CD_REQUEST_SENSE 0x03 +#define SCSI_CD_RESERVE 0x16 +#define SCSI_CD_REZERO_UNIT 0x01 +#define SCSI_CD_SEARCH_DATA_EQUAL_10 0x31 +#define SCSI_CD_SEARCH_DATA_EQUAL_12 0xb1 +#define SCSI_CD_SEARCH_DATA_HIGH_10 0x30 +#define SCSI_CD_SEARCH_DATA_HIGH_12 0xb0 +#define SCSI_CD_SEARCH_DATA_LOW_10 0x32 +#define SCSI_CD_SEARCH_DATA_LOW_12 0xb2 +#define SCSI_CD_SEEK_6 0x0b +#define SCSI_CD_SEEK_10 0x2b +#define SCSI_CD_SEND_DIAGNOSTIC 0x1d +#define SCSI_CD_SET_LIMITS_10 0x33 +#define SCSI_CD_SET_LIMITS_12 0xb3 +#define SCSI_CD_START_STOP_UNIT 0x1b +#define SCSI_CD_SYNCHRONIZE_CACHE 0x35 +#define SCSI_CD_TEST_UNIT_READY 0x00 +#define SCSI_CD_VERIFY_10 0x2f +#define SCSI_CD_VERIFY_12 0xaf +#define SCSI_CD_WRITE_BUFFER 0x3b + + +/* Scanner devices */ + +#define SCSI_SC_CHANGE_DEFINITION 0x40 +#define SCSI_SC_COMPARE 0x39 +#define SCSI_SC_COPY 0x18 +#define SCSI_SC_COPY_AND_VERIFY 0x3a +#define SCSI_SC_GET_DATA_BUFFER_STATUS 0x34 +#define SCSI_SC_GET_WINDOW 0x25 +#define SCSI_SC_INQUIRY 0x12 +#define SCSI_SC_LOG_SELECT 0x4c +#define SCSI_SC_LOG_SENSE 0x4d +#define SCSI_SC_MODE_SELECT_6 0x15 +#define SCSI_SC_MODE_SELECT_10 0x55 +#define SCSI_SC_MODE_SENSE_6 0x1a +#define SCSI_SC_MODE_SENSE_10 0x5a +#define SCSI_SC_OBJECT_POSITION 0x31 +#define SCSI_SC_READ 0x28 +#define SCSI_SC_READ_BUFFER 0x3c +#define SCSI_SC_RECEIVE_DIAGNOSTIC_RESULTS 0x1c +#define SCSI_SC_RELEASE_UNIT 0x17 +#define SCSI_SC_REQUEST_SENSE 0x03 +#define SCSI_SC_RESERVE_UNIT 0x16 +#define SCSI_SC_SCAN 0x1b +#define SCSI_SC_SET_WINDOW 0x24 +#define SCSI_SC_SEND 0x2a +#define SCSI_SC_SEND_DIAGNOSTIC 0x1d +#define SCSI_SC_TEST_UNIT_READY 0x00 +#define SCSI_SC_WRITE_BUFFER 0x3b + + +/* Optical memory devices */ + +#define SCSI_OM_CHANGE_DEFINITION 0x40 +#define SCSI_OM_COMPARE 0x39 +#define SCSI_OM_COPY 0x18 +#define SCSI_OM_COPY_AND_VERIFY 0x3a +#define SCSI_OM_ERASE_10 0x2c +#define SCSI_OM_ERASE_12 0xac +#define SCSI_OM_FORMAT_UNIT 0x04 +#define SCSI_OM_INQUIRY 0x12 +#define SCSI_OM_LOCK_UNLOCK_CACHE 0x36 +#define SCSI_OM_LOG_SELECT 0x4c +#define SCSI_OM_LOG_SENSE 0x4d +#define SCSI_OM_MEDIUM_SCAN 0x38 +#define SCSI_OM_MODE_SELECT_6 0x15 +#define SCSI_OM_MODE_SELECT_10 0x55 +#define SCSI_OM_MODE_SENSE_6 0x1a +#define SCSI_OM_MODE_SENSE_10 0x5a +#define SCSI_OM_PRE_FETCH 0x34 +#define SCSI_OM_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e +#define SCSI_OM_READ_6 0x08 +#define SCSI_OM_READ_10 0x28 +#define SCSI_OM_READ_12 0xa8 +#define SCSI_OM_READ_BUFFER 0x3c +#define SCSI_OM_READ_CAPACITY 0x25 +#define SCSI_OM_READ_DEFECT_DATA_10 0x37 +#define SCSI_OM_READ_DEFECT_DATA_12 0xb7 +#define SCSI_OM_READ_GENERATION 0x29 +#define SCSI_OM_READ_LONG 0x3e +#define SCSI_OM_READ_UPDATED_BLOCK 0x2d +#define SCSI_OM_REASSIGN_BLOCKS 0x07 +#define SCSI_OM_RECEIVE_DIAGNOSTIC_RESULTS 0x1c +#define SCSI_OM_RELEASE 0x17 +#define SCSI_OM_REQUEST_SENSE 0x03 +#define SCSI_OM_RESERVE 0x16 +#define SCSI_OM_REZERO_UNIT 0x01 +#define SCSI_OM_SEARCH_DATA_EQUAL_10 0x31 +#define SCSI_OM_SEARCH_DATA_EQUAL_12 0xb1 +#define SCSI_OM_SEARCH_DATA_HIGH_10 0x30 +#define SCSI_OM_SEARCH_DATA_HIGH_12 0xb0 +#define SCSI_OM_SEARCH_DATA_LOW_10 0x32 +#define SCSI_OM_SEARCH_DATA_LOW_12 0xb2 +#define SCSI_OM_SEEK_6 0x0b +#define SCSI_OM_SEEK_10 0x2b +#define SCSI_OM_SEND_DIAGNOSTIC 0x1d +#define SCSI_OM_SET_LIMITS_10 0x33 +#define SCSI_OM_SET_LIMITS_12 0xb3 +#define SCSI_OM_START_STOP_UNIT 0x1b +#define SCSI_OM_SYNCHRONIZE_CACHE 0x35 +#define SCSI_OM_TEST_UNIT_READY 0x00 +#define SCSI_OM_UPDATE_BLOCK 0x3d +#define SCSI_OM_VERIFY_10 0x2f +#define SCSI_OM_VERIFY_12 0xaf +#define SCSI_OM_WRITE_6 0x0a +#define SCSI_OM_WRITE_10 0x2a +#define SCSI_OM_WRITE_12 0xaa +#define SCSI_OM_WRITE_AND_VERIFY_10 0x2e +#define SCSI_OM_WRITE_AND_VERIFY_12 0xae +#define SCSI_OM_WRITE_BUFFER 0x3b +#define SCSI_OM_WRITE_LONG 0x3f + + +/* Medium changer devices */ + +#define SCSI_MC_CHANGE_DEFINITION 0x40 +#define SCSI_MC_EXCHANGE_MEDIUM 0xa6 +#define SCSI_MC_INITIALIZE_ELEMENT_STATUS 0x07 +#define SCSI_MC_INQUIRY 0x12 +#define SCSI_MC_LOG_SELECT 0x4c +#define SCSI_MC_LOG_SENSE 0x4d +#define SCSI_MC_MODE_SELECT_6 0x15 +#define SCSI_MC_MODE_SELECT_10 0x55 +#define SCSI_MC_MODE_SENSE_6 0x1a +#define SCSI_MC_MODE_SENSE_10 0x5a +#define SCSI_MC_MOVE_MEDIUM 0xa5 +#define SCSI_MC_POSITION_TO_ELEMENT 0x2b +#define SCSI_MC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e +#define SCSI_MC_READ_BUFFER 0x3c +#define SCSI_MC_READ_ELEMENT_STATUS 0xb8 +#define SCSI_MC_RECEIVE_DIAGNOSTIC_RESULTS 0x1c +#define SCSI_MC_RELEASE 0x17 +#define SCSI_MC_REQUEST_VOLUME_ELEMENT_ADDRESS 0xb5 +#define SCSI_MC_REQUEST_SENSE 0x03 +#define SCSI_MC_RESERVE 0x16 +#define SCSI_MC_REZERO_UNIT 0x01 +#define SCSI_MC_SEND_DIAGNOSTIC 0x1d +#define SCSI_MC_SEND_VOLUME_TAG 0xb6 +#define SCSI_MC_TEST_UNIT_READY 0x00 +#define SCSI_MC_WRITE_BUFFER 0x3b + + +/* Communications devices */ + +#define SCSI_COM_CHANGE_DEFINITION 0x40 +#define SCSI_COM_GET_MESSAGE_6 0x08 +#define SCSI_COM_GET_MESSAGE_10 0x28 +#define SCSI_COM_GET_MESSAGE_12 0xa8 +#define SCSI_COM_INQUIRY 0x12 +#define SCSI_COM_LOG_SELECT 0x4c +#define SCSI_COM_LOG_SENSE 0x4d +#define SCSI_COM_MODE_SELECT_6 0x15 +#define SCSI_COM_MODE_SELECT_10 0x55 +#define SCSI_COM_MODE_SENSE_6 0x1a +#define SCSI_COM_MODE_SENSE_10 0x5a +#define SCSI_COM_READ_BUFFER 0x3c +#define SCSI_COM_RECEIVE_DIAGNOSTIC_RESULTS 0x1c +#define SCSI_COM_REQUEST_SENSE 0x03 +#define SCSI_COM_SEND_DIAGNOSTIC 0x1d +#define SCSI_COM_SEND_MESSAGE_6 0x0a +#define SCSI_COM_SEND_MESSAGE_10 0x2a +#define SCSI_COM_SEND_MESSAGE_12 0xaa +#define SCSI_COM_TEST_UNIT_READY 0x00 +#define SCSI_COM_WRITE_BUFFER 0x3b + diff --git a/tools/ioemu/iodev/scsidefs.h b/tools/ioemu/iodev/scsidefs.h new file mode 100644 index 0000000000..86239e8a7d --- /dev/null +++ b/tools/ioemu/iodev/scsidefs.h @@ -0,0 +1,286 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: scsidefs.h,v 1.4 2002/09/16 16:58:36 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// +// +// iodev/scsidefs.h +// $Id: scsidefs.h,v 1.4 2002/09/16 16:58:36 bdenney Exp $ +// +// This file was copied from ... ? +// + +//*************************************************************************** +// +// Name: SCSIDEFS.H +// +// Description: SCSI definitions ('C' Language) +// +//*************************************************************************** + +//*************************************************************************** +// %%% TARGET STATUS VALUES %%% +//*************************************************************************** +#define STATUS_GOOD 0x00 // Status Good +#define STATUS_CHKCOND 0x02 // Check Condition +#define STATUS_CONDMET 0x04 // Condition Met +#define STATUS_BUSY 0x08 // Busy +#define STATUS_INTERM 0x10 // Intermediate +#define STATUS_INTCDMET 0x14 // Intermediate-condition met +#define STATUS_RESCONF 0x18 // Reservation conflict +#define STATUS_COMTERM 0x22 // Command Terminated +#define STATUS_QFULL 0x28 // Queue full + +//*************************************************************************** +// %%% SCSI MISCELLANEOUS EQUATES %%% +//*************************************************************************** +#define MAXLUN 7 // Maximum Logical Unit Id +#define MAXTARG 7 // Maximum Target Id +#define MAX_SCSI_LUNS 64 // Maximum Number of SCSI LUNs +#define MAX_NUM_HA 8 // Maximum Number of SCSI HA's + +//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ +// +// %%% SCSI COMMAND OPCODES %%% +// +///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ + +//*************************************************************************** +// %%% Commands for all Device Types %%% +//*************************************************************************** +#define SCSI_CHANGE_DEF 0x40 // Change Definition (Optional) +#define SCSI_COMPARE 0x39 // Compare (O) +#define SCSI_COPY 0x18 // Copy (O) +#define SCSI_COP_VERIFY 0x3A // Copy and Verify (O) +#define SCSI_INQUIRY 0x12 // Inquiry (MANDATORY) +#define SCSI_LOG_SELECT 0x4C // Log Select (O) +#define SCSI_LOG_SENSE 0x4D // Log Sense (O) +#define SCSI_MODE_SEL6 0x15 // Mode Select 6-byte (Device Specific) +#define SCSI_MODE_SEL10 0x55 // Mode Select 10-byte (Device Specific) +#define SCSI_MODE_SEN6 0x1A // Mode Sense 6-byte (Device Specific) +#define SCSI_MODE_SEN10 0x5A // Mode Sense 10-byte (Device Specific) +#define SCSI_READ_BUFF 0x3C // Read Buffer (O) +#define SCSI_REQ_SENSE 0x03 // Request Sense (MANDATORY) +#define SCSI_SEND_DIAG 0x1D // Send Diagnostic (O) +#define SCSI_TST_U_RDY 0x00 // Test Unit Ready (MANDATORY) +#define SCSI_WRITE_BUFF 0x3B // Write Buffer (O) + +//*************************************************************************** +// %%% Commands Unique to Direct Access Devices %%% +//*************************************************************************** +#define SCSI_COMPARE 0x39 // Compare (O) +#define SCSI_FORMAT 0x04 // Format Unit (MANDATORY) +#define SCSI_LCK_UN_CAC 0x36 // Lock Unlock Cache (O) +#define SCSI_PREFETCH 0x34 // Prefetch (O) +#define SCSI_MED_REMOVL 0x1E // Prevent/Allow medium Removal (O) +#define SCSI_READ6 0x08 // Read 6-byte (MANDATORY) +#define SCSI_READ10 0x28 // Read 10-byte (MANDATORY) +#define SCSI_RD_CAPAC 0x25 // Read Capacity (MANDATORY) +#define SCSI_RD_DEFECT 0x37 // Read Defect Data (O) +#define SCSI_READ_LONG 0x3E // Read Long (O) +#define SCSI_REASS_BLK 0x07 // Reassign Blocks (O) +#define SCSI_RCV_DIAG 0x1C // Receive Diagnostic Results (O) +#define SCSI_RELEASE 0x17 // Release Unit (MANDATORY) +#define SCSI_REZERO 0x01 // Rezero Unit (O) +#define SCSI_SRCH_DAT_E 0x31 // Search Data Equal (O) +#define SCSI_SRCH_DAT_H 0x30 // Search Data High (O) +#define SCSI_SRCH_DAT_L 0x32 // Search Data Low (O) +#define SCSI_SEEK6 0x0B // Seek 6-Byte (O) +#define SCSI_SEEK10 0x2B // Seek 10-Byte (O) +#define SCSI_SEND_DIAG 0x1D // Send Diagnostics (MANDATORY) +#define SCSI_SET_LIMIT 0x33 // Set Limits (O) +#define SCSI_START_STP 0x1B // Start/Stop Unit (O) +#define SCSI_SYNC_CACHE 0x35 // Synchronize Cache (O) +#define SCSI_VERIFY 0x2F // Verify (O) +#define SCSI_WRITE6 0x0A // Write 6-Byte (MANDATORY) +#define SCSI_WRITE10 0x2A // Write 10-Byte (MANDATORY) +#define SCSI_WRT_VERIFY 0x2E // Write and Verify (O) +#define SCSI_WRITE_LONG 0x3F // Write Long (O) +#define SCSI_WRITE_SAME 0x41 // Write Same (O) + +//*************************************************************************** +// %%% Commands Unique to Sequential Access Devices %%% +//*************************************************************************** +#define SCSI_ERASE 0x19 // Erase (MANDATORY) +#define SCSI_LOAD_UN 0x1B // Load/Unload (O) +#define SCSI_LOCATE 0x2B // Locate (O) +#define SCSI_RD_BLK_LIM 0x05 // Read Block Limits (MANDATORY) +#define SCSI_READ_POS 0x34 // Read Position (O) +#define SCSI_READ_REV 0x0F // Read Reverse (O) +#define SCSI_REC_BF_DAT 0x14 // Recover Buffer Data (O) +#define SCSI_RESERVE 0x16 // Reserve Unit (MANDATORY) +#define SCSI_REWIND 0x01 // Rewind (MANDATORY) +#define SCSI_SPACE 0x11 // Space (MANDATORY) +#define SCSI_VERIFY_T 0x13 // Verify (Tape) (O) +#define SCSI_WRT_FILE 0x10 // Write Filemarks (MANDATORY) + +//*************************************************************************** +// %%% Commands Unique to Printer Devices %%% +//*************************************************************************** +#define SCSI_PRINT 0x0A // Print (MANDATORY) +#define SCSI_SLEW_PNT 0x0B // Slew and Print (O) +#define SCSI_STOP_PNT 0x1B // Stop Print (O) +#define SCSI_SYNC_BUFF 0x10 // Synchronize Buffer (O) + +//*************************************************************************** +// %%% Commands Unique to Processor Devices %%% +//*************************************************************************** +#define SCSI_RECEIVE 0x08 // Receive (O) +#define SCSI_SEND 0x0A // Send (O) + +//*************************************************************************** +// %%% Commands Unique to Write-Once Devices %%% +//*************************************************************************** +#define SCSI_MEDIUM_SCN 0x38 // Medium Scan (O) +#define SCSI_SRCHDATE10 0x31 // Search Data Equal 10-Byte (O) +#define SCSI_SRCHDATE12 0xB1 // Search Data Equal 12-Byte (O) +#define SCSI_SRCHDATH10 0x30 // Search Data High 10-Byte (O) +#define SCSI_SRCHDATH12 0xB0 // Search Data High 12-Byte (O) +#define SCSI_SRCHDATL10 0x32 // Search Data Low 10-Byte (O) +#define SCSI_SRCHDATL12 0xB2 // Search Data Low 12-Byte (O) +#define SCSI_SET_LIM_10 0x33 // Set Limits 10-Byte (O) +#define SCSI_SET_LIM_12 0xB3 // Set Limits 10-Byte (O) +#define SCSI_VERIFY10 0x2F // Verify 10-Byte (O) +#define SCSI_VERIFY12 0xAF // Verify 12-Byte (O) +#define SCSI_WRITE12 0xAA // Write 12-Byte (O) +#define SCSI_WRT_VER10 0x2E // Write and Verify 10-Byte (O) +#define SCSI_WRT_VER12 0xAE // Write and Verify 12-Byte (O) + +//*************************************************************************** +// %%% Commands Unique to CD-ROM Devices %%% +//*************************************************************************** +#define SCSI_PLAYAUD_10 0x45 // Play Audio 10-Byte (O) +#define SCSI_PLAYAUD_12 0xA5 // Play Audio 12-Byte 12-Byte (O) +#define SCSI_PLAYAUDMSF 0x47 // Play Audio MSF (O) +#define SCSI_PLAYA_TKIN 0x48 // Play Audio Track/Index (O) +#define SCSI_PLYTKREL10 0x49 // Play Track Relative 10-Byte (O) +#define SCSI_PLYTKREL12 0xA9 // Play Track Relative 12-Byte (O) +#define SCSI_READCDCAP 0x25 // Read CD-ROM Capacity (MANDATORY) +#define SCSI_READHEADER 0x44 // Read Header (O) +#define SCSI_SUBCHANNEL 0x42 // Read Subchannel (O) +#define SCSI_READ_TOC 0x43 // Read TOC (O) + +//*************************************************************************** +// %%% Commands Unique to Scanner Devices %%% +//*************************************************************************** +#define SCSI_GETDBSTAT 0x34 // Get Data Buffer Status (O) +#define SCSI_GETWINDOW 0x25 // Get Window (O) +#define SCSI_OBJECTPOS 0x31 // Object Postion (O) +#define SCSI_SCAN 0x1B // Scan (O) +#define SCSI_SETWINDOW 0x24 // Set Window (MANDATORY) + +//*************************************************************************** +// %%% Commands Unique to Optical Memory Devices %%% +//*************************************************************************** +#define SCSI_UpdateBlk 0x3D // Update Block (O) + +//*************************************************************************** +// %%% Commands Unique to Medium Changer Devices %%% +//*************************************************************************** +#define SCSI_EXCHMEDIUM 0xA6 // Exchange Medium (O) +#define SCSI_INITELSTAT 0x07 // Initialize Element Status (O) +#define SCSI_POSTOELEM 0x2B // Position to Element (O) +#define SCSI_REQ_VE_ADD 0xB5 // Request Volume Element Address (O) +#define SCSI_SENDVOLTAG 0xB6 // Send Volume Tag (O) + +//*************************************************************************** +// %%% Commands Unique to Communication Devices %%% +//*************************************************************************** +#define SCSI_GET_MSG_6 0x08 // Get Message 6-Byte (MANDATORY) +#define SCSI_GET_MSG_10 0x28 // Get Message 10-Byte (O) +#define SCSI_GET_MSG_12 0xA8 // Get Message 12-Byte (O) +#define SCSI_SND_MSG_6 0x0A // Send Message 6-Byte (MANDATORY) +#define SCSI_SND_MSG_10 0x2A // Send Message 10-Byte (O) +#define SCSI_SND_MSG_12 0xAA // Send Message 12-Byte (O) + +//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ +// +// %%% END OF SCSI COMMAND OPCODES %%% +// +///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ + +//*************************************************************************** +// %%% Request Sense Data Format %%% +//*************************************************************************** +typedef struct { + + BYTE ErrorCode; // Error Code (70H or 71H) + BYTE SegmentNum; // Number of current segment descriptor + BYTE SenseKey; // Sense Key(See bit definitions too) + BYTE InfoByte0; // Information MSB + BYTE InfoByte1; // Information MID + BYTE InfoByte2; // Information MID + BYTE InfoByte3; // Information LSB + BYTE AddSenLen; // Additional Sense Length + BYTE ComSpecInf0; // Command Specific Information MSB + BYTE ComSpecInf1; // Command Specific Information MID + BYTE ComSpecInf2; // Command Specific Information MID + BYTE ComSpecInf3; // Command Specific Information LSB + BYTE AddSenseCode; // Additional Sense Code + BYTE AddSenQual; // Additional Sense Code Qualifier + BYTE FieldRepUCode; // Field Replaceable Unit Code + BYTE SenKeySpec15; // Sense Key Specific 15th byte + BYTE SenKeySpec16; // Sense Key Specific 16th byte + BYTE SenKeySpec17; // Sense Key Specific 17th byte + BYTE AddSenseBytes; // Additional Sense Bytes + +} SENSE_DATA_FMT; + +//*************************************************************************** +// %%% REQUEST SENSE ERROR CODE %%% +//*************************************************************************** +#define SERROR_CURRENT 0x70 // Current Errors +#define SERROR_DEFERED 0x71 // Deferred Errors + +//*************************************************************************** +// %%% REQUEST SENSE BIT DEFINITIONS %%% +//*************************************************************************** +#define SENSE_VALID 0x80 // Byte 0 Bit 7 +#define SENSE_FILEMRK 0x80 // Byte 2 Bit 7 +#define SENSE_EOM 0x40 // Byte 2 Bit 6 +#define SENSE_ILI 0x20 // Byte 2 Bit 5 + +//*************************************************************************** +// %%% REQUEST SENSE SENSE KEY DEFINITIONS %%% +//*************************************************************************** +#define KEY_NOSENSE 0x00 // No Sense +#define KEY_RECERROR 0x01 // Recovered Error +#define KEY_NOTREADY 0x02 // Not Ready +#define KEY_MEDIUMERR 0x03 // Medium Error +#define KEY_HARDERROR 0x04 // Hardware Error +#define KEY_ILLGLREQ 0x05 // Illegal Request +#define KEY_UNITATT 0x06 // Unit Attention +#define KEY_DATAPROT 0x07 // Data Protect +#define KEY_BLANKCHK 0x08 // Blank Check +#define KEY_VENDSPEC 0x09 // Vendor Specific +#define KEY_COPYABORT 0x0A // Copy Abort +#define KEY_EQUAL 0x0C // Equal (Search) +#define KEY_VOLOVRFLW 0x0D // Volume Overflow +#define KEY_MISCOMP 0x0E // Miscompare (Search) +#define KEY_RESERVED 0x0F // Reserved + +//*************************************************************************** +// %%% PERIPHERAL DEVICE TYPE DEFINITIONS %%% +//*************************************************************************** +#define DTYPE_DASD 0x00 // Disk Device +#define DTYPE_SEQD 0x01 // Tape Device +#define DTYPE_PRNT 0x02 // Printer +#define DTYPE_PROC 0x03 // Processor +#define DTYPE_WORM 0x04 // Write-once read-multiple +#define DTYPE_CROM 0x05 // CD-ROM device +#define DTYPE_CDROM 0x05 // CD-ROM device +#define DTYPE_SCAN 0x06 // Scanner device +#define DTYPE_OPTI 0x07 // Optical memory device +#define DTYPE_JUKE 0x08 // Medium Changer device +#define DTYPE_COMM 0x09 // Communications device +#define DTYPE_RESL 0x0A // Reserved (low) +#define DTYPE_RESH 0x1E // Reserved (high) +#define DTYPE_UNKNOWN 0x1F // Unknown or no device type + +//*************************************************************************** +// %%% ANSI APPROVED VERSION DEFINITIONS %%% +//*************************************************************************** +#define ANSI_MAYBE 0x0 // Device may or may not be ANSI approved stand +#define ANSI_SCSI1 0x1 // Device complies to ANSI X3.131-1986 (SCSI-1) +#define ANSI_SCSI2 0x2 // Device complies to SCSI-2 +#define ANSI_RESLO 0x3 // Reserved (low) +#define ANSI_RESHI 0x7 // Reserved (high) diff --git a/tools/ioemu/iodev/scsipt.h b/tools/ioemu/iodev/scsipt.h new file mode 100644 index 0000000000..b3c8508abf --- /dev/null +++ b/tools/ioemu/iodev/scsipt.h @@ -0,0 +1,144 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: scsipt.h,v 1.4 2002/09/16 16:58:36 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// +// +// iodev/scsipt.h +// $Id: scsipt.h,v 1.4 2002/09/16 16:58:36 bdenney Exp $ +// +// This file was copied from ... ? +// +// distilled information from various header files from Microsoft's +// DDK for Windows NT 4.0 +// + +#ifndef _SCSIPT_H_INC +#define _SCSIPT_H_INC + +#include <windows.h> + +typedef struct { + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + ULONG DataBufferOffset; + ULONG SenseInfoOffset; + UCHAR Cdb[16]; +} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH; + + +typedef struct { + USHORT Length; + UCHAR ScsiStatus; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR CdbLength; + UCHAR SenseInfoLength; + UCHAR DataIn; + ULONG DataTransferLength; + ULONG TimeOutValue; + PVOID DataBuffer; + ULONG SenseInfoOffset; + UCHAR Cdb[16]; +} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT; + + +typedef struct { + SCSI_PASS_THROUGH spt; + ULONG Filler; + UCHAR ucSenseBuf[32]; + UCHAR ucDataBuf[512]; +} SCSI_PASS_THROUGH_WITH_BUFFERS, *PSCSI_PASS_THROUGH_WITH_BUFFERS; + + +typedef struct { + SCSI_PASS_THROUGH_DIRECT spt; + ULONG Filler; + UCHAR ucSenseBuf[32]; +} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, *PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER; + + + +typedef struct { + UCHAR NumberOfLogicalUnits; + UCHAR InitiatorBusId; + ULONG InquiryDataOffset; +} SCSI_BUS_DATA, *PSCSI_BUS_DATA; + + +typedef struct { + UCHAR NumberOfBusses; + SCSI_BUS_DATA BusData[1]; +} SCSI_ADAPTER_BUS_INFO, *PSCSI_ADAPTER_BUS_INFO; + + +typedef struct { + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + BOOLEAN DeviceClaimed; + ULONG InquiryDataLength; + ULONG NextInquiryDataOffset; + UCHAR InquiryData[1]; +} SCSI_INQUIRY_DATA, *PSCSI_INQUIRY_DATA; + + +typedef struct { + ULONG Length; + UCHAR PortNumber; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; +} SCSI_ADDRESS, *PSCSI_ADDRESS; + + +/* + * method codes + */ +#define METHOD_BUFFERED 0 +#define METHOD_IN_DIRECT 1 +#define METHOD_OUT_DIRECT 2 +#define METHOD_NEITHER 3 + +/* + * file access values + */ +#define FILE_ANY_ACCESS 0 +#define FILE_READ_ACCESS (0x0001) +#define FILE_WRITE_ACCESS (0x0002) + + +#define IOCTL_SCSI_BASE 0x00000004 + +/* + * constants for DataIn member of SCSI_PASS_THROUGH* structures + */ +#define SCSI_IOCTL_DATA_OUT 0 +#define SCSI_IOCTL_DATA_IN 1 +#define SCSI_IOCTL_DATA_UNSPECIFIED 2 + +/* + * Standard IOCTL define + */ +#define CTL_CODE( DevType, Function, Method, Access ) ( \ + ((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \ +) + +#define IOCTL_SCSI_PASS_THROUGH CTL_CODE( IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS ) +#define IOCTL_SCSI_MINIPORT CTL_CODE( IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS ) +#define IOCTL_SCSI_GET_INQUIRY_DATA CTL_CODE( IOCTL_SCSI_BASE, 0x0403, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_SCSI_GET_CAPABILITIES CTL_CODE( IOCTL_SCSI_BASE, 0x0404, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE( IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS ) +#define IOCTL_SCSI_GET_ADDRESS CTL_CODE( IOCTL_SCSI_BASE, 0x0406, METHOD_BUFFERED, FILE_ANY_ACCESS ) + + + +#endif diff --git a/tools/ioemu/iodev/serial.cc b/tools/ioemu/iodev/serial.cc new file mode 100644 index 0000000000..02deec3014 --- /dev/null +++ b/tools/ioemu/iodev/serial.cc @@ -0,0 +1,1001 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: serial.cc,v 1.41 2003/11/09 00:14:43 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + +// Peter Grehan (grehan@iprg.nokia.com) coded the original version of this +// serial emulation. He implemented a single 8250, and allow terminal +// input/output to stdout on FreeBSD. +// The current version emulates a single 16550A with FIFO. Terminal +// input/output now works on some more platforms. + + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" + +#define LOG_THIS theSerialDevice-> + +#if USE_RAW_SERIAL +#include <signal.h> +#endif + +#ifdef WIN32 +#ifndef __MINGW32__ +// +++ +//#include <winsock2.h> +#include <winsock.h> +#endif +#endif + +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux__) || defined(__GNU__) || defined(__APPLE__) +#define SERIAL_ENABLE +#endif + +#ifdef SERIAL_ENABLE +extern "C" { +#include <termios.h> +}; +#endif + +#ifdef SERIAL_ENABLE +static struct termios term_orig, term_new; +#endif + +static int tty_id; + +bx_serial_c *theSerialDevice = NULL; + + int +libserial_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + theSerialDevice = new bx_serial_c (); + bx_devices.pluginSerialDevice = theSerialDevice; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theSerialDevice, BX_PLUGIN_SERIAL); + return(0); // Success +} + + void +libserial_LTX_plugin_fini(void) +{ +} + +bx_serial_c::bx_serial_c(void) +{ + put("SER"); + settype(SERLOG); + tty_id = -1; + for (int i=0; i<BX_SERIAL_MAXDEV; i++) { + s[i].tx_timer_index = BX_NULL_TIMER_HANDLE; + s[i].rx_timer_index = BX_NULL_TIMER_HANDLE; + s[i].fifo_timer_index = BX_NULL_TIMER_HANDLE; + } +} + +bx_serial_c::~bx_serial_c(void) +{ +#ifdef SERIAL_ENABLE + if ((bx_options.com[0].Oenabled->get ()) && (tty_id >= 0)) + tcsetattr(tty_id, TCSAFLUSH, &term_orig); +#endif + // nothing for now +} + + + void +bx_serial_c::init(void) +{ + Bit16u ports[BX_SERIAL_MAXDEV] = {0x03f8, 0x02f8, 0x03e8, 0x02e8}; + char name[16]; + + if (!bx_options.com[0].Oenabled->get ()) + return; + +#ifdef SERIAL_ENABLE + if (strlen(bx_options.com[0].Odev->getptr ()) > 0) { + tty_id = open(bx_options.com[0].Odev->getptr (), O_RDWR|O_NONBLOCK,600); + if (tty_id < 0) + BX_PANIC(("open of %s (%s) failed\n", + "com1", bx_options.com[0].Odev->getptr ())); + BX_DEBUG(("tty_id: %d",tty_id)); + tcgetattr(tty_id, &term_orig); + bcopy((caddr_t) &term_orig, (caddr_t) &term_new, sizeof(struct termios)); + cfmakeraw(&term_new); + term_new.c_oflag |= OPOST | ONLCR; // Enable NL to CR-NL translation +#ifndef TRUE_CTLC + // ctl-C will exit Bochs, or trap to the debugger + term_new.c_iflag &= ~IGNBRK; + term_new.c_iflag |= BRKINT; + term_new.c_lflag |= ISIG; +#else + // ctl-C will be delivered to the serial port + term_new.c_iflag |= IGNBRK; + term_new.c_iflag &= ~BRKINT; +#endif /* !def TRUE_CTLC */ + term_new.c_iflag = 0; + term_new.c_oflag = 0; + term_new.c_cflag = CS8|CREAD|CLOCAL; + term_new.c_lflag = 0; + term_new.c_cc[VMIN] = 1; + term_new.c_cc[VTIME] = 0; + //term_new.c_iflag |= IXOFF; + tcsetattr(tty_id, TCSAFLUSH, &term_new); + } +#endif /* def SERIAL_ENABLE */ + // nothing for now +#if USE_RAW_SERIAL + this->raw = new serial_raw("/dev/cua0", SIGUSR1); +#endif // USE_RAW_SERIAL + + /* + * Put the UART registers into their RESET state + */ + for (unsigned i=0; i<BX_N_SERIAL_PORTS; i++) { + if (bx_options.com[i].Oenabled->get ()) { + sprintf(name, "Serial Port %d", i + 1); + /* serial interrupt */ + BX_SER_THIS s[i].IRQ = 4 - (i & 1); + if (i < 2) { + DEV_register_irq(BX_SER_THIS s[i].IRQ, name); + } + /* internal state */ + BX_SER_THIS s[i].ls_ipending = 0; + BX_SER_THIS s[i].ms_ipending = 0; + BX_SER_THIS s[i].rx_ipending = 0; + BX_SER_THIS s[i].fifo_ipending = 0; + BX_SER_THIS s[i].ls_interrupt = 0; + BX_SER_THIS s[i].ms_interrupt = 0; + BX_SER_THIS s[i].rx_interrupt = 0; + BX_SER_THIS s[i].tx_interrupt = 0; + BX_SER_THIS s[i].fifo_interrupt = 0; + + if (BX_SER_THIS s[i].tx_timer_index == BX_NULL_TIMER_HANDLE) { + BX_SER_THIS s[i].tx_timer_index = + bx_pc_system.register_timer(this, tx_timer_handler, 0, + 0,0, "serial.tx"); // one-shot, inactive + } + + if (BX_SER_THIS s[i].rx_timer_index == BX_NULL_TIMER_HANDLE) { + BX_SER_THIS s[i].rx_timer_index = + bx_pc_system.register_timer(this, rx_timer_handler, 0, + 0,0, "serial.rx"); // one-shot, inactive + } + if (BX_SER_THIS s[i].fifo_timer_index == BX_NULL_TIMER_HANDLE) { + BX_SER_THIS s[i].fifo_timer_index = + bx_pc_system.register_timer(this, fifo_timer_handler, 0, + 0,0, "serial.fifo"); // one-shot, inactive + } + BX_SER_THIS s[i].rx_pollstate = BX_SER_RXIDLE; + + /* int enable: b0000 0000 */ + BX_SER_THIS s[i].int_enable.rxdata_enable = 0; + BX_SER_THIS s[i].int_enable.txhold_enable = 0; + BX_SER_THIS s[i].int_enable.rxlstat_enable = 0; + BX_SER_THIS s[i].int_enable.modstat_enable = 0; + + /* int ID: b0000 0001 */ + BX_SER_THIS s[i].int_ident.ipending = 1; + BX_SER_THIS s[i].int_ident.int_ID = 0; + + /* FIFO control: b0000 0000 */ + BX_SER_THIS s[i].fifo_cntl.enable = 0; + BX_SER_THIS s[i].fifo_cntl.rxtrigger = 0; + BX_SER_THIS s[i].rx_fifo_end = 0; + BX_SER_THIS s[i].tx_fifo_end = 0; + + /* Line Control reg: b0000 0000 */ + BX_SER_THIS s[i].line_cntl.wordlen_sel = 0; + BX_SER_THIS s[i].line_cntl.stopbits = 0; + BX_SER_THIS s[i].line_cntl.parity_enable = 0; + BX_SER_THIS s[i].line_cntl.evenparity_sel = 0; + BX_SER_THIS s[i].line_cntl.stick_parity = 0; + BX_SER_THIS s[i].line_cntl.break_cntl = 0; + BX_SER_THIS s[i].line_cntl.dlab = 0; + + /* Modem Control reg: b0000 0000 */ + BX_SER_THIS s[i].modem_cntl.dtr = 0; + BX_SER_THIS s[i].modem_cntl.rts = 0; + BX_SER_THIS s[i].modem_cntl.out1 = 0; + BX_SER_THIS s[i].modem_cntl.out2 = 0; + BX_SER_THIS s[i].modem_cntl.local_loopback = 0; + + /* Line Status register: b0110 0000 */ + BX_SER_THIS s[i].line_status.rxdata_ready = 0; + BX_SER_THIS s[i].line_status.overrun_error = 0; + BX_SER_THIS s[i].line_status.parity_error = 0; + BX_SER_THIS s[i].line_status.framing_error = 0; + BX_SER_THIS s[i].line_status.break_int = 0; + BX_SER_THIS s[i].line_status.thr_empty = 1; + BX_SER_THIS s[i].line_status.tsr_empty = 1; + BX_SER_THIS s[i].line_status.fifo_error = 0; + + /* Modem Status register: bXXXX 0000 */ + BX_SER_THIS s[i].modem_status.delta_cts = 0; + BX_SER_THIS s[i].modem_status.delta_dsr = 0; + BX_SER_THIS s[i].modem_status.ri_trailedge = 0; + BX_SER_THIS s[i].modem_status.delta_dcd = 0; + BX_SER_THIS s[i].modem_status.cts = 0; + BX_SER_THIS s[i].modem_status.dsr = 0; + BX_SER_THIS s[i].modem_status.ri = 0; + BX_SER_THIS s[i].modem_status.dcd = 0; + + BX_SER_THIS s[i].scratch = 0; /* scratch register */ + BX_SER_THIS s[i].divisor_lsb = 1; /* divisor-lsb register */ + BX_SER_THIS s[i].divisor_msb = 0; /* divisor-msb register */ + + BX_SER_THIS s[i].baudrate = 115200; + + for (unsigned addr=ports[i]; addr<(unsigned)(ports[i]+8); addr++) { + BX_DEBUG(("register read/write: 0x%04x",addr)); + DEV_register_ioread_handler(this, read_handler, addr, name, 1); + DEV_register_iowrite_handler(this, write_handler, addr, name, 1); + } + BX_INFO(("com%d at 0x%04x irq %d", i+1, ports[i], BX_SER_THIS s[i].IRQ)); + } + } +} + + void +bx_serial_c::reset(unsigned type) +{ +} + + void +bx_serial_c::lower_interrupt(Bit8u port) +{ + /* If there are no more ints pending, clear the irq */ + if ((BX_SER_THIS s[port].rx_interrupt == 0) && + (BX_SER_THIS s[port].tx_interrupt == 0) && + (BX_SER_THIS s[port].ls_interrupt == 0) && + (BX_SER_THIS s[port].ms_interrupt == 0) && + (BX_SER_THIS s[port].fifo_interrupt == 0)) { + DEV_pic_lower_irq(BX_SER_THIS s[port].IRQ); + } +} + + void +bx_serial_c::raise_interrupt(Bit8u port, int type) +{ + bx_bool gen_int = 0; + + switch (type) { + case BX_SER_INT_IER: /* IER has changed */ + gen_int = 1; + break; + case BX_SER_INT_RXDATA: + if (BX_SER_THIS s[port].int_enable.rxdata_enable) { + BX_SER_THIS s[port].rx_interrupt = 1; + gen_int = 1; + } else { + BX_SER_THIS s[port].rx_ipending = 1; + } + break; + case BX_SER_INT_TXHOLD: + if (BX_SER_THIS s[port].int_enable.txhold_enable) { + BX_SER_THIS s[port].tx_interrupt = 1; + gen_int = 1; + } + break; + case BX_SER_INT_RXLSTAT: + if (BX_SER_THIS s[port].int_enable.rxlstat_enable) { + BX_SER_THIS s[port].ls_interrupt = 1; + gen_int = 1; + } else { + BX_SER_THIS s[port].ls_ipending = 1; + } + break; + case BX_SER_INT_MODSTAT: + if ((BX_SER_THIS s[port].ms_ipending == 1) && + (BX_SER_THIS s[port].int_enable.modstat_enable == 1)) { + BX_SER_THIS s[port].ms_interrupt = 1; + BX_SER_THIS s[port].ms_ipending = 0; + gen_int = 1; + } + break; + case BX_SER_INT_FIFO: + if (BX_SER_THIS s[port].int_enable.rxdata_enable) { + BX_SER_THIS s[port].fifo_interrupt = 1; + gen_int = 1; + } else { + BX_SER_THIS s[port].fifo_ipending = 1; + } + break; + } + if (gen_int && BX_SER_THIS s[port].modem_cntl.out2) { + DEV_pic_raise_irq(BX_SER_THIS s[port].IRQ); + } +} + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_serial_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_SER_SMF + bx_serial_c *class_ptr = (bx_serial_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + Bit32u +bx_serial_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_SER_SMF + //UNUSED(address); + Bit8u val; + + /* SERIAL PORT 1 */ + + BX_DEBUG(("register read from address 0x%04x - ", (unsigned) address)); + + switch (address) { + case 0x03F8: /* receive buffer, or divisor latch LSB if DLAB set */ + if (BX_SER_THIS s[0].line_cntl.dlab) { + val = BX_SER_THIS s[0].divisor_lsb; + } else { + if (BX_SER_THIS s[0].fifo_cntl.enable) { + val = BX_SER_THIS s[0].rx_fifo[0]; + if (BX_SER_THIS s[0].rx_fifo_end > 0) { + memcpy(&BX_SER_THIS s[0].rx_fifo[0], &BX_SER_THIS s[0].rx_fifo[1], 15); + BX_SER_THIS s[0].rx_fifo_end--; + } + if (BX_SER_THIS s[0].rx_fifo_end == 0) { + BX_SER_THIS s[0].line_status.rxdata_ready = 0; + BX_SER_THIS s[0].rx_interrupt = 0; + BX_SER_THIS s[0].rx_ipending = 0; + BX_SER_THIS s[0].fifo_interrupt = 0; + BX_SER_THIS s[0].fifo_ipending = 0; + lower_interrupt(0); + } + } else { + val = BX_SER_THIS s[0].rxbuffer; + BX_SER_THIS s[0].line_status.rxdata_ready = 0; + BX_SER_THIS s[0].rx_interrupt = 0; + BX_SER_THIS s[0].rx_ipending = 0; + lower_interrupt(0); + } + } + break; + + case 0x03F9: /* interrupt enable register, or div. latch MSB */ + if (BX_SER_THIS s[0].line_cntl.dlab) { + val = BX_SER_THIS s[0].divisor_msb; + } else { + val = BX_SER_THIS s[0].int_enable.rxdata_enable | + (BX_SER_THIS s[0].int_enable.txhold_enable << 1) | + (BX_SER_THIS s[0].int_enable.rxlstat_enable << 2) | + (BX_SER_THIS s[0].int_enable.modstat_enable << 3); + } + break; + + case 0x03FA: /* interrupt ID register */ + /* + * Set the interrupt ID based on interrupt source + */ + if (BX_SER_THIS s[0].ls_interrupt) { + BX_SER_THIS s[0].int_ident.int_ID = 0x3; + BX_SER_THIS s[0].int_ident.ipending = 0; + } else if (BX_SER_THIS s[0].fifo_interrupt) { + BX_SER_THIS s[0].int_ident.int_ID = 0x6; + BX_SER_THIS s[0].int_ident.ipending = 0; + } else if (BX_SER_THIS s[0].rx_interrupt) { + BX_SER_THIS s[0].int_ident.int_ID = 0x2; + BX_SER_THIS s[0].int_ident.ipending = 0; + } else if (BX_SER_THIS s[0].tx_interrupt) { + BX_SER_THIS s[0].int_ident.int_ID = 0x1; + BX_SER_THIS s[0].int_ident.ipending = 0; + } else if (BX_SER_THIS s[0].ms_interrupt) { + BX_SER_THIS s[0].int_ident.int_ID = 0x0; + BX_SER_THIS s[0].int_ident.ipending = 0; + } else { + BX_SER_THIS s[0].int_ident.int_ID = 0x0; + BX_SER_THIS s[0].int_ident.ipending = 1; + } + BX_SER_THIS s[0].tx_interrupt = 0; + lower_interrupt(0); + + val = BX_SER_THIS s[0].int_ident.ipending | + (BX_SER_THIS s[0].int_ident.int_ID << 1) | + (BX_SER_THIS s[0].fifo_cntl.enable ? 0xc0 : 0x00); + break; + + case 0x03FB: /* Line control register */ + val = BX_SER_THIS s[0].line_cntl.wordlen_sel | + (BX_SER_THIS s[0].line_cntl.stopbits << 2) | + (BX_SER_THIS s[0].line_cntl.parity_enable << 3) | + (BX_SER_THIS s[0].line_cntl.evenparity_sel << 4) | + (BX_SER_THIS s[0].line_cntl.stick_parity << 5) | + (BX_SER_THIS s[0].line_cntl.break_cntl << 6) | + (BX_SER_THIS s[0].line_cntl.dlab << 7); + break; + + case 0x03FC: /* MODEM control register */ + val = BX_SER_THIS s[0].modem_cntl.dtr | + (BX_SER_THIS s[0].modem_cntl.rts << 1) | + (BX_SER_THIS s[0].modem_cntl.out1 << 2) | + (BX_SER_THIS s[0].modem_cntl.out2 << 3) | + (BX_SER_THIS s[0].modem_cntl.local_loopback << 4); + break; + + case 0x03FD: /* Line status register */ + val = BX_SER_THIS s[0].line_status.rxdata_ready | + (BX_SER_THIS s[0].line_status.overrun_error << 1) | + (BX_SER_THIS s[0].line_status.parity_error << 2) | + (BX_SER_THIS s[0].line_status.framing_error << 3) | + (BX_SER_THIS s[0].line_status.break_int << 4) | + (BX_SER_THIS s[0].line_status.thr_empty << 5) | + (BX_SER_THIS s[0].line_status.tsr_empty << 6) | + (BX_SER_THIS s[0].line_status.fifo_error << 7); + BX_SER_THIS s[0].line_status.overrun_error = 0; + BX_SER_THIS s[0].line_status.break_int = 0; + BX_SER_THIS s[0].ls_interrupt = 0; + BX_SER_THIS s[0].ls_ipending = 0; + lower_interrupt(0); + break; + + case 0x03FE: /* MODEM status register */ + val = BX_SER_THIS s[0].modem_status.delta_cts | + (BX_SER_THIS s[0].modem_status.delta_dsr << 1) | + (BX_SER_THIS s[0].modem_status.ri_trailedge << 2) | + (BX_SER_THIS s[0].modem_status.delta_dcd << 3) | + (BX_SER_THIS s[0].modem_status.cts << 4) | + (BX_SER_THIS s[0].modem_status.dsr << 5) | + (BX_SER_THIS s[0].modem_status.ri << 6) | + (BX_SER_THIS s[0].modem_status.dcd << 7); + BX_SER_THIS s[0].modem_status.delta_cts = 0; + BX_SER_THIS s[0].modem_status.delta_dsr = 0; + BX_SER_THIS s[0].modem_status.ri_trailedge = 0; + BX_SER_THIS s[0].modem_status.delta_dcd = 0; + BX_SER_THIS s[0].ms_interrupt = 0; + BX_SER_THIS s[0].ms_ipending = 0; + lower_interrupt(0); + break; + + case 0x03FF: /* scratch register */ + val = BX_SER_THIS s[0].scratch; + break; + + default: + val = 0; // keep compiler happy + BX_PANIC(("unsupported io read from address=0x%04x!", + (unsigned) address)); + break; + } + + BX_DEBUG(("val = 0x%02x", (unsigned) val)); + + return(val); +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + +void +bx_serial_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_SER_SMF + bx_serial_c *class_ptr = (bx_serial_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + +void +bx_serial_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_SER_SMF + bx_bool prev_cts, prev_dsr, prev_ri, prev_dcd; + bx_bool new_rx_ien, new_tx_ien, new_ls_ien, new_ms_ien; + bx_bool gen_int = 0; + + /* SERIAL PORT 1 */ + + BX_DEBUG(("write to address: 0x%04x = 0x%02x", + (unsigned) address, (unsigned) value)); + + switch (address) { + case 0x03F8: /* transmit buffer, or divisor latch LSB if DLAB set */ + if (BX_SER_THIS s[0].line_cntl.dlab) { + BX_SER_THIS s[0].divisor_lsb = value; + + if ((value != 0) || (BX_SER_THIS s[0].divisor_msb != 0)) { + BX_SER_THIS s[0].baudrate = (int) (BX_PC_CLOCK_XTL / + (16 * ((BX_SER_THIS s[0].divisor_msb << 8) | + BX_SER_THIS s[0].divisor_lsb))); +#if USE_RAW_SERIAL + BX_SER_THIS raw->set_baudrate(BX_SER_THIS s[0].baudrate); +#endif // USE_RAW_SERIAL + } + } else { + Bit8u bitmask = 0xff >> (3 - BX_SER_THIS s[0].line_cntl.wordlen_sel); + if (BX_SER_THIS s[0].line_status.thr_empty) { + if (BX_SER_THIS s[0].fifo_cntl.enable) { + BX_SER_THIS s[0].tx_fifo[BX_SER_THIS s[0].tx_fifo_end++] = value & bitmask; + } else { + BX_SER_THIS s[0].thrbuffer = value & bitmask; + } + BX_SER_THIS s[0].line_status.thr_empty = 0; + if (BX_SER_THIS s[0].line_status.tsr_empty) { + if (BX_SER_THIS s[0].fifo_cntl.enable) { + BX_SER_THIS s[0].tsrbuffer = BX_SER_THIS s[0].tx_fifo[0]; + memcpy(&BX_SER_THIS s[0].tx_fifo[0], &BX_SER_THIS s[0].tx_fifo[1], 15); + BX_SER_THIS s[0].line_status.thr_empty = (--BX_SER_THIS s[0].tx_fifo_end == 0); + } else { + BX_SER_THIS s[0].tsrbuffer = BX_SER_THIS s[0].thrbuffer; + BX_SER_THIS s[0].line_status.thr_empty = 1; + } + BX_SER_THIS s[0].line_status.tsr_empty = 0; + raise_interrupt(0, BX_SER_INT_TXHOLD); + bx_pc_system.activate_timer(BX_SER_THIS s[0].tx_timer_index, + (int) (1000000.0 / BX_SER_THIS s[0].baudrate * + (BX_SER_THIS s[0].line_cntl.wordlen_sel + 5)), + 0); /* not continuous */ + } else { + BX_SER_THIS s[0].tx_interrupt = 0; + lower_interrupt(0); + } + } else { + if (BX_SER_THIS s[0].fifo_cntl.enable) { + if (BX_SER_THIS s[0].tx_fifo_end < 16) { + BX_SER_THIS s[0].tx_fifo[BX_SER_THIS s[0].tx_fifo_end++] = value & bitmask; + } else { + BX_ERROR(("com1: transmit FIFO overflow")); + } + } else { + BX_ERROR(("write to tx hold register when not empty")); + } + } + } + break; + + case 0x03F9: /* interrupt enable register, or div. latch MSB */ + if (BX_SER_THIS s[0].line_cntl.dlab) { + BX_SER_THIS s[0].divisor_msb = value; + + if ((value != 0) || (BX_SER_THIS s[0].divisor_lsb != 0)) { + BX_SER_THIS s[0].baudrate = (int) (BX_PC_CLOCK_XTL / + (16 * ((BX_SER_THIS s[0].divisor_msb << 8) | + BX_SER_THIS s[0].divisor_lsb))); +#if USE_RAW_SERIAL + BX_SER_THIS raw->set_baudrate(BX_SER_THIS s[0].baudrate); +#endif // USE_RAW_SERIAL + } + } else { + new_rx_ien = value & 0x01; + new_tx_ien = (value & 0x02) >> 1; + new_ls_ien = (value & 0x04) >> 2; + new_ms_ien = (value & 0x08) >> 3; + if (new_ms_ien != BX_SER_THIS s[0].int_enable.modstat_enable) { + BX_SER_THIS s[0].int_enable.modstat_enable = new_ms_ien; + if (BX_SER_THIS s[0].int_enable.modstat_enable == 1) { + if (BX_SER_THIS s[0].ms_ipending == 1) { + BX_SER_THIS s[0].ms_interrupt = 1; + BX_SER_THIS s[0].ms_ipending = 0; + gen_int = 1; + } + } else { + if (BX_SER_THIS s[0].ms_interrupt == 1) { + BX_SER_THIS s[0].ms_interrupt = 0; + BX_SER_THIS s[0].ms_ipending = 1; + lower_interrupt(0); + } + } + } + if (new_tx_ien != BX_SER_THIS s[0].int_enable.txhold_enable) { + BX_SER_THIS s[0].int_enable.txhold_enable = new_tx_ien; + if (BX_SER_THIS s[0].int_enable.txhold_enable == 1) { + BX_SER_THIS s[0].tx_interrupt = BX_SER_THIS s[0].line_status.thr_empty; + if (BX_SER_THIS s[0].tx_interrupt) gen_int = 1; + } else { + BX_SER_THIS s[0].tx_interrupt = 0; + lower_interrupt(0); + } + } + if (new_rx_ien != BX_SER_THIS s[0].int_enable.rxdata_enable) { + BX_SER_THIS s[0].int_enable.rxdata_enable = new_rx_ien; + if (BX_SER_THIS s[0].int_enable.rxdata_enable == 1) { + if (BX_SER_THIS s[0].fifo_ipending == 1) { + BX_SER_THIS s[0].fifo_interrupt = 1; + BX_SER_THIS s[0].fifo_ipending = 0; + gen_int = 1; + } + if (BX_SER_THIS s[0].rx_ipending == 1) { + BX_SER_THIS s[0].rx_interrupt = 1; + BX_SER_THIS s[0].rx_ipending = 0; + gen_int = 1; + } + } else { + if (BX_SER_THIS s[0].rx_interrupt == 1) { + BX_SER_THIS s[0].rx_interrupt = 0; + BX_SER_THIS s[0].rx_ipending = 1; + lower_interrupt(0); + } + if (BX_SER_THIS s[0].fifo_interrupt == 1) { + BX_SER_THIS s[0].fifo_interrupt = 0; + BX_SER_THIS s[0].fifo_ipending = 1; + lower_interrupt(0); + } + } + } + if (new_ls_ien != BX_SER_THIS s[0].int_enable.rxlstat_enable) { + BX_SER_THIS s[0].int_enable.rxlstat_enable = new_ls_ien; + if (BX_SER_THIS s[0].int_enable.rxlstat_enable == 1) { + if (BX_SER_THIS s[0].ls_ipending == 1) { + BX_SER_THIS s[0].ls_interrupt = 1; + BX_SER_THIS s[0].ls_ipending = 0; + gen_int = 1; + } + } else { + if (BX_SER_THIS s[0].ls_interrupt == 1) { + BX_SER_THIS s[0].ls_interrupt = 0; + BX_SER_THIS s[0].ls_ipending = 1; + lower_interrupt(0); + } + } + } + if (gen_int) raise_interrupt(0, BX_SER_INT_IER); + } + break; + + case 0x03FA: /* FIFO control register */ + if (!BX_SER_THIS s[0].fifo_cntl.enable && (value & 0x01)) { + BX_INFO(("FIFO enabled")); + BX_SER_THIS s[0].rx_fifo_end = 0; + BX_SER_THIS s[0].tx_fifo_end = 0; + } + BX_SER_THIS s[0].fifo_cntl.enable = value & 0x01; + if (value & 0x02) { + BX_SER_THIS s[0].rx_fifo_end = 0; + } + if (value & 0x04) { + BX_SER_THIS s[0].tx_fifo_end = 0; + } + BX_SER_THIS s[0].fifo_cntl.rxtrigger = (value & 0xc0) >> 6; + break; + + case 0x03FB: /* Line control register */ +#if !USE_RAW_SERIAL + if ((value & 0x3) != 0x3) { + /* ignore this: this is set by FreeBSD when the console + code wants to set DLAB */ + } +#endif // !USE_RAW_SERIAL +#if USE_RAW_SERIAL + if (BX_SER_THIS s[0].line_cntl.wordlen_sel != (value & 0x3)) { + BX_SER_THIS raw->set_data_bits((value & 0x3) + 5); + } + if (BX_SER_THIS s[0].line_cntl.stopbits != (value & 0x4) >> 2) { + BX_SER_THIS raw->set_stop_bits((value & 0x4 >> 2) ? 2 : 1); + } + if (BX_SER_THIS s[0].line_cntl.parity_enable != (value & 0x8) >> 3 || + BX_SER_THIS s[0].line_cntl.evenparity_sel != (value & 0x10) >> 4 || + BX_SER_THIS s[0].line_cntl.stick_parity != (value & 0x20) >> 5) { + if (((value & 0x20) >> 5) && + ((value & 0x8) >> 3)) + BX_PANIC(("sticky parity set and parity enabled")); + BX_SER_THIS raw->set_parity_mode(((value & 0x8) >> 3), + ((value & 0x10) >> 4) ? P_EVEN : P_ODD); + } + if (BX_SER_THIS s[0].line_cntl.break_cntl && !((value & 0x40) >> 6)) { + BX_SER_THIS raw->transmit(C_BREAK); + } +#endif // USE_RAW_SERIAL + + BX_SER_THIS s[0].line_cntl.wordlen_sel = value & 0x3; + /* These are ignored, but set them up so they can be read back */ + BX_SER_THIS s[0].line_cntl.stopbits = (value & 0x4) >> 2; + BX_SER_THIS s[0].line_cntl.parity_enable = (value & 0x8) >> 3; + BX_SER_THIS s[0].line_cntl.evenparity_sel = (value & 0x10) >> 4; + BX_SER_THIS s[0].line_cntl.stick_parity = (value & 0x20) >> 5; + BX_SER_THIS s[0].line_cntl.break_cntl = (value & 0x40) >> 6; + /* used when doing future writes */ + if (BX_SER_THIS s[0].line_cntl.dlab && + !((value & 0x80) >> 7)) { + // Start the receive polling process if not already started + // and there is a valid baudrate. + if (BX_SER_THIS s[0].rx_pollstate == BX_SER_RXIDLE && + BX_SER_THIS s[0].baudrate != 0) { + BX_SER_THIS s[0].rx_pollstate = BX_SER_RXPOLL; + bx_pc_system.activate_timer(BX_SER_THIS s[0].rx_timer_index, + (int) (1000000.0 / BX_SER_THIS s[0].baudrate * + (BX_SER_THIS s[0].line_cntl.wordlen_sel + 5)), + 0); /* not continuous */ + } + BX_DEBUG(("baud rate set - %d", BX_SER_THIS s[0].baudrate)); + } + BX_SER_THIS s[0].line_cntl.dlab = (value & 0x80) >> 7; + break; + + case 0x03FC: /* MODEM control register */ + if ((value & 0x01) == 0) { +#if USE_RAW_SERIAL + BX_SER_THIS raw->send_hangup(); +#endif + } + + BX_SER_THIS s[0].modem_cntl.dtr = value & 0x01; + BX_SER_THIS s[0].modem_cntl.rts = (value & 0x02) >> 1; + BX_SER_THIS s[0].modem_cntl.out1 = (value & 0x04) >> 2; + BX_SER_THIS s[0].modem_cntl.out2 = (value & 0x08) >> 3; + BX_SER_THIS s[0].modem_cntl.local_loopback = (value & 0x10) >> 4; + + if (BX_SER_THIS s[0].modem_cntl.local_loopback) { + prev_cts = BX_SER_THIS s[0].modem_status.cts; + prev_dsr = BX_SER_THIS s[0].modem_status.dsr; + prev_ri = BX_SER_THIS s[0].modem_status.ri; + prev_dcd = BX_SER_THIS s[0].modem_status.dcd; + BX_SER_THIS s[0].modem_status.cts = BX_SER_THIS s[0].modem_cntl.rts; + BX_SER_THIS s[0].modem_status.dsr = BX_SER_THIS s[0].modem_cntl.dtr; + BX_SER_THIS s[0].modem_status.ri = BX_SER_THIS s[0].modem_cntl.out1; + BX_SER_THIS s[0].modem_status.dcd = BX_SER_THIS s[0].modem_cntl.out2; + if (BX_SER_THIS s[0].modem_status.cts != prev_cts) { + BX_SER_THIS s[0].modem_status.delta_cts = 1; + BX_SER_THIS s[0].ms_ipending = 1; + } + if (BX_SER_THIS s[0].modem_status.dsr != prev_dsr) { + BX_SER_THIS s[0].modem_status.delta_dsr = 1; + BX_SER_THIS s[0].ms_ipending = 1; + } + if (BX_SER_THIS s[0].modem_status.ri != prev_ri) + BX_SER_THIS s[0].ms_ipending = 1; + if ((BX_SER_THIS s[0].modem_status.ri == 0) && (prev_ri == 1)) + BX_SER_THIS s[0].modem_status.ri_trailedge = 1; + if (BX_SER_THIS s[0].modem_status.dcd != prev_dcd) { + BX_SER_THIS s[0].modem_status.delta_dcd = 1; + BX_SER_THIS s[0].ms_ipending = 1; + } + raise_interrupt(0, BX_SER_INT_MODSTAT); + } else { + /* set these to 0 for the time being */ + BX_SER_THIS s[0].modem_status.cts = 0; + BX_SER_THIS s[0].modem_status.dsr = 0; + BX_SER_THIS s[0].modem_status.ri = 0; + BX_SER_THIS s[0].modem_status.dcd = 0; + } + break; + + case 0x03FD: /* Line status register */ + BX_ERROR(("write to line status register ignored")); + break; + + case 0x03FE: /* MODEM status register */ + BX_ERROR(("write to MODEM status register ignored")); + break; + + case 0x03FF: /* scratch register */ + BX_SER_THIS s[0].scratch = value; + break; + + default: + BX_PANIC(("unsupported io write to address=0x%04x, value = 0x%02x!", + (unsigned) address, (unsigned) value)); + break; + } +} + + +void +bx_serial_c::rx_fifo_enq(Bit8u port, Bit8u data) +{ + bx_bool gen_int = 0; + + if (BX_SER_THIS s[port].fifo_cntl.enable) { + if (BX_SER_THIS s[port].rx_fifo_end == 16) { + BX_ERROR(("com%d: receive FIFO overflow", port + 1)); + BX_SER_THIS s[port].line_status.overrun_error = 1; + raise_interrupt(port, BX_SER_INT_RXLSTAT); + } else { + BX_SER_THIS s[port].rx_fifo[BX_SER_THIS s[0].rx_fifo_end++] = data; + switch (BX_SER_THIS s[port].fifo_cntl.rxtrigger) { + case 1: + if (BX_SER_THIS s[0].rx_fifo_end == 4) gen_int = 1; + break; + case 2: + if (BX_SER_THIS s[0].rx_fifo_end == 8) gen_int = 1; + break; + case 3: + if (BX_SER_THIS s[0].rx_fifo_end == 14) gen_int = 1; + break; + default: + gen_int = 1; + } + if (gen_int) { + bx_pc_system.deactivate_timer(BX_SER_THIS s[0].fifo_timer_index); + BX_SER_THIS s[port].line_status.rxdata_ready = 1; + raise_interrupt(port, BX_SER_INT_RXDATA); + } else { + bx_pc_system.activate_timer(BX_SER_THIS s[0].fifo_timer_index, + (int) (1000000.0 / BX_SER_THIS s[0].baudrate * + (BX_SER_THIS s[0].line_cntl.wordlen_sel + 5) * 16), + 0); /* not continuous */ + } + } + } else { + if (BX_SER_THIS s[port].line_status.rxdata_ready == 1) { + BX_ERROR(("com%d: overrun error", port + 1)); + BX_SER_THIS s[port].line_status.overrun_error = 1; + raise_interrupt(port, BX_SER_INT_RXLSTAT); + } + BX_SER_THIS s[port].rxbuffer = data; + BX_SER_THIS s[port].line_status.rxdata_ready = 1; + raise_interrupt(port, BX_SER_INT_RXDATA); + } +} + + +void +bx_serial_c::tx_timer_handler(void *this_ptr) +{ + bx_serial_c *class_ptr = (bx_serial_c *) this_ptr; + + class_ptr->tx_timer(); +} + + +void +bx_serial_c::tx_timer(void) +{ + bx_bool gen_int = 0; + + if (BX_SER_THIS s[0].modem_cntl.local_loopback) { + rx_fifo_enq(0, BX_SER_THIS s[0].tsrbuffer); + } else { +#if USE_RAW_SERIAL + if (!BX_SER_THIS raw->ready_transmit()) + BX_PANIC(("Not ready to transmit")); + BX_SER_THIS raw->transmit(BX_SER_THIS s[0].tsrbuffer); +#endif +#if defined(SERIAL_ENABLE) + BX_DEBUG(("write: '%c'", BX_SER_THIS s[0].tsrbuffer)); + if (tty_id >= 0) write(tty_id, (bx_ptr_t) & BX_SER_THIS s[0].tsrbuffer, 1); +#endif + } + + BX_SER_THIS s[0].line_status.tsr_empty = 1; + if (BX_SER_THIS s[0].fifo_cntl.enable && (BX_SER_THIS s[0].tx_fifo_end > 0)) { + BX_SER_THIS s[0].tsrbuffer = BX_SER_THIS s[0].tx_fifo[0]; + BX_SER_THIS s[0].line_status.tsr_empty = 0; + memcpy(&BX_SER_THIS s[0].tx_fifo[0], &BX_SER_THIS s[0].tx_fifo[1], 15); + gen_int = (--BX_SER_THIS s[0].tx_fifo_end == 0); + } else if (!BX_SER_THIS s[0].line_status.thr_empty) { + BX_SER_THIS s[0].tsrbuffer = BX_SER_THIS s[0].thrbuffer; + BX_SER_THIS s[0].line_status.tsr_empty = 0; + gen_int = 1; + } + if (!BX_SER_THIS s[0].line_status.tsr_empty) { + if (gen_int) { + BX_SER_THIS s[0].line_status.thr_empty = 1; + raise_interrupt(0, BX_SER_INT_TXHOLD); + } + bx_pc_system.activate_timer(BX_SER_THIS s[0].tx_timer_index, + (int) (1000000.0 / BX_SER_THIS s[0].baudrate * + (BX_SER_THIS s[0].line_cntl.wordlen_sel + 5)), + 0); /* not continuous */ + } +} + + +void +bx_serial_c::rx_timer_handler(void *this_ptr) +{ + bx_serial_c *class_ptr = (bx_serial_c *) this_ptr; + + class_ptr->rx_timer(); +} + + +void +bx_serial_c::rx_timer(void) +{ +#if BX_HAVE_SELECT +#ifndef __BEOS__ + struct timeval tval; + fd_set fds; +#endif +#endif + int bdrate = BX_SER_THIS s[0].baudrate / (BX_SER_THIS s[0].line_cntl.wordlen_sel + 5); + unsigned char chbuf = 0; + +#if BX_HAVE_SELECT +#ifndef __BEOS__ + tval.tv_sec = 0; + tval.tv_usec = 0; + +// MacOS: I'm not sure what to do with this, since I don't know +// what an fd_set is or what FD_SET() or select() do. They aren't +// declared in the CodeWarrior standard library headers. I'm just +// leaving it commented out for the moment. + + FD_ZERO(&fds); + if (tty_id >= 0) FD_SET(tty_id, &fds); + + if ((BX_SER_THIS s[0].line_status.rxdata_ready == 0) || + (BX_SER_THIS s[0].fifo_cntl.enable)) { +#if USE_RAW_SERIAL + bx_bool rdy; + uint16 data; + if ((rdy = BX_SER_THIS raw->ready_receive())) { + data = BX_SER_THIS raw->receive(); + if (data == C_BREAK) { + BX_DEBUG(("got BREAK")); + BX_SER_THIS s[0].line_status.break_int = 1; + rdy = 0; + } + } + if (rdy) { + chbuf = data; +#elif defined(SERIAL_ENABLE) + if ((tty_id >= 0) && (select(tty_id + 1, &fds, NULL, NULL, &tval) == 1)) { + (void) read(tty_id, &chbuf, 1); + BX_DEBUG(("read: '%c'",chbuf)); +#else + if (0) { +#endif + if (!BX_SER_THIS s[0].modem_cntl.local_loopback) { + rx_fifo_enq(0, chbuf); + } + } else { + if (!BX_SER_THIS s[0].fifo_cntl.enable) { + bdrate = (int) (1000000.0 / 100000); // Poll frequency is 100ms + } + } + } else { + // Poll at 4x baud rate to see if the next-char can + // be read + bdrate *= 4; + } +#endif +#endif + + bx_pc_system.activate_timer(BX_SER_THIS s[0].rx_timer_index, + (int) (1000000.0 / bdrate), + 0); /* not continuous */ +} + + +void +bx_serial_c::fifo_timer_handler(void *this_ptr) +{ + bx_serial_c *class_ptr = (bx_serial_c *) this_ptr; + + class_ptr->fifo_timer(); +} + + +void +bx_serial_c::fifo_timer(void) +{ + BX_SER_THIS s[0].line_status.rxdata_ready = 1; + raise_interrupt(0, BX_SER_INT_FIFO); +} diff --git a/tools/ioemu/iodev/serial.h b/tools/ioemu/iodev/serial.h new file mode 100644 index 0000000000..00f01c976e --- /dev/null +++ b/tools/ioemu/iodev/serial.h @@ -0,0 +1,193 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: serial.h,v 1.15 2003/11/16 08:21:10 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + + +// Peter Grehan (grehan@iprg.nokia.com) coded most of this +// serial emulation. + +#if USE_RAW_SERIAL +#include "serial_raw.h" +#endif // USE_RAW_SERIAL + +#if BX_USE_SER_SMF +# define BX_SER_SMF static +# define BX_SER_THIS theSerialDevice-> +#else +# define BX_SER_SMF +# define BX_SER_THIS this-> +#endif + +#define BX_SERIAL_MAXDEV 4 + +#define BX_PC_CLOCK_XTL 1843200.0 + +#define BX_SER_RXIDLE 0 +#define BX_SER_RXPOLL 1 +#define BX_SER_RXWAIT 2 + +enum { + BX_SER_INT_IER, + BX_SER_INT_RXDATA, + BX_SER_INT_TXHOLD, + BX_SER_INT_RXLSTAT, + BX_SER_INT_MODSTAT, + BX_SER_INT_FIFO +}; + +typedef struct { + /* + * UART internal state + */ + bx_bool ls_interrupt; + bx_bool ms_interrupt; + bx_bool rx_interrupt; + bx_bool tx_interrupt; + bx_bool fifo_interrupt; + bx_bool ls_ipending; + bx_bool ms_ipending; + bx_bool rx_ipending; + bx_bool fifo_ipending; + + Bit8u IRQ; + + Bit8u rx_fifo_end; + Bit8u tx_fifo_end; + + int baudrate; + int tx_timer_index; + + int rx_pollstate; + int rx_timer_index; + int fifo_timer_index; + + /* + * Register definitions + */ + Bit8u rxbuffer; /* receiver buffer register (r/o) */ + Bit8u thrbuffer; /* transmit holding register (w/o) */ + /* Interrupt Enable Register */ + struct { + bx_bool rxdata_enable; /* 1=enable receive data interrupts */ + bx_bool txhold_enable; /* 1=enable tx. holding reg. empty ints */ + bx_bool rxlstat_enable; /* 1=enable rx line status interrupts */ + bx_bool modstat_enable; /* 1=enable modem status interrupts */ + } int_enable; + /* Interrupt Identification Register (r/o) */ + struct { + bx_bool ipending; /* 0=interrupt pending */ + Bit8u int_ID; /* 3-bit interrupt ID */ + } int_ident; + /* FIFO Control Register (w/o) */ + struct { + bx_bool enable; /* 1=enable tx and rx FIFOs */ + Bit8u rxtrigger; /* 2-bit code for rx fifo trigger level */ + } fifo_cntl; + /* Line Control Register (r/w) */ + struct { + Bit8u wordlen_sel; /* 2-bit code for char length */ + bx_bool stopbits; /* select stop bit len */ + bx_bool parity_enable; /* ... */ + bx_bool evenparity_sel; /* ... */ + bx_bool stick_parity; /* ... */ + bx_bool break_cntl; /* 1=send break signal */ + bx_bool dlab; /* divisor latch access bit */ + } line_cntl; + /* MODEM Control Register (r/w) */ + struct { + bx_bool dtr; /* DTR output value */ + bx_bool rts; /* RTS output value */ + bx_bool out1; /* OUTPUT1 value */ + bx_bool out2; /* OUTPUT2 value */ + bx_bool local_loopback; /* 1=loopback mode */ + } modem_cntl; + /* Line Status Register (r/w) */ + struct { + bx_bool rxdata_ready; /* 1=receiver data ready */ + bx_bool overrun_error; /* 1=receive overrun detected */ + bx_bool parity_error; /* 1=rx char has a bad parity bit */ + bx_bool framing_error; /* 1=no stop bit detected for rx char */ + bx_bool break_int; /* 1=break signal detected */ + bx_bool thr_empty; /* 1=tx hold register (or fifo) is empty */ + bx_bool tsr_empty; /* 1=shift reg and hold reg empty */ + bx_bool fifo_error; /* 1=at least 1 err condition in fifo */ + } line_status; + /* Modem Status Register (r/w) */ + struct { + bx_bool delta_cts; /* 1=CTS changed since last read */ + bx_bool delta_dsr; /* 1=DSR changed since last read */ + bx_bool ri_trailedge; /* 1=RI moved from low->high */ + bx_bool delta_dcd; /* 1=CD changed since last read */ + bx_bool cts; /* CTS input value */ + bx_bool dsr; /* DSR input value */ + bx_bool ri; /* RI input value */ + bx_bool dcd; /* DCD input value */ + } modem_status; + + Bit8u scratch; /* Scratch Register (r/w) */ + Bit8u tsrbuffer; /* transmit shift register (internal) */ + Bit8u rx_fifo[16]; /* receive FIFO (internal) */ + Bit8u tx_fifo[16]; /* transmit FIFO (internal) */ + Bit8u divisor_lsb; /* Divisor latch, least-sig. byte */ + Bit8u divisor_msb; /* Divisor latch, most-sig. byte */ +} bx_serial_t; + + + +class bx_serial_c : public bx_devmodel_c { +public: + bx_serial_c(void); + ~bx_serial_c(void); + virtual void init(void); + virtual void reset(unsigned type); +#if USE_RAW_SERIAL + serial_raw* raw; +#endif // USE_RAW_SERIAL + +private: + bx_serial_t s[BX_SERIAL_MAXDEV]; + + static void lower_interrupt(Bit8u port); + static void raise_interrupt(Bit8u port, int type); + + static void rx_fifo_enq(Bit8u port, Bit8u data); + + static void tx_timer_handler(void *); + BX_SER_SMF void tx_timer(void); + + static void rx_timer_handler(void *); + BX_SER_SMF void rx_timer(void); + + static void fifo_timer_handler(void *); + BX_SER_SMF void fifo_timer(void); + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#if !BX_USE_SER_SMF + Bit32u read(Bit32u address, unsigned io_len); + void write(Bit32u address, Bit32u value, unsigned io_len); +#endif + }; + diff --git a/tools/ioemu/iodev/serial_raw.h b/tools/ioemu/iodev/serial_raw.h new file mode 100644 index 0000000000..978c28d29b --- /dev/null +++ b/tools/ioemu/iodev/serial_raw.h @@ -0,0 +1,23 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: serial_raw.h,v 1.2 2001/10/03 13:10:38 bdenney Exp $ +///////////////////////////////////////////////////////////////////////// +// +#include <linux/serial.h> + +#define P_EVEN 0 +#define P_ODD 1 +#define C_BREAK 201 + +class serial_raw : public logfunctions { + public: + serial_raw (char *ttypath, int signal); + void set_baudrate (int rate); + void set_data_bits (int ); + void set_stop_bits (int); + void set_parity_mode (int, int); + void transmit (int byte); + void send_hangup (); + int ready_transmit (); + int ready_receive (); + int receive (); +}; diff --git a/tools/ioemu/iodev/slowdown_timer.cc b/tools/ioemu/iodev/slowdown_timer.cc new file mode 100644 index 0000000000..76d8613ec8 --- /dev/null +++ b/tools/ioemu/iodev/slowdown_timer.cc @@ -0,0 +1,182 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: slowdown_timer.cc,v 1.17.2.1 2004/02/06 22:14:36 danielg4 Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 +// + +#include "bochs.h" +#include <errno.h> + +//These need to stay printfs because they are useless in the log file. +#define BX_SLOWDOWN_PRINTF_FEEDBACK 0 + +#define SECINUSEC 1000000 +#define usectosec(a) ((a)/SECINUSEC) +#define sectousec(a) ((a)*SECINUSEC) +#define nsectousec(a) ((a)/1000) + +#define MSECINUSEC 1000 +#define usectomsec(a) ((a)/MSECINUSEC) + +#if BX_HAVE_USLEEP +# define Qval 1000 +#else +# define Qval SECINUSEC +#endif + +#define MAXMULT 1.5 +#define REALTIME_Q SECINUSEC + +#define LOG_THIS bx_slowdown_timer. + +bx_slowdown_timer_c bx_slowdown_timer; + +bx_slowdown_timer_c::bx_slowdown_timer_c() { + put("STIMER"); + settype(STIMERLOG); + + + s.start_time=0; + s.start_emulated_time=0; + s.timer_handle=BX_NULL_TIMER_HANDLE; +} + +void +bx_slowdown_timer_c::init(void) { + + // Return early if slowdown timer not selected + if ( (bx_options.clock.Osync->get () != BX_CLOCK_SYNC_SLOWDOWN) + && (bx_options.clock.Osync->get () != BX_CLOCK_SYNC_BOTH) ) + return; + + BX_INFO(("using 'slowdown' timer synchronization method")); + s.MAXmultiplier=MAXMULT; + s.Q=Qval; + + if(s.MAXmultiplier<1) + s.MAXmultiplier=1; + + s.start_time=sectousec(time(NULL)); + s.start_emulated_time = bx_pc_system.time_usec(); + s.lasttime=0; + if (s.timer_handle == BX_NULL_TIMER_HANDLE) { + s.timer_handle=bx_pc_system.register_timer(this, timer_handler, 100 , 1, 1, + "slowdown_timer"); + } + bx_pc_system.deactivate_timer(s.timer_handle); + bx_pc_system.activate_timer(s.timer_handle,(Bit32u)s.Q,0); +} + +void +bx_slowdown_timer_c::reset(unsigned type) +{ +} + +void +bx_slowdown_timer_c::timer_handler(void * this_ptr) { + bx_slowdown_timer_c * class_ptr = (bx_slowdown_timer_c *) this_ptr; + + class_ptr->handle_timer(); +} + +void +bx_slowdown_timer_c::handle_timer() { + Bit64u total_emu_time = (bx_pc_system.time_usec()) - s.start_emulated_time; + Bit64u wanttime = s.lasttime+s.Q; + Bit64u totaltime = sectousec(time(NULL)) - s.start_time; + Bit64u thistime=(wanttime>totaltime)?wanttime:totaltime; + +#if BX_SLOWDOWN_PRINTF_FEEDBACK + printf("Entering slowdown timer handler.\n"); +#endif + + /* Decide if we're behind. + * Set interrupt interval accordingly. */ + if(totaltime > total_emu_time) { + bx_pc_system.deactivate_timer(s.timer_handle); + bx_pc_system.activate_timer(s.timer_handle, + (Bit32u)(s.MAXmultiplier * (float)((Bit64s)s.Q)), + 0); +#if BX_SLOWDOWN_PRINTF_FEEDBACK + printf("running at MAX speed\n"); +#endif + } else { + bx_pc_system.deactivate_timer(s.timer_handle); + bx_pc_system.activate_timer(s.timer_handle,s.Q,0); +#if BX_SLOWDOWN_PRINTF_FEEDBACK + printf("running at NORMAL speed\n"); +#endif + } + + /* Make sure we took at least one time quantum. */ + /* This is a little strange. I'll try to explain. + * We're running bochs one second ahead of real time. + * this gives us a very precise division on whether + * we're ahead or behind the second line. + * Basically, here's how it works: + * *****|******************|***********... + * Time Time+1sec + * <^Bochs doesn't delay. + * ^>Bochs delays. + * <^Bochs runs at MAX speed. + * ^>Bochs runs at normal + */ + if(wanttime > (totaltime+REALTIME_Q)) { +#if BX_HAVE_USLEEP + usleep(s.Q); +#elif BX_HAVE_MSLEEP + msleep(usectomsec(s.Q)); +#elif BX_HAVE_SLEEP + sleep(usectosec(s.Q)); +#else +#error do not know have to sleep +#endif //delay(wanttime-totaltime); + /*alternatively: delay(Q); + * This works okay because we share the delay between + * two time quantums. + */ +#if BX_SLOWDOWN_PRINTF_FEEDBACK + printf("DELAYING for a quantum\n"); +#endif + } + s.lasttime=thistime; + + //Diagnostic info: +#if 0 + if(wanttime > (totaltime+REALTIME_Q)) { + if(totaltime > total_emu_time) { + printf("Solving OpenBSD problem.\n"); + } else { + printf("too fast.\n"); + } + } else { + if(totaltime > total_emu_time) { + printf("too slow.\n"); + } else { + printf("sometimes invalid state, normally okay.\n"); + } + } +#endif // Diagnostic info +} + diff --git a/tools/ioemu/iodev/slowdown_timer.h b/tools/ioemu/iodev/slowdown_timer.h new file mode 100644 index 0000000000..3b6b153a71 --- /dev/null +++ b/tools/ioemu/iodev/slowdown_timer.h @@ -0,0 +1,33 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: slowdown_timer.h,v 1.8 2003/08/19 00:10:38 cbothamy Exp $ +///////////////////////////////////////////////////////////////////////// +// + +class bx_slowdown_timer_c : public logfunctions { + +private: + struct { + Bit64u start_time; + Bit64u start_emulated_time; + Bit64u lasttime; + + int timer_handle; + + float MAXmultiplier; + Bit64u Q; // (Q (in seconds)) + } s; + +public: + bx_slowdown_timer_c(); + + void init(void); + void reset(unsigned type); + + static void timer_handler(void * this_ptr); + + void handle_timer(); + +}; + +extern bx_slowdown_timer_c bx_slowdown_timer; + diff --git a/tools/ioemu/iodev/soundlnx.cc b/tools/ioemu/iodev/soundlnx.cc new file mode 100644 index 0000000000..773addc311 --- /dev/null +++ b/tools/ioemu/iodev/soundlnx.cc @@ -0,0 +1,227 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: soundlnx.cc,v 1.6 2002/12/24 10:12:26 vruppert 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 + +// This file (SOUNDLNX.CC) written and donated by Josef Drexler + + +#include "bochs.h" +#if (defined(linux) || defined(__FreeBSD__)) && BX_SUPPORT_SB16 +#define LOG_THIS bx_sb16. + +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/soundcard.h> + +bx_sound_linux_c::bx_sound_linux_c(bx_sb16_c *sb16) + :bx_sound_output_c(sb16) +{ + this->sb16 = sb16; + midi = NULL; + wavedevice = NULL; + wave = -1; +} + +bx_sound_linux_c::~bx_sound_linux_c() +{ + // nothing for now +} + + +int bx_sound_linux_c::waveready() +{ + return BX_SOUND_OUTPUT_OK; +} + +int bx_sound_linux_c::midiready() +{ + return BX_SOUND_OUTPUT_OK; +} + +int bx_sound_linux_c::openmidioutput(char *device) +{ + if ( (device == NULL) || (strlen(device) < 1) ) + return BX_SOUND_OUTPUT_ERR; + + midi = fopen(device,"w"); + + if (midi == NULL) + { + WRITELOG( MIDILOG(2), "Couldn't open midi output device %s: %s.", + device, strerror(errno)); + return BX_SOUND_OUTPUT_ERR; + } + + return BX_SOUND_OUTPUT_OK; +} + + +int bx_sound_linux_c::sendmidicommand(int delta, int command, int length, Bit8u data[]) +{ + UNUSED(delta); + // BX_PANIC(("Sendmidicommand!!"); + + fputc(command, midi); + fwrite(data, 1, length, midi); + fflush(midi); // to start playing immediately + + return BX_SOUND_OUTPUT_OK; +} + + +int bx_sound_linux_c::closemidioutput() +{ + fclose(midi); + + return BX_SOUND_OUTPUT_OK; +} + + +int bx_sound_linux_c::openwaveoutput(char *device) +{ + int length = strlen(device) + 1; + + if (wavedevice != NULL) + delete(wavedevice); + + wavedevice = new char[length]; + + if (wavedevice == NULL) + return BX_SOUND_OUTPUT_ERR; + + strncpy(wavedevice, device, length); + + return BX_SOUND_OUTPUT_OK; +} + +int bx_sound_linux_c::startwaveplayback(int frequency, int bits, int stereo, int format) +{ + int fmt, ret; + int signeddata = format & 1; + + if ( (wavedevice == NULL) || (strlen(wavedevice) < 1) ) + return BX_SOUND_OUTPUT_ERR; + + if (wave == -1) + wave = open(wavedevice, O_WRONLY); + else + if ( (frequency == oldfreq) && + (bits == oldbits) && + (stereo == oldstereo) && + (format == oldformat) ) + return BX_SOUND_OUTPUT_OK; // nothing to do + + oldfreq = frequency; + oldbits = bits; + oldstereo = stereo; + oldformat = format; + + if (wave == -1) + return BX_SOUND_OUTPUT_ERR; + + if (bits == 16) + if (signeddata == 1) + fmt = AFMT_S16_LE; + else + fmt = AFMT_U16_LE; + else if (bits == 8) + if (signeddata == 1) + fmt = AFMT_S8; + else + fmt = AFMT_U8; + else + return BX_SOUND_OUTPUT_ERR; + + // set frequency etc. + ret = ioctl(wave, SNDCTL_DSP_RESET); + if (ret != 0) + WRITELOG( WAVELOG(4), "ioctl(SNDCTL_DSP_RESET): %s", strerror(errno)); + + /* + ret = ioctl(wave, SNDCTL_DSP_SETFRAGMENT, &fragment); + if (ret != 0) + WRITELOG( WAVELOG(4), "ioctl(SNDCTL_DSP_SETFRAGMENT, %d): %s", + fragment, strerror(errno)); + */ + + ret = ioctl(wave, SNDCTL_DSP_SETFMT, &fmt); + if (ret != 0) // abort if the format is unknown, to avoid playing noise + { + WRITELOG( WAVELOG(4), "ioctl(SNDCTL_DSP_SETFMT, %d): %s", + fmt, strerror(errno)); + return BX_SOUND_OUTPUT_ERR; + } + + ret = ioctl(wave, SNDCTL_DSP_STEREO, &stereo); + if (ret != 0) + WRITELOG( WAVELOG(4), "ioctl(SNDCTL_DSP_STEREO, %d): %s", + stereo, strerror(errno)); + + ret = ioctl(wave, SNDCTL_DSP_SPEED, &frequency); + if (ret != 0) + WRITELOG( WAVELOG(4), "ioctl(SNDCTL_DSP_SPEED, %d): %s", + frequency, strerror(errno)); + + // ioctl(wave, SNDCTL_DSP_GETBLKSIZE, &fragment); + // WRITELOG( WAVELOG(4), "current output block size is %d", fragment); + + return BX_SOUND_OUTPUT_OK; +} + +int bx_sound_linux_c::sendwavepacket(int length, Bit8u data[]) +{ + int ret; + + ret = write(wave, data, length); + + return BX_SOUND_OUTPUT_OK; +} + +int bx_sound_linux_c::stopwaveplayback() +{ + // ioctl(wave, SNDCTL_DSP_SYNC); + // close(wave); + // wave = -1; + + return BX_SOUND_OUTPUT_OK; +} + +int bx_sound_linux_c::closewaveoutput() +{ + if (wavedevice != NULL) + delete(wavedevice); + + if (wave != -1) + { + close(wave); + wave = -1; + } + + wavedevice = NULL; + + return BX_SOUND_OUTPUT_OK; +} + +#endif // defined(linux) diff --git a/tools/ioemu/iodev/soundlnx.h b/tools/ioemu/iodev/soundlnx.h new file mode 100644 index 0000000000..8f718b5acd --- /dev/null +++ b/tools/ioemu/iodev/soundlnx.h @@ -0,0 +1,69 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: soundlnx.h,v 1.5 2002/12/24 10:12:26 vruppert 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 + +// This file (SOUNDLNX.H) written and donated by Josef Drexler + + +#if (defined(linux) || defined(__FreeBSD__)) + +#include "bochs.h" + +#define BX_SOUND_LINUX_BUFSIZE BX_SOUND_OUTPUT_WAVEPACKETSIZE + +class bx_sound_linux_c : public bx_sound_output_c { +public: + bx_sound_linux_c(bx_sb16_c *sb16); + BX_SOUND_VIRTUAL ~bx_sound_linux_c(); + + // if virtual functions are used, we have to override them + // and define our own. Otherwise this file will just implement + // the original functions +#ifdef BX_USE_SOUND_VIRTUAL + BX_SOUND_VIRTUAL int waveready(); + BX_SOUND_VIRTUAL int midiready(); + + BX_SOUND_VIRTUAL int openmidioutput(char *device); + BX_SOUND_VIRTUAL int sendmidicommand(int delta, int command, int length, Bit8u data[]); + BX_SOUND_VIRTUAL int closemidioutput(); + + BX_SOUND_VIRTUAL int openwaveoutput(char *device); + BX_SOUND_VIRTUAL int startwaveplayback(int frequency, int bits, int stereo, int format); + BX_SOUND_VIRTUAL int sendwavepacket(int length, Bit8u data[]); + BX_SOUND_VIRTUAL int stopwaveplayback(); + BX_SOUND_VIRTUAL int closewaveoutput(); +#endif + +private: + bx_sb16_c *sb16; + FILE *midi; + char *wavedevice; + int wave; + int bufferpos; + Bit8u audio_buffer[BX_SOUND_LINUX_BUFSIZE]; + int oldfreq,oldbits,oldstereo,oldformat; +}; + +#endif // defined(linux) diff --git a/tools/ioemu/iodev/soundwin.cc b/tools/ioemu/iodev/soundwin.cc new file mode 100644 index 0000000000..b8b386cae4 --- /dev/null +++ b/tools/ioemu/iodev/soundwin.cc @@ -0,0 +1,521 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: soundwin.cc,v 1.13 2003/04/05 08:26:49 vruppert 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 + +// This file (SOUNDWIN.CC) written and donated by Josef Drexler + +// Define BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" +#if defined(WIN32) && BX_SUPPORT_SB16 + +#define LOG_THIS bx_devices.pluginSB16Device-> + +bx_sound_windows_c::bx_sound_windows_c(bx_sb16_c *sb16) + :bx_sound_output_c(sb16) +{ + this->sb16 = sb16; + + MidiOpen = 0; + WaveOpen = 0; + + ismidiready = 1; + iswaveready = 1; + + // size is the total size of the midi header and buffer and the + // BX_SOUND_WINDOWS_NBUF wave header and buffers, all aligned + // on a 16-byte boundary + +#define ALIGN(size) ( (size + 15) & ~15 ) + +#define size ALIGN(sizeof(MIDIHDR)) \ + + ALIGN(sizeof(WAVEHDR)) \ + + ALIGN(BX_SOUND_WINDOWS_MAXSYSEXLEN) * BX_SOUND_WINDOWS_NBUF \ + + ALIGN(BX_SOUND_OUTPUT_WAVEPACKETSIZE) * BX_SOUND_WINDOWS_NBUF + + DataHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, size); + DataPointer = (Bit8u*) GlobalLock(DataHandle); + + if (DataPointer == NULL) + BX_PANIC(("GlobalLock returned NULL-pointer")); + +#define NEWBUFFER(size) &(DataPointer[offset]); offset += ALIGN(size) + + int offset = 0; + MidiHeader = (LPMIDIHDR) NEWBUFFER(sizeof(MIDIHDR)); + MidiData = (LPSTR) NEWBUFFER(BX_SOUND_WINDOWS_MAXSYSEXLEN); + + for (int bufnum=0; bufnum<BX_SOUND_WINDOWS_NBUF; bufnum++) + { + WaveHeader[bufnum] = (LPWAVEHDR) NEWBUFFER(sizeof(WAVEHDR)); + WaveData[bufnum] = (LPSTR) NEWBUFFER(BX_SOUND_OUTPUT_WAVEPACKETSIZE); + } + + if (offset > size) + BX_PANIC(("Allocated memory was too small!")); + +#undef size +#undef ALIGN +#undef NEWBUFFER +} + +bx_sound_windows_c::~bx_sound_windows_c() +{ + GlobalUnlock(DataHandle); + GlobalFree(DataHandle); +} + +int bx_sound_windows_c::waveready() +{ + if (iswaveready == 0) + checkwaveready(); + + if (iswaveready == 1) + return BX_SOUND_OUTPUT_OK; + else + return BX_SOUND_OUTPUT_ERR; +} +int bx_sound_windows_c::midiready() +{ + if (ismidiready == 0) + checkmidiready(); + + if (ismidiready == 1) + return BX_SOUND_OUTPUT_OK; + else + return BX_SOUND_OUTPUT_ERR; +} + +int bx_sound_windows_c::openmidioutput(char *device) +{ + // could make the output device selectable, + // but currently only the midi mapper is supported + UNUSED(device); + + UINT deviceid = (UINT) MIDIMAPPER; + + MidiOpen = 0; + + UINT ret = midiOutOpen( &MidiOut, deviceid, 0, 0, CALLBACK_NULL); + if (ret == 0) + MidiOpen = 1; + + WRITELOG( MIDILOG(4), "midiOutOpen() = %d, MidiOpen: %d", ret, MidiOpen); + + return (MidiOpen == 1) ? BX_SOUND_OUTPUT_OK : BX_SOUND_OUTPUT_ERR; +} + +int bx_sound_windows_c::sendmidicommand(int delta, int command, int length, Bit8u data[]) +{ + UINT ret; + + if (MidiOpen != 1) + return BX_SOUND_OUTPUT_ERR; + + if ( (command == 0xf0) || (command == 0xf7) || (length > 3) ) + { + WRITELOG( WAVELOG(5), "SYSEX started, length %d", length); + ismidiready = 0; // until the buffer is done + memcpy(MidiData, data, length); + MidiHeader->lpData = MidiData; + MidiHeader->dwBufferLength = BX_SOUND_WINDOWS_MAXSYSEXLEN; + MidiHeader->dwBytesRecorded = 0; + MidiHeader->dwUser = 0; + MidiHeader->dwFlags = 0; + ret = midiOutPrepareHeader(MidiOut, MidiHeader, sizeof(*MidiHeader)); + if (ret != 0) + WRITELOG( MIDILOG(2), "midiOutPrepareHeader() = %d", ret); + ret = midiOutLongMsg(MidiOut, MidiHeader, sizeof(*MidiHeader)); + if (ret != 0) + WRITELOG( MIDILOG(2), "midiOutLongMsg() = %d", ret); + } + else + { + DWORD msg = command; + + for (int i = 0; i<length; i++) + msg |= (data[i] << (8 * (i + 1) ) ); + + ret = midiOutShortMsg(MidiOut, msg); + WRITELOG( MIDILOG(4), "midiOutShortMsg(%x) = %d", msg, ret); + } + + return (ret == 0) ? BX_SOUND_OUTPUT_OK : BX_SOUND_OUTPUT_ERR; +} + +int bx_sound_windows_c::closemidioutput() +{ + UINT ret; + + if (MidiOpen != 1) + return BX_SOUND_OUTPUT_ERR; + + ret = midiOutReset(MidiOut); + if (ismidiready == 0) + checkmidiready(); // to clear any pending SYSEX + + ret = midiOutClose(MidiOut); + WRITELOG( MIDILOG(4), "midiOutClose() = %d", ret); + MidiOpen = 0; + + return (ret == 0) ? BX_SOUND_OUTPUT_OK : BX_SOUND_OUTPUT_ERR; +} + +int bx_sound_windows_c::openwaveoutput(char *device) +{ + // could make the output device selectable, + // but currently only the midi mapper is supported + UNUSED(device); + + WRITELOG( WAVELOG(4), "openwaveoutput(%s)", device); + +#ifdef usewaveOut + WaveDevice = (UINT) WAVEMAPPER; + + for (int i=0; i<BX_SOUND_WINDOWS_NBUF; i++) + WaveHeader[i]->dwFlags = WHDR_DONE; + + head = 0; + tailfull = 0; + tailplay = 0; + needreopen = 0; +#endif + + return BX_SOUND_OUTPUT_OK; +} + +int bx_sound_windows_c::playnextbuffer() +{ + UINT ret; + PCMWAVEFORMAT waveformat; + int bufnum; + + // if the format is different, we have to reopen the device, + // so reset it first + if (needreopen != 0) + if (WaveOpen != 0) + ret = waveOutReset( WaveOut ); + + // clean up the buffers and mark if output is ready + checkwaveready(); + + // do we have to play anything? + if (tailplay == head) + return BX_SOUND_OUTPUT_OK; + + // if the format is different, we have to close and reopen the device + // or, just open the device if it's not open yet + if ( (needreopen != 0) || (WaveOpen == 0) ) + { + if (WaveOpen != 0) + { + ret = waveOutClose( WaveOut ); + WaveOpen = 0; + } + + // try three times to find a suitable format + for (int tries = 0; tries < 3; tries++) + { + int frequency = WaveInfo.frequency; + int stereo = WaveInfo.stereo; + int bits = WaveInfo.bits; + int format = WaveInfo.format; + int bps = (bits / 8) * (stereo + 1); + + waveformat.wf.wFormatTag = WAVE_FORMAT_PCM; + waveformat.wf.nChannels = stereo + 1; + waveformat.wf.nSamplesPerSec = frequency; + waveformat.wf.nAvgBytesPerSec = frequency * bps; + waveformat.wf.nBlockAlign = bps; + waveformat.wBitsPerSample = bits; + + ret = waveOutOpen( &(WaveOut), WaveDevice, (LPWAVEFORMATEX)&(waveformat.wf), 0, 0, CALLBACK_NULL); + if (ret != 0) + { + char errormsg[4*MAXERRORLENGTH+1]; + waveOutGetErrorTextA(ret, errormsg, 4*MAXERRORLENGTH+1); + WRITELOG( WAVELOG(5), "waveOutOpen: %s", errormsg); + switch (tries) + { + case 0: // maybe try a different frequency + if (frequency < 15600) + frequency = 11025; + else if (frequency < 31200) + frequency = 22050; + else + frequency = 44100; + + WRITELOG( WAVELOG(4), "Couldn't open wave device (error %d), trying frequency %d", ret, frequency); + + break; + case 1: // or something else + frequency = 11025; + stereo = 0; + bits = 8; + bps = 1; + + WRITELOG( WAVELOG(4), "Couldn't open wave device again (error %d), trying 11KHz, mono, 8bit", ret); + + break; + case 2: // nope, doesn't work + + WRITELOG( WAVELOG(2), "Couldn't open wave device (error %d)!", ret); + + return BX_SOUND_OUTPUT_ERR; + } + WRITELOG( WAVELOG(5), "The format was: wFormatTag=%d, nChannels=%d, nSamplesPerSec=%d,", + waveformat.wf.wFormatTag, waveformat.wf.nChannels, waveformat.wf.nSamplesPerSec); + WRITELOG( WAVELOG(5), " nAvgBytesPerSec=%d, nBlockAlign=%d, wBitsPerSample=%d", + waveformat.wf.nAvgBytesPerSec, waveformat.wf.nBlockAlign, waveformat.wBitsPerSample); + + } + else + { + WaveOpen = 1; + needreopen = 0; + break; + } + } + } + + for (bufnum=tailplay; bufnum != head; + bufnum++, bufnum &= BX_SOUND_WINDOWS_NMASK, tailplay=bufnum) + { + WRITELOG( WAVELOG(5), "Playing buffer %d", bufnum); + + // prepare the wave header + WaveHeader[bufnum]->lpData = WaveData[bufnum]; + WaveHeader[bufnum]->dwBufferLength = length[bufnum]; + WaveHeader[bufnum]->dwBytesRecorded = length[bufnum]; + WaveHeader[bufnum]->dwUser = 0; + WaveHeader[bufnum]->dwFlags = 0; + WaveHeader[bufnum]->dwLoops = 1; + + ret = waveOutPrepareHeader(WaveOut, WaveHeader[bufnum], sizeof(*WaveHeader[bufnum])); + if (ret != 0) + { + WRITELOG( WAVELOG(2), "waveOutPrepareHeader = %d", ret); + return BX_SOUND_OUTPUT_ERR; + } + + ret = waveOutWrite(WaveOut, WaveHeader[bufnum], sizeof(*WaveHeader[bufnum])); + if (ret != 0) + { + char errormsg[4*MAXERRORLENGTH+1]; + waveOutGetErrorTextA(ret, errormsg, 4*MAXERRORLENGTH+1); + WRITELOG( WAVELOG(5), "waveOutWrite: %s", errormsg); + } + } + return BX_SOUND_OUTPUT_OK; +} + +int bx_sound_windows_c::startwaveplayback(int frequency, int bits, int stereo, int format) +{ + // UINT ret; + + WRITELOG( WAVELOG(4), "startwaveplayback(%d, %d, %d, %x)", frequency, bits, stereo, format); + +#ifdef usewaveOut + // check if any of the properties have changed + if ( (WaveInfo.frequency != frequency) || + (WaveInfo.bits != bits) || + (WaveInfo.stereo != stereo) || + (WaveInfo.format != format) ) + { + needreopen = 1; + + // store the current settings to be used by sendwavepacket() + WaveInfo.frequency = frequency; + WaveInfo.bits = bits; + WaveInfo.stereo = stereo; + WaveInfo.format = format; + } +#endif + +#ifdef usesndPlaySnd + int bps = (bits / 8) * (stereo + 1); + LPWAVEFILEHEADER header = (LPWAVEFILEHEADER) WaveData; + + memcpy(header->RIFF, "RIFF", 4); + memcpy(header->TYPE, "WAVE", 4); + memcpy(header->chnk, "fmt ", 4); + header->chnklen = 16; + header->waveformat.wf.wFormatTag = WAVE_FORMAT_PCM; + header->waveformat.wf.nChannels = stereo + 1; + header->waveformat.wf.nSamplesPerSec = frequency; + header->waveformat.wf.nAvgBytesPerSec = frequency * bps; + header->waveformat.wf.nBlockAlign = bps; + header->waveformat.wBitsPerSample = bits; + memcpy(header->chnk2, "data", 4); +#endif + + return BX_SOUND_OUTPUT_OK; +} + +int bx_sound_windows_c::sendwavepacket(int length, Bit8u data[]) +{ +// UINT ret; + int bufnum; + + WRITELOG( WAVELOG(4), "sendwavepacket(%d, %p)", length, data); + +#ifdef usewaveOut + bufnum = head; + + memcpy(WaveData[bufnum], data, length); + this->length[bufnum] = length; + + // select next buffer to write to + bufnum++; + bufnum &= BX_SOUND_WINDOWS_NMASK; + + if ( ( (bufnum + 1) & BX_SOUND_WINDOWS_NMASK) == tailfull ) + { // this should not actually happen! + WRITELOG( WAVELOG(2), "Output buffer overflow! Not played. Iswaveready was %d", iswaveready); + iswaveready = 0; // stop the output for a while + return BX_SOUND_OUTPUT_ERR; + } + + head = bufnum; + + // check if more buffers are available, otherwise stall the emulator + if ( ( (bufnum + 2) & BX_SOUND_WINDOWS_NMASK) == tailfull ) + { + WRITELOG( WAVELOG(5), "Buffer status: Head %d, TailFull %d, TailPlay %d. Stall.", + head, tailfull, tailplay); + iswaveready = 0; + } + + playnextbuffer(); + +#endif + +#ifdef usesndPlaySnd + LPWAVEFILEHEADER header = (LPWAVEFILEHEADER) WaveData; + + header->length = length + 36; + header->chnk2len = length; + + memcpy( &(header->data), data, length); + + FILE *test = fopen("test", "a"); + fwrite(WaveData, 1, length + 44, test); + fclose(test); + + ret = sndPlaySoundA( (LPCSTR) header, SND_SYNC | SND_MEMORY ); + if (ret != 0) + { + WRITELOG( WAVELOG(3), "sndPlaySoundA: %d", ret); + } +#endif + + return BX_SOUND_OUTPUT_OK; +} + +int bx_sound_windows_c::stopwaveplayback() +{ + WRITELOG( WAVELOG(4), "stopwaveplayback()"); + +#ifdef usewaveOut + // this is handled by checkwaveready() when closing +#endif + +#ifdef usesndPlaySnd + sndPlaySoundA( NULL, SND_ASYNC | SND_MEMORY ); + + WaveOpen = 0; +#endif + + return BX_SOUND_OUTPUT_OK; +} + +int bx_sound_windows_c::closewaveoutput() +{ +// int bufnum; + + WRITELOG( WAVELOG(4), "closewaveoutput"); + +#ifdef usewaveOut + if (WaveOpen == 1) + { + waveOutReset(WaveOut); + + // let checkwaveready() clean up the buffers + checkwaveready(); + + waveOutClose(WaveOut); + + head = 0; + tailfull = 0; + tailplay = 0; + needreopen = 0; + } +#endif + + return BX_SOUND_OUTPUT_OK; +} + +void bx_sound_windows_c::checkmidiready() +{ + UINT ret; + + if ( (MidiHeader->dwFlags & WHDR_DONE) != 0) + { + WRITELOG( MIDILOG(5), "SYSEX message done, midi ready again."); + ret = midiOutUnprepareHeader( MidiOut, MidiHeader, sizeof(*MidiHeader)); + ismidiready = 1; + } +} +void bx_sound_windows_c::checkwaveready() +{ + int bufnum; + UINT ret; + + // clean up all finished buffers and mark them as available + for (bufnum=tailfull; + (bufnum != tailplay) && + ( (WaveHeader[bufnum]->dwFlags & WHDR_DONE) != 0); + bufnum++, bufnum &= BX_SOUND_WINDOWS_NMASK) + { + WRITELOG( WAVELOG(5), "Buffer %d done.", bufnum); + + ret = waveOutUnprepareHeader(WaveOut, WaveHeader[bufnum], sizeof(*WaveHeader[bufnum])); + } + + tailfull = bufnum; + + // enable gathering data if a buffer is available + if ( ( (head + 2) & BX_SOUND_WINDOWS_NMASK) != tailfull ) + { + WRITELOG( WAVELOG(5), "Buffer status: Head %d, TailFull %d, TailPlay %d. Ready.", + head, tailfull, tailplay); + iswaveready = 1; + } +} + +#endif // defined(WIN32) diff --git a/tools/ioemu/iodev/soundwin.h b/tools/ioemu/iodev/soundwin.h new file mode 100644 index 0000000000..122fa554e9 --- /dev/null +++ b/tools/ioemu/iodev/soundwin.h @@ -0,0 +1,229 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: soundwin.h,v 1.3 2001/10/03 13:10:38 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 + +// This file (SOUNDWIN.H) written and donated by Josef Drexler + + +#if defined(WIN32) + +#include "bochs.h" +#include <windows.h> + +// uncomment one of the following two #defines +//#define usesndPlaySnd +#define usewaveOut + +#define BX_SOUND_WINDOWS_MAXSYSEXLEN 256 // maximum supported length of a sysex message + +#define BX_SOUND_WINDOWS_NBUF 4 // number of buffers for the output, must be power of 2 and >= 4 +#define BX_SOUND_WINDOWS_NMASK (BX_SOUND_WINDOWS_NBUF - 1) + +#ifndef WAVEMAPPER +#define WAVEMAPPER -1 +#endif + +// Definitions for WINMM.DLL, if not defined already +#ifndef MMSYSERR_NOERROR + +#pragma pack(1) + +typedef UINT HMIDIOUT; +typedef HMIDIOUT *LPHMIDIOUT; +typedef struct midihdr_tag { + LPSTR lpData; + DWORD dwBufferLength; + DWORD dwBytesRecorded; + DWORD dwUser; + DWORD dwFlags; + struct midihdr_tag *lpNext; + DWORD reserved; +} MIDIHDR, *LPMIDIHDR; + +typedef UINT HWAVEOUT; +typedef HWAVEOUT *LPHWAVEOUT; + +typedef struct wavehdr_tag { + LPSTR lpData; + DWORD dwBufferLength; + DWORD dwBytesRecorded; + DWORD dwUser; + DWORD dwFlags; + DWORD dwLoops; + struct wavehdr_tag *lpNext; + DWORD reserved; +} WAVEHDR, *LPWAVEHDR; + +#define WHDR_DONE 0x00000001 +#define WHDR_PREPARED 0x00000002 +#define WHDR_BEGINLOOP 0x00000004 +#define WHDR_ENDLOOP 0x00000008 +#define WHDR_INQUEUE 0x00000010 + + +typedef struct waveformat_tag { + WORD wFormatTag; + WORD nChannels; + DWORD nSamplesPerSec; + DWORD nAvgBytesPerSec; + WORD nBlockAlign; +} WAVEFORMAT, *LPWAVEFORMAT; + +#define WAVE_FORMAT_PCM 1 + +typedef struct pcmwaveformat_tag { + WAVEFORMAT wf; + WORD wBitsPerSample; +} PCMWAVEFORMAT, *LPPCMWAVEFORMAT; + +#define MIDIMAPPER -1 + +#define CALLBACK_NULL 0x00000000 +#define CALLBACK_WINDOW 0x00010000 +#define CALLBACK_TASK 0x00020000 +#define CALLBACK_FUNCTION 0x00030000 + +#define MMSYSERR_NOERROR 0 +#define MMSYSERR_ERROR 1 +#define MMSYSERR_BADDEVICEID 2 +#define MMSYSERR_NOTENABLED 3 +#define MMSYSERR_ALLOCATED 4 +#define MMSYSERR_INVALHANDLE 5 +#define MMSYSERR_NODRIVER 6 +#define MMSYSERR_NOMEM 7 +#define MMSYSERR_NOTSUPPORTED 8 +#define MMSYSERR_NOMAP 7 + +#define MIDIERR_UNPREPARED 64 +#define MIDIERR_STILLPLAYING 65 +#define MIDIERR_NOTREADY 66 +#define MIDIERR_NODEVICE 67 + +#define WAVERR_BADFORMAT 32 +#define WAVERR_STILLPLAYING 33 +#define WAVERR_UNPREPARED 34 +#define WAVERR_SYNC 35 + +#define MAXERRORLENGTH 128 + +extern "C" { +UINT STDCALL midiOutOpen(LPHMIDIOUT, UINT, DWORD, DWORD, DWORD); +UINT STDCALL midiOutShortMsg(HMIDIOUT, DWORD); +UINT STDCALL midiOutLongMsg(HMIDIOUT, LPMIDIHDR, UINT); +UINT STDCALL midiOutPrepareHeader(HMIDIOUT, LPMIDIHDR, UINT); +UINT STDCALL midiOutUnprepareHeader(HMIDIOUT, LPMIDIHDR, UINT); +UINT STDCALL midiOutReset(HMIDIOUT); +UINT STDCALL midiOutClose(HMIDIOUT); + +UINT STDCALL waveOutOpen(LPHWAVEOUT, UINT, LPWAVEFORMAT, DWORD, DWORD, DWORD); +UINT STDCALL waveOutWrite(HWAVEOUT, LPWAVEHDR, UINT); +UINT STDCALL waveOutPrepareHeader(HWAVEOUT, LPWAVEHDR, UINT); +UINT STDCALL waveOutUnprepareHeader(HWAVEOUT, LPWAVEHDR, UINT); +UINT STDCALL waveOutReset(HWAVEOUT); +UINT STDCALL waveOutClose(HWAVEOUT); + +UINT STDCALL waveOutGetErrorTextA(UINT, LPSTR, UINT); + +BOOL STDCALL sndPlaySoundA(LPCSTR, UINT); +} + +typedef struct { + char RIFF[4]; + Bit32u length; + char TYPE[4]; + char chnk[4]; + Bit32u chnklen; + PCMWAVEFORMAT waveformat; + char chnk2[4]; + Bit32u chnk2len; + char data[1]; +} WAVEFILEHEADER, *LPWAVEFILEHEADER; +#pragma pack(0) + +#endif // MMSYSERR_NOERROR defined + +class bx_sound_windows_c : public bx_sound_output_c { +public: + bx_sound_windows_c(bx_sb16_c *sb16); + BX_SOUND_VIRTUAL ~bx_sound_windows_c(); + + // if virtual functions are used, we have to override them + // and define our own. Otherwise this file will just implement + // the original functions +#ifdef BX_USE_SOUND_VIRTUAL + BX_SOUND_VIRTUAL int waveready(); + BX_SOUND_VIRTUAL int midiready(); + + BX_SOUND_VIRTUAL int openmidioutput(char *device); + BX_SOUND_VIRTUAL int sendmidicommand(int delta, int command, int length, Bit8u data[]); + BX_SOUND_VIRTUAL int closemidioutput(); + + BX_SOUND_VIRTUAL int openwaveoutput(char *device); + BX_SOUND_VIRTUAL int startwaveplayback(int frequency, int bits, int stereo, int format); + BX_SOUND_VIRTUAL int sendwavepacket(int length, Bit8u data[]); + BX_SOUND_VIRTUAL int stopwaveplayback(); + BX_SOUND_VIRTUAL int closewaveoutput(); +#endif + +private: + bx_sb16_c *sb16; + + struct bx_sb16_waveinfo_struct { + int frequency; + int bits; + int stereo; + int format; + }; + + HMIDIOUT MidiOut; // Midi output device + int MidiOpen; // is it open? + HWAVEOUT WaveOut; // Wave output device + int WaveOpen; // is it open? + + UINT WaveDevice; // Wave device ID, for waveOutOpen + + // some data for the wave buffers + HANDLE DataHandle; // returned by GlobalAlloc() + Bit8u *DataPointer; // returned by GlobalLock() + + LPWAVEHDR WaveHeader[BX_SOUND_WINDOWS_NBUF]; + LPSTR WaveData[BX_SOUND_WINDOWS_NBUF]; + int length[BX_SOUND_WINDOWS_NBUF]; // length of the data in the buffer + int needreopen; // if the format has changed + int head,tailfull,tailplay; // These are for three states of the buffers: empty, full, played + bx_sb16_waveinfo_struct WaveInfo; // format for the next buffer to be played + int iswaveready; + + // and the midi buffer for the SYSEX messages + LPMIDIHDR MidiHeader; + LPSTR MidiData; + int ismidiready; + + int playnextbuffer(); + void checkmidiready(); + void checkwaveready(); +}; + +#endif // defined(WIN32) diff --git a/tools/ioemu/iodev/state_file.cc b/tools/ioemu/iodev/state_file.cc new file mode 100644 index 0000000000..f7d0d0feef --- /dev/null +++ b/tools/ioemu/iodev/state_file.cc @@ -0,0 +1,136 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: state_file.cc,v 1.9 2001/12/21 19:33:18 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 + + + +// Classes for helping to make checkpoints of the emulator state. + + + +#include "bochs.h" +#define LOG_THIS log-> + + + +FILE *state_file::get_handle() +{ + BX_INFO(("state_file::get_handle()")); + return NULL; +} + +void state_file::write(Bit8u) +{ + BX_PANIC(("state_file::write(Bit8u)")); +} + +void state_file::write(Bit16u) +{ + BX_PANIC(("state_file::write(Bit16u)")); +} + +void state_file::write(Bit32u) +{ + BX_PANIC(("state_file::write(Bit32u)")); +} + +void state_file::write(Bit64u) +{ + BX_PANIC(("state_file::write(Bit64u)")); +} + +void state_file::write(const void *, size_t) +{ + BX_PANIC(("state_file::write(const void *, size_t)")); +} + +void state_file::read(Bit8u &) +{ + BX_PANIC(("state_file::read(uint8 &)")); +} + +void state_file::read(Bit16u &) +{ + BX_PANIC(("state_file::read(uint16 &)")); +} + +void state_file::read(Bit32u &) +{ + BX_PANIC(("state_file::read(uint32 &)")); +} + +void state_file::read(Bit64u &) +{ + BX_PANIC(("state_file::read(uint64 &)")); +} + +void state_file::read(void *, size_t) +{ + BX_PANIC(("state_file::read(void *, size_t)")); +} + +void state_file::write_check(const char *) +{ + BX_PANIC(("state_file::write_check()")); +} + +void state_file::read_check (const char *) +{ + BX_PANIC(("state_file::read_check()")); +} + +void +state_file::init(void) +{ + log = new class logfunctions(); + log->put("STAT"); + log->settype(GENLOG); +} + + +state_file::state_file (const char *name, const char *options) +{ + UNUSED(name); + UNUSED(options); + init(); + BX_DEBUG(( "Init(const char *, const char *)." )); +} + +state_file::state_file (FILE *f) +{ + UNUSED(f); + init(); + BX_INFO(("Init(FILE *).")); +} + +state_file::~state_file() +{ + BX_DEBUG(("Exit.")); + if ( log != NULL ) + { + delete log; + log = NULL; + } +} diff --git a/tools/ioemu/iodev/unmapped.cc b/tools/ioemu/iodev/unmapped.cc new file mode 100644 index 0000000000..5c7aafedb1 --- /dev/null +++ b/tools/ioemu/iodev/unmapped.cc @@ -0,0 +1,305 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: unmapped.cc,v 1.22 2003/08/10 17:19:49 akrisak 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 BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" + +#define LOG_THIS theUnmappedDevice-> + + +bx_unmapped_c *theUnmappedDevice = NULL; + + int +libunmapped_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + theUnmappedDevice = new bx_unmapped_c (); + bx_devices.pluginUnmapped = theUnmappedDevice; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theUnmappedDevice, BX_PLUGIN_UNMAPPED); + return(0); // Success +} + + void +libunmapped_LTX_plugin_fini(void) +{ +} + +bx_unmapped_c::bx_unmapped_c(void) +{ + put("UNMP"); + settype(UNMAPLOG); + s.port80 = 0x00; + s.port8e = 0x00; + s.shutdown = 0; +} + +bx_unmapped_c::~bx_unmapped_c(void) +{ + // Nothing yet +} + + void +bx_unmapped_c::init(void) +{ + DEV_register_default_ioread_handler(this, read_handler, "Unmapped", 7); + DEV_register_default_iowrite_handler(this, write_handler, "Unmapped", 7); +} + + void +bx_unmapped_c::reset(unsigned type) +{ +} + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_unmapped_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_UM_SMF + bx_unmapped_c *class_ptr = (bx_unmapped_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + Bit32u +bx_unmapped_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_UM_SMF + UNUSED(io_len); + + Bit32u retval; + + // This function gets called for access to any IO ports which + // are not mapped to any device handler. Reads return 0 + + if (address >= 0x02e0 && address <= 0x02ef) { + retval = 0; + goto return_from_read; + } + + switch (address) { + case 0x80: + retval = BX_UM_THIS s.port80; + break; + case 0x8e: + retval = BX_UM_THIS s.port8e; + break; +#if BX_PORT_E9_HACK + // Unused port on ISA - this can be used by the emulated code + // to detect it is running inside Bochs and that the debugging + // features are available (write 0xFF or something on unused + // port 0x80, then read from 0xe9, if value is 0xe9, debug + // output is available) (see write() for that) -- Andreas and Emmanuel + case 0xe9: + retval = 0xe9; + break; +#endif + case 0x03df: + retval = 0xffffffff; + BX_DEBUG(("unsupported IO read from port %04x (CGA)", address)); + break; + case 0x023a: + case 0x02f8: /* UART */ + case 0x02f9: /* UART */ + case 0x02fb: /* UART */ + case 0x02fc: /* UART */ + case 0x02fd: /* UART */ + case 0x02ea: + case 0x02eb: + case 0x03e8: + case 0x03e9: + case 0x03ea: + case 0x03eb: + case 0x03ec: + case 0x03ed: + case 0x03f8: /* UART */ + case 0x03f9: /* UART */ + case 0x03fb: /* UART */ + case 0x03fc: /* UART */ + case 0x03fd: /* UART */ + case 0x17c6: + retval = 0xffffffff; + BX_DEBUG(("unsupported IO read from port %04x", address)); + break; + default: + retval = 0xffffffff; + } + + return_from_read: + if (bx_dbg.unsupported_io) + switch (io_len) { + case 1: + retval = (Bit8u)retval; + BX_DEBUG(("unmapped: 8-bit read from %04x = %02x", address, retval)); + break; + case 2: + retval = (Bit16u)retval; + BX_DEBUG(("unmapped: 16-bit read from %04x = %04x", address, retval)); + break; + case 4: + BX_DEBUG(("unmapped: 32-bit read from %04x = %08x", address, retval)); + break; + default: + BX_DEBUG(("unmapped: %d-bit read from %04x = %x", io_len * 8, address, retval)); + } + return retval; +} + + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_unmapped_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_UM_SMF + bx_unmapped_c *class_ptr = (bx_unmapped_c *) this_ptr; + + class_ptr->write(address, value, io_len); +} + + void +bx_unmapped_c::write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_UM_SMF + UNUSED(io_len); + + + // This function gets called for access to any IO ports which + // are not mapped to any device handler. Writes to an unmapped + // IO port are ignored. + +// ??? + + if (address >= 0x02e0 && address <= 0x02ef) + goto return_from_write; + + switch (address) { + case 0x80: // diagnostic test port to display progress of POST + //BX_DEBUG(("Diagnostic port 80h: write = %02xh", (unsigned) value)); + BX_UM_THIS s.port80 = value; + break; + + case 0x8e: // ??? + BX_UM_THIS s.port8e = value; + break; + +#if BX_PORT_E9_HACK + // This port doesn't exist on normal ISA architecture. However, + // we define a convention here, to display on the console of the + // system running Bochs, anything that is written to it. The + // idea is to provide debug output very early when writing + // BIOS or OS code for example, without having to bother with + // properly setting up a serial port or anything. + // + // Idea by Andreas Beck (andreas.beck@ggi-project.org) + + case 0xe9: + putchar(value); + fflush(stdout); + break; +#endif + + case 0xed: // Dummy port used as I/O delay + break; + case 0xee: // ??? + break; + + case 0x2f2: + case 0x2f3: + case 0x2f4: + case 0x2f5: + case 0x2f6: + case 0x2f7: + case 0x3e8: + case 0x3e9: + case 0x3eb: + case 0x3ec: + case 0x3ed: + // BX_DEBUG(("unsupported IO write to port %04x of %02x", + // address, value)); + break; + + case 0x8900: // Shutdown port, could be moved in a PM device + // or a host <-> guest communication device + switch (value) { + case 'S': if (BX_UM_THIS s.shutdown == 0) BX_UM_THIS s.shutdown = 1; break; + case 'h': if (BX_UM_THIS s.shutdown == 1) BX_UM_THIS s.shutdown = 2; break; + case 'u': if (BX_UM_THIS s.shutdown == 2) BX_UM_THIS s.shutdown = 3; break; + case 't': if (BX_UM_THIS s.shutdown == 3) BX_UM_THIS s.shutdown = 4; break; + case 'd': if (BX_UM_THIS s.shutdown == 4) BX_UM_THIS s.shutdown = 5; break; + case 'o': if (BX_UM_THIS s.shutdown == 5) BX_UM_THIS s.shutdown = 6; break; + case 'w': if (BX_UM_THIS s.shutdown == 6) BX_UM_THIS s.shutdown = 7; break; + case 'n': if (BX_UM_THIS s.shutdown == 7) BX_UM_THIS s.shutdown = 8; break; +#if BX_DEBUGGER + // Very handy for debugging: + // output 'D' to port 8900, and bochs quits to debugger + case 'D': bx_debug_break (); break; +#endif + default : BX_UM_THIS s.shutdown = 0; break; + } + if (BX_UM_THIS s.shutdown == 8) { + bx_user_quit = 1; + LOG_THIS setonoff(LOGLEV_PANIC, ACT_FATAL); + BX_PANIC(("Shutdown port: shutdown requested")); + } + break; + + case 0xfedc: + bx_dbg.debugger = (value > 0); + BX_DEBUG(( "DEBUGGER = %u", (unsigned) bx_dbg.debugger)); + break; + + default: + break; + } + return_from_write: + if (bx_dbg.unsupported_io) + switch (io_len) { + case 1: + BX_INFO(("unmapped: 8-bit write to %04x = %02x", address, value)); + break; + case 2: + BX_INFO(("unmapped: 16-bit write to %04x = %04x", address, value)); + break; + case 4: + BX_INFO(("unmapped: 32-bit write to %04x = %08x", address, value)); + break; + default: + BX_INFO(("unmapped: %d-bit write to %04x = %x", io_len * 8, address, value)); + break; + } +} diff --git a/tools/ioemu/iodev/unmapped.h b/tools/ioemu/iodev/unmapped.h new file mode 100644 index 0000000000..c9ef1dc068 --- /dev/null +++ b/tools/ioemu/iodev/unmapped.h @@ -0,0 +1,64 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: unmapped.h,v 1.10 2002/10/24 21:07:52 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 + + + + +#if BX_USE_UM_SMF +# define BX_UM_SMF static +# define BX_UM_THIS theUnmappedDevice-> +#else +# define BX_UM_SMF +# define BX_UM_THIS this-> +#endif + + + +class bx_unmapped_c : public bx_devmodel_c { +public: + bx_unmapped_c(void); + ~bx_unmapped_c(void); + + virtual void init(void); + virtual void reset (unsigned type); + +private: + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#if !BX_USE_UM_SMF + Bit32u read(Bit32u address, unsigned io_len); + void write(Bit32u address, Bit32u value, unsigned io_len); +#endif + + + struct { + Bit8u port80; + Bit8u port8e; + Bit8u shutdown; + } s; // state information + + }; diff --git a/tools/ioemu/iodev/vga.cc b/tools/ioemu/iodev/vga.cc new file mode 100644 index 0000000000..894f80bbb6 --- /dev/null +++ b/tools/ioemu/iodev/vga.cc @@ -0,0 +1,3116 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: vga.cc,v 1.94.2.1 2004/02/02 22:37:48 cbothamy Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 BX_PLUGGABLE in files that can be compiled into plugins. For +// platforms that require a special tag on exported symbols, BX_PLUGGABLE +// is used to know when we are exporting symbols and when we are importing. +#define BX_PLUGGABLE + +#include "bochs.h" + +#define LOG_THIS theVga-> + +/* NOTES: + * I take it data rotate is a true rotate with carry of bit 0 to bit 7. + * support map mask (3c5 reg 02) + */ + +/* Notes from cb + * + * It seems that the vga card should support multi bytes IO reads and write + * From my tests, inw(port) return port+1 * 256 + port, except for port 0x3c9 + * (PEL data register, data cycling). More reverse engineering is needed. + * This would fix the gentoo bug. + */ + +// (mch) +#define VGA_TRACE_FEATURE + +// Only reference the array if the tile numbers are within the bounds +// of the array. If out of bounds, do nothing. +#define SET_TILE_UPDATED(xtile,ytile,value) \ + do { \ + if (((xtile) < BX_NUM_X_TILES) && ((ytile) < BX_NUM_Y_TILES)) \ + BX_VGA_THIS s.vga_tile_updated[(xtile)][(ytile)] = value; \ + } while (0) + +// Only reference the array if the tile numbers are within the bounds +// of the array. If out of bounds, return 0. +#define GET_TILE_UPDATED(xtile,ytile) \ + ((((xtile) < BX_NUM_X_TILES) && ((ytile) < BX_NUM_Y_TILES))? \ + BX_VGA_THIS s.vga_tile_updated[(xtile)][(ytile)] \ + : 0) + +static const Bit8u ccdat[16][4] = { + { 0x00, 0x00, 0x00, 0x00 }, + { 0xff, 0x00, 0x00, 0x00 }, + { 0x00, 0xff, 0x00, 0x00 }, + { 0xff, 0xff, 0x00, 0x00 }, + { 0x00, 0x00, 0xff, 0x00 }, + { 0xff, 0x00, 0xff, 0x00 }, + { 0x00, 0xff, 0xff, 0x00 }, + { 0xff, 0xff, 0xff, 0x00 }, + { 0x00, 0x00, 0x00, 0xff }, + { 0xff, 0x00, 0x00, 0xff }, + { 0x00, 0xff, 0x00, 0xff }, + { 0xff, 0xff, 0x00, 0xff }, + { 0x00, 0x00, 0xff, 0xff }, + { 0xff, 0x00, 0xff, 0xff }, + { 0x00, 0xff, 0xff, 0xff }, + { 0xff, 0xff, 0xff, 0xff }, +}; + +bx_vga_c *theVga = NULL; + +unsigned old_iHeight = 0, old_iWidth = 0, old_MSL = 0, old_BPP = 0; + + int +libvga_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]) +{ + theVga = new bx_vga_c (); + bx_devices.pluginVgaDevice = theVga; + BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theVga, BX_PLUGIN_VGA); + return(0); // Success +} + + void +libvga_LTX_plugin_fini(void) +{ +} + +bx_vga_c::bx_vga_c(void) +{ + put("VGA"); + s.vga_mem_updated = 0; + s.x_tilesize = X_TILESIZE; + s.y_tilesize = Y_TILESIZE; + timer_id = BX_NULL_TIMER_HANDLE; +} + + +bx_vga_c::~bx_vga_c(void) +{ + // nothing for now +} + + + void +bx_vga_c::init(void) +{ + unsigned i; + unsigned x,y; + + unsigned addr; + for (addr=0x03B4; addr<=0x03B5; addr++) { + DEV_register_ioread_handler(this, read_handler, addr, "vga video", 1); + DEV_register_iowrite_handler(this, write_handler, addr, "vga video", 3); + } + + for (addr=0x03BA; addr<=0x03BA; addr++) { + DEV_register_ioread_handler(this, read_handler, addr, "vga video", 1); + DEV_register_iowrite_handler(this, write_handler, addr, "vga video", 3); + } + + for (addr=0x03C0; addr<=0x03CF; addr++) { + DEV_register_ioread_handler(this, read_handler, addr, "vga video", 1); + DEV_register_iowrite_handler(this, write_handler, addr, "vga video", 3); + } + + for (addr=0x03D4; addr<=0x03D5; addr++) { + DEV_register_ioread_handler(this, read_handler, addr, "vga video", 1); + DEV_register_iowrite_handler(this, write_handler, addr, "vga video", 3); + } + + for (addr=0x03DA; addr<=0x03DA; addr++) { + DEV_register_ioread_handler(this, read_handler, addr, "vga video", 1); + DEV_register_iowrite_handler(this, write_handler, addr, "vga video", 3); + } + + + BX_VGA_THIS s.misc_output.color_emulation = 1; + BX_VGA_THIS s.misc_output.enable_ram = 1; + BX_VGA_THIS s.misc_output.clock_select = 0; + BX_VGA_THIS s.misc_output.select_high_bank = 0; + BX_VGA_THIS s.misc_output.horiz_sync_pol = 1; + BX_VGA_THIS s.misc_output.vert_sync_pol = 1; + + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.graphics_alpha = 0; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.display_type = 0; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.enable_line_graphics = 1; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.blink_intensity = 0; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_panning_compat = 0; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_clock_select = 0; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.internal_palette_size = 0; + + BX_VGA_THIS s.line_offset=80; + BX_VGA_THIS s.line_compare=1023; + BX_VGA_THIS s.vertical_display_end=399; + + for (i=0; i<=0x18; i++) + BX_VGA_THIS s.CRTC.reg[i] = 0; + BX_VGA_THIS s.CRTC.address = 0; + + BX_VGA_THIS s.attribute_ctrl.flip_flop = 0; + BX_VGA_THIS s.attribute_ctrl.address = 0; + BX_VGA_THIS s.attribute_ctrl.video_enabled = 1; + for (i=0; i<16; i++) + BX_VGA_THIS s.attribute_ctrl.palette_reg[i] = 0; + BX_VGA_THIS s.attribute_ctrl.overscan_color = 0; + BX_VGA_THIS s.attribute_ctrl.color_plane_enable = 0x0f; + BX_VGA_THIS s.attribute_ctrl.horiz_pel_panning = 0; + BX_VGA_THIS s.attribute_ctrl.color_select = 0; + + for (i=0; i<256; i++) { + BX_VGA_THIS s.pel.data[i].red = 0; + BX_VGA_THIS s.pel.data[i].green = 0; + BX_VGA_THIS s.pel.data[i].blue = 0; + } + BX_VGA_THIS s.pel.write_data_register = 0; + BX_VGA_THIS s.pel.write_data_cycle = 0; + BX_VGA_THIS s.pel.read_data_register = 0; + BX_VGA_THIS s.pel.read_data_cycle = 0; + BX_VGA_THIS s.pel.dac_state = 0x01; + BX_VGA_THIS s.pel.mask = 0xff; + + BX_VGA_THIS s.graphics_ctrl.index = 0; + BX_VGA_THIS s.graphics_ctrl.set_reset = 0; + BX_VGA_THIS s.graphics_ctrl.enable_set_reset = 0; + BX_VGA_THIS s.graphics_ctrl.color_compare = 0; + BX_VGA_THIS s.graphics_ctrl.data_rotate = 0; + BX_VGA_THIS s.graphics_ctrl.raster_op = 0; + BX_VGA_THIS s.graphics_ctrl.read_map_select = 0; + BX_VGA_THIS s.graphics_ctrl.write_mode = 0; + BX_VGA_THIS s.graphics_ctrl.read_mode = 0; + BX_VGA_THIS s.graphics_ctrl.odd_even = 0; + BX_VGA_THIS s.graphics_ctrl.chain_odd_even = 0; + BX_VGA_THIS s.graphics_ctrl.shift_reg = 0; + BX_VGA_THIS s.graphics_ctrl.graphics_alpha = 0; + BX_VGA_THIS s.graphics_ctrl.memory_mapping = 2; // monochrome text mode + BX_VGA_THIS s.graphics_ctrl.color_dont_care = 0; + BX_VGA_THIS s.graphics_ctrl.bitmask = 0; + for (i=0; i<4; i++) { + BX_VGA_THIS s.graphics_ctrl.latch[i] = 0; + } + + BX_VGA_THIS s.sequencer.index = 0; + BX_VGA_THIS s.sequencer.map_mask = 0; + for (i=0; i<4; i++) { + BX_VGA_THIS s.sequencer.map_mask_bit[i] = 0; + } + BX_VGA_THIS s.sequencer.reset1 = 1; + BX_VGA_THIS s.sequencer.reset2 = 1; + BX_VGA_THIS s.sequencer.reg1 = 0; + BX_VGA_THIS s.sequencer.char_map_select = 0; + BX_VGA_THIS s.sequencer.extended_mem = 1; // display mem greater than 64K + BX_VGA_THIS s.sequencer.odd_even = 1; // use sequential addressing mode + BX_VGA_THIS s.sequencer.chain_four = 0; // use map mask & read map select + + memset(BX_VGA_THIS s.vga_memory, 0, sizeof(BX_VGA_THIS s.vga_memory)); + + BX_VGA_THIS s.vga_mem_updated = 0; + for (y=0; y<480/Y_TILESIZE; y++) + for (x=0; x<640/X_TILESIZE; x++) + SET_TILE_UPDATED (x, y, 0); + + { + /* ??? should redo this to pass X args */ + char *argv[1] = { "bochs" }; + bx_gui->init(1, &argv[0], BX_VGA_THIS s.x_tilesize, BX_VGA_THIS s.y_tilesize); + } + + BX_INFO(("interval=%u", bx_options.Ovga_update_interval->get ())); + if (BX_VGA_THIS timer_id == BX_NULL_TIMER_HANDLE) { + BX_VGA_THIS timer_id = bx_pc_system.register_timer(this, timer_handler, + bx_options.Ovga_update_interval->get (), 1, 1, "vga"); + } + + /* video card with BIOS ROM */ + DEV_cmos_set_reg(0x14, (DEV_cmos_get_reg(0x14) & 0xcf) | 0x00); + + BX_VGA_THIS s.charmap_address = 0; + BX_VGA_THIS s.x_dotclockdiv2 = 0; + BX_VGA_THIS s.y_doublescan = 0; + +#if BX_SUPPORT_VBE + // The following is for the vbe display extension + + for (addr=VBE_DISPI_IOPORT_INDEX; addr<=VBE_DISPI_IOPORT_DATA; addr++) { + DEV_register_ioread_handler(this, vbe_read_handler, addr, "vga video", 7); + DEV_register_iowrite_handler(this, vbe_write_handler, addr, "vga video", 7); + } +#if !BX_PCI_USB_SUPPORT + for (addr=VBE_DISPI_IOPORT_INDEX_OLD; addr<=VBE_DISPI_IOPORT_DATA_OLD; addr++) { + DEV_register_ioread_handler(this, vbe_read_handler, addr, "vga video", 7); + DEV_register_iowrite_handler(this, vbe_write_handler, addr, "vga video", 7); + } +#endif + BX_VGA_THIS s.vbe_cur_dispi=VBE_DISPI_ID0; + BX_VGA_THIS s.vbe_xres=640; + BX_VGA_THIS s.vbe_yres=480; + BX_VGA_THIS s.vbe_bpp=8; + BX_VGA_THIS s.vbe_bank=0; + BX_VGA_THIS s.vbe_enabled=0; + BX_VGA_THIS s.vbe_curindex=0; + BX_VGA_THIS s.vbe_offset_x=0; + BX_VGA_THIS s.vbe_offset_y=0; + BX_VGA_THIS s.vbe_virtual_xres=640; + BX_VGA_THIS s.vbe_virtual_yres=480; + BX_VGA_THIS s.vbe_bpp_multiplier=1; + BX_VGA_THIS s.vbe_virtual_start=0; + BX_VGA_THIS s.vbe_line_byte_width=640; + BX_VGA_THIS s.vbe_lfb_enabled=0; + + + BX_INFO(("VBE Bochs Display Extension Enabled")); +#endif + bios_init(); +} + + void +bx_vga_c::bios_init() +{ + int i; + + BX_VGA_THIS s.misc_output.color_emulation = 1; + BX_VGA_THIS s.misc_output.enable_ram = 1; + BX_VGA_THIS s.misc_output.clock_select = 1; + BX_VGA_THIS s.misc_output.select_high_bank = 1; + BX_VGA_THIS s.misc_output.horiz_sync_pol = 1; + BX_VGA_THIS s.misc_output.vert_sync_pol = 0; + BX_VGA_THIS s.CRTC.address = 15; + BX_VGA_THIS s.CRTC.reg[0] = 95; + BX_VGA_THIS s.CRTC.reg[1] = 79; + BX_VGA_THIS s.CRTC.reg[2] = 80; + BX_VGA_THIS s.CRTC.reg[3] = 130; + BX_VGA_THIS s.CRTC.reg[4] = 85; + BX_VGA_THIS s.CRTC.reg[5] = 129; + BX_VGA_THIS s.CRTC.reg[6] = 191; + BX_VGA_THIS s.CRTC.reg[7] = 31; + BX_VGA_THIS s.CRTC.reg[8] = 0; + BX_VGA_THIS s.CRTC.reg[9] = 79; + BX_VGA_THIS s.CRTC.reg[10] = 14; + BX_VGA_THIS s.CRTC.reg[11] = 15; + BX_VGA_THIS s.CRTC.reg[12] = 0; + BX_VGA_THIS s.CRTC.reg[13] = 0; + BX_VGA_THIS s.CRTC.reg[14] = 5; + BX_VGA_THIS s.CRTC.reg[15] = 160; + BX_VGA_THIS s.CRTC.reg[16] = 156; + BX_VGA_THIS s.CRTC.reg[17] = 142; + BX_VGA_THIS s.CRTC.reg[18] = 143; + BX_VGA_THIS s.CRTC.reg[19] = 40; + BX_VGA_THIS s.CRTC.reg[20] = 31; + BX_VGA_THIS s.CRTC.reg[21] = 150; + BX_VGA_THIS s.CRTC.reg[22] = 185; + BX_VGA_THIS s.CRTC.reg[23] = 163; + BX_VGA_THIS s.CRTC.reg[24] = 255; + BX_VGA_THIS s.attribute_ctrl.flip_flop = 1; + BX_VGA_THIS s.attribute_ctrl.address = 0; + BX_VGA_THIS s.attribute_ctrl.video_enabled = 1; + BX_VGA_THIS s.attribute_ctrl.palette_reg[0] = 0; + BX_VGA_THIS s.attribute_ctrl.palette_reg[1] = 1; + BX_VGA_THIS s.attribute_ctrl.palette_reg[2] = 2; + BX_VGA_THIS s.attribute_ctrl.palette_reg[3] = 3; + BX_VGA_THIS s.attribute_ctrl.palette_reg[4] = 4; + BX_VGA_THIS s.attribute_ctrl.palette_reg[5] = 5; + BX_VGA_THIS s.attribute_ctrl.palette_reg[6] = 6; + BX_VGA_THIS s.attribute_ctrl.palette_reg[7] = 7; + BX_VGA_THIS s.attribute_ctrl.palette_reg[8] = 8; + BX_VGA_THIS s.attribute_ctrl.palette_reg[9] = 9; + BX_VGA_THIS s.attribute_ctrl.palette_reg[10] = 10; + BX_VGA_THIS s.attribute_ctrl.palette_reg[11] = 11; + BX_VGA_THIS s.attribute_ctrl.palette_reg[12] = 12; + BX_VGA_THIS s.attribute_ctrl.palette_reg[13] = 13; + BX_VGA_THIS s.attribute_ctrl.palette_reg[14] = 14; + BX_VGA_THIS s.attribute_ctrl.palette_reg[15] = 15; + BX_VGA_THIS s.attribute_ctrl.overscan_color = 0; + BX_VGA_THIS s.attribute_ctrl.color_plane_enable = 15; + BX_VGA_THIS s.attribute_ctrl.horiz_pel_panning = 8; + BX_VGA_THIS s.attribute_ctrl.color_select = 0; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.graphics_alpha = 0; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.display_type = 0; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.enable_line_graphics = 1; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.blink_intensity = 1; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_panning_compat = 0; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_clock_select = 0; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.internal_palette_size = 0; + BX_VGA_THIS s.pel.write_data_register = 16; + BX_VGA_THIS s.pel.write_data_cycle = 0; + BX_VGA_THIS s.pel.read_data_register = 0; + BX_VGA_THIS s.pel.read_data_cycle = 0; + BX_VGA_THIS s.pel.dac_state = 0; + memset((BX_VGA_THIS s.pel.data), 0, 256); + BX_VGA_THIS s.pel.data[0].red = 0; + BX_VGA_THIS s.pel.data[0].green = 0; + BX_VGA_THIS s.pel.data[0].blue = 0; + BX_VGA_THIS s.pel.mask = 255; + BX_VGA_THIS s.graphics_ctrl.index = 6; + BX_VGA_THIS s.graphics_ctrl.set_reset = 0; + BX_VGA_THIS s.graphics_ctrl.enable_set_reset = 0; + BX_VGA_THIS s.graphics_ctrl.color_compare = 0; + BX_VGA_THIS s.graphics_ctrl.data_rotate = 0; + BX_VGA_THIS s.graphics_ctrl.raster_op = 0; + BX_VGA_THIS s.graphics_ctrl.read_map_select = 0; + BX_VGA_THIS s.graphics_ctrl.write_mode = 0; + BX_VGA_THIS s.graphics_ctrl.read_mode = 0; + BX_VGA_THIS s.graphics_ctrl.odd_even = 1; + BX_VGA_THIS s.graphics_ctrl.chain_odd_even = 1; + BX_VGA_THIS s.graphics_ctrl.shift_reg = 0; + BX_VGA_THIS s.graphics_ctrl.graphics_alpha = 0; + BX_VGA_THIS s.graphics_ctrl.memory_mapping = 3; + BX_VGA_THIS s.graphics_ctrl.color_dont_care = 15; + BX_VGA_THIS s.graphics_ctrl.bitmask = 255; + BX_VGA_THIS s.graphics_ctrl.latch[0] = 0; + BX_VGA_THIS s.graphics_ctrl.latch[1] = 0; + BX_VGA_THIS s.graphics_ctrl.latch[2] = 0; + BX_VGA_THIS s.graphics_ctrl.latch[3] = 0; + BX_VGA_THIS s.sequencer.index = 3; + BX_VGA_THIS s.sequencer.map_mask = 3; + BX_VGA_THIS s.sequencer.map_mask_bit[0] = 1; + BX_VGA_THIS s.sequencer.map_mask_bit[1] = 1; + BX_VGA_THIS s.sequencer.map_mask_bit[2] = 0; + BX_VGA_THIS s.sequencer.map_mask_bit[3] = 0; + BX_VGA_THIS s.sequencer.reset1 = 1; + BX_VGA_THIS s.sequencer.reset2 = 1; + BX_VGA_THIS s.sequencer.reg1 = 0; + BX_VGA_THIS s.sequencer.char_map_select = 0; + BX_VGA_THIS s.sequencer.extended_mem = 1; + BX_VGA_THIS s.sequencer.odd_even = 0; + BX_VGA_THIS s.sequencer.chain_four = 0; + BX_VGA_THIS s.vga_mem_updated = 1; + BX_VGA_THIS s.x_tilesize = 16; + BX_VGA_THIS s.y_tilesize = 24; + BX_VGA_THIS s.line_offset = 160; + BX_VGA_THIS s.line_compare = 1023; + BX_VGA_THIS s.vertical_display_end = 399; + memset((BX_VGA_THIS s.vga_tile_updated), 0, BX_NUM_X_TILES * BX_NUM_Y_TILES); + + memset((BX_VGA_THIS s.vga_memory), ' ', 256 * 1024); + for(i = 0; i < 256 * 1024;i+=2) { + BX_VGA_THIS s.vga_memory[i] = ' '; + BX_VGA_THIS s.vga_memory[i+1] = 0x07; + + } + memset((BX_VGA_THIS s.text_snapshot), 0, 32 * 1024); + memset((BX_VGA_THIS s.rgb), 0, 3 * 256); + memset((BX_VGA_THIS s.tile), 0, X_TILESIZE * Y_TILESIZE * 4); + BX_VGA_THIS s.charmap_address = 0; + BX_VGA_THIS s.x_dotclockdiv2 = 0; + BX_VGA_THIS s.y_doublescan = 1; +} + + void +bx_vga_c::reset(unsigned type) +{ +} + + + void +bx_vga_c::determine_screen_dimensions(unsigned *piHeight, unsigned *piWidth) +{ + int ai[0x20]; + int i,h,v; + for ( i = 0 ; i < 0x20 ; i++ ) + ai[i] = BX_VGA_THIS s.CRTC.reg[i]; + + h = (ai[1] + 1) * 8; + v = (ai[18] | ((ai[7] & 0x02) << 7) | ((ai[7] & 0x40) << 3)) + 1; + + if ( BX_VGA_THIS s.graphics_ctrl.shift_reg == 0 ) + { + *piWidth = 640; + *piHeight = 480; + + if ( BX_VGA_THIS s.CRTC.reg[6] == 0xBF ) + { + if (BX_VGA_THIS s.CRTC.reg[23] == 0xA3 && + BX_VGA_THIS s.CRTC.reg[20] == 0x40 && + BX_VGA_THIS s.CRTC.reg[9] == 0x41) + { + *piWidth = 320; + *piHeight = 240; + } + else { + if (BX_VGA_THIS s.x_dotclockdiv2) h <<= 1; + *piWidth = h; + *piHeight = v; + } + } + else if ((h >= 640) && (v >= 480)) { + *piWidth = h; + *piHeight = v; + } + } + else if ( BX_VGA_THIS s.graphics_ctrl.shift_reg == 2 ) + { + + if ( BX_VGA_THIS s.sequencer.chain_four ) + { + *piWidth = h; + *piHeight = v; + } + else + { + *piWidth = h; + *piHeight = v; + } + } + else + { + if (BX_VGA_THIS s.x_dotclockdiv2) h <<= 1; + *piWidth = h; + *piHeight = v; + } +} + + + // static IO port read callback handler + // redirects to non-static class handler to avoid virtual functions + + Bit32u +bx_vga_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_VGA_SMF + bx_vga_c *class_ptr = (bx_vga_c *) this_ptr; + + return( class_ptr->read(address, io_len) ); +} + + + Bit32u +bx_vga_c::read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_VGA_SMF + bx_bool horiz_retrace = 0, vert_retrace = 0; + Bit64u usec; + Bit16u vertres; + Bit8u retval; + +#if defined(VGA_TRACE_FEATURE) + Bit32u ret = 0; +#define RETURN(x) do { ret = (x); goto read_return; } while (0) +#else +#define RETURN return +#endif + +#ifdef __OS2__ + if ( bx_options.videomode == BX_VIDEO_DIRECT ) + { + return _inp(address); + } +#endif + +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("io read from 0x%04x", (unsigned) address)); +#endif + + if ( (address >= 0x03b0) && (address <= 0x03bf) && + (BX_VGA_THIS s.misc_output.color_emulation) ) { + RETURN(0xff); + } + if ( (address >= 0x03d0) && (address <= 0x03df) && + (BX_VGA_THIS s.misc_output.color_emulation==0) ) { + RETURN(0xff); + } + + switch (address) { + case 0x03ba: /* Input Status 1 (monochrome emulation modes) */ + case 0x03ca: /* Feature Control ??? */ + case 0x03da: /* Input Status 1 (color emulation modes) */ + // bit3: Vertical Retrace + // 0 = display is in the display mode + // 1 = display is in the vertical retrace mode + // bit0: Display Enable + // 0 = display is in the display mode + // 1 = display is not in the display mode; either the + // horizontal or vertical retrace period is active + + // using 72 Hz vertical frequency + usec = bx_pc_system.time_usec(); + switch ( ( BX_VGA_THIS s.misc_output.vert_sync_pol << 1) | BX_VGA_THIS s.misc_output.horiz_sync_pol ) + { + case 0: vertres = 200; break; + case 1: vertres = 400; break; + case 2: vertres = 350; break; + default: vertres = 480; break; + } + if ((usec % 13888) < 70) { + vert_retrace = 1; + } + if ((usec % (13888 / vertres)) == 0) { + horiz_retrace = 1; + } + + retval = 0; + if (horiz_retrace || vert_retrace) + retval = 0x01; + if (vert_retrace) + retval |= 0x08; + + /* reading this port resets the flip-flop to address mode */ + BX_VGA_THIS s.attribute_ctrl.flip_flop = 0; + RETURN(retval); + break; + + + case 0x03c0: /* */ + if (BX_VGA_THIS s.attribute_ctrl.flip_flop == 0) { + //BX_INFO(("io read: 0x3c0: flip_flop = 0")); + retval = + (BX_VGA_THIS s.attribute_ctrl.video_enabled << 5) | + BX_VGA_THIS s.attribute_ctrl.address; + RETURN(retval); + } + else { + BX_ERROR(("io read: 0x3c0: flip_flop != 0")); + return(0); + } + break; + + case 0x03c1: /* */ + switch (BX_VGA_THIS s.attribute_ctrl.address) { + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + retval = BX_VGA_THIS s.attribute_ctrl.palette_reg[BX_VGA_THIS s.attribute_ctrl.address]; + RETURN(retval); + break; + case 0x10: /* mode control register */ + retval = + (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.graphics_alpha << 0) | + (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.display_type << 1) | + (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.enable_line_graphics << 2) | + (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.blink_intensity << 3) | + (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_panning_compat << 5) | + (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_clock_select << 6) | + (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.internal_palette_size << 7); + RETURN(retval); + break; + case 0x11: /* overscan color register */ + RETURN(BX_VGA_THIS s.attribute_ctrl.overscan_color); + break; + case 0x12: /* color plane enable */ + RETURN(BX_VGA_THIS s.attribute_ctrl.color_plane_enable); + break; + case 0x13: /* horizontal PEL panning register */ + RETURN(BX_VGA_THIS s.attribute_ctrl.horiz_pel_panning); + break; + case 0x14: /* color select register */ + RETURN(BX_VGA_THIS s.attribute_ctrl.color_select); + break; + default: + BX_INFO(("io read: 0x3c1: unknown register 0x%02x", + (unsigned) BX_VGA_THIS s.attribute_ctrl.address)); + RETURN(0); + } + break; + + case 0x03c2: /* Input Status 0 */ + BX_DEBUG(("io read 0x3c2: input status #0: ignoring")); + RETURN(0); + break; + + case 0x03c3: /* VGA Enable Register */ + RETURN(1); + break; + + case 0x03c4: /* Sequencer Index Register */ + RETURN(BX_VGA_THIS s.sequencer.index); + break; + + case 0x03c5: /* Sequencer Registers 00..04 */ + switch (BX_VGA_THIS s.sequencer.index) { + case 0: /* sequencer: reset */ + BX_DEBUG(("io read 0x3c5: sequencer reset")); + RETURN(BX_VGA_THIS s.sequencer.reset1 | (BX_VGA_THIS s.sequencer.reset2<<1)); + break; + case 1: /* sequencer: clocking mode */ + BX_DEBUG(("io read 0x3c5: sequencer clocking mode")); + RETURN(BX_VGA_THIS s.sequencer.reg1); + break; + case 2: /* sequencer: map mask register */ + RETURN(BX_VGA_THIS s.sequencer.map_mask); + break; + case 3: /* sequencer: character map select register */ + RETURN(BX_VGA_THIS s.sequencer.char_map_select); + break; + case 4: /* sequencer: memory mode register */ + retval = + (BX_VGA_THIS s.sequencer.extended_mem << 1) | + (BX_VGA_THIS s.sequencer.odd_even << 2) | + (BX_VGA_THIS s.sequencer.chain_four << 3); + RETURN(retval); + break; + + default: + BX_DEBUG(("io read 0x3c5: index %u unhandled", + (unsigned) BX_VGA_THIS s.sequencer.index)); + RETURN(0); + } + break; + + case 0x03c6: /* PEL mask ??? */ + RETURN(BX_VGA_THIS s.pel.mask); + break; + + case 0x03c7: /* DAC state, read = 11b, write = 00b */ + RETURN(BX_VGA_THIS s.pel.dac_state); + break; + + case 0x03c8: /* PEL address write mode */ + RETURN(BX_VGA_THIS s.pel.write_data_register); + break; + + case 0x03c9: /* PEL Data Register, colors 00..FF */ + if (BX_VGA_THIS s.pel.dac_state == 0x03) { + switch (BX_VGA_THIS s.pel.read_data_cycle) { + case 0: + retval = BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.read_data_register].red; + break; + case 1: + retval = BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.read_data_register].green; + break; + case 2: + retval = BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.read_data_register].blue; + break; + default: + retval = 0; // keep compiler happy + } + BX_VGA_THIS s.pel.read_data_cycle++; + if (BX_VGA_THIS s.pel.read_data_cycle >= 3) { + BX_VGA_THIS s.pel.read_data_cycle = 0; + BX_VGA_THIS s.pel.read_data_register++; + } + } + else { + retval = 0x3f; + } + RETURN(retval); + break; + + case 0x03cc: /* Miscellaneous Output / Graphics 1 Position ??? */ + retval = + ((BX_VGA_THIS s.misc_output.color_emulation & 0x01) << 0) | + ((BX_VGA_THIS s.misc_output.enable_ram & 0x01) << 1) | + ((BX_VGA_THIS s.misc_output.clock_select & 0x03) << 2) | + ((BX_VGA_THIS s.misc_output.select_high_bank & 0x01) << 5) | + ((BX_VGA_THIS s.misc_output.horiz_sync_pol & 0x01) << 6) | + ((BX_VGA_THIS s.misc_output.vert_sync_pol & 0x01) << 7); + RETURN(retval); + break; + + case 0x03ce: /* Graphics Controller Index Register */ + RETURN(BX_VGA_THIS s.graphics_ctrl.index); + break; + + case 0x03cd: /* ??? */ + BX_DEBUG(("io read from 03cd")); + RETURN(0x00); + break; + + case 0x03cf: /* Graphics Controller Registers 00..08 */ + switch (BX_VGA_THIS s.graphics_ctrl.index) { + case 0: /* Set/Reset */ + RETURN(BX_VGA_THIS s.graphics_ctrl.set_reset); + break; + case 1: /* Enable Set/Reset */ + RETURN(BX_VGA_THIS s.graphics_ctrl.enable_set_reset); + break; + case 2: /* Color Compare */ + RETURN(BX_VGA_THIS s.graphics_ctrl.color_compare); + break; + case 3: /* Data Rotate */ + retval = + ((BX_VGA_THIS s.graphics_ctrl.raster_op & 0x03) << 3) | + ((BX_VGA_THIS s.graphics_ctrl.data_rotate & 0x07) << 0); + RETURN(retval); + break; + case 4: /* Read Map Select */ + RETURN(BX_VGA_THIS s.graphics_ctrl.read_map_select); + break; + case 5: /* Mode */ + retval = + ((BX_VGA_THIS s.graphics_ctrl.shift_reg & 0x03) << 5) | + ((BX_VGA_THIS s.graphics_ctrl.odd_even & 0x01 ) << 4) | + ((BX_VGA_THIS s.graphics_ctrl.read_mode & 0x01) << 3) | + ((BX_VGA_THIS s.graphics_ctrl.write_mode & 0x03) << 0); + + if (BX_VGA_THIS s.graphics_ctrl.odd_even || + BX_VGA_THIS s.graphics_ctrl.shift_reg) + BX_DEBUG(("io read 0x3cf: reg 05 = 0x%02x", (unsigned) retval)); + RETURN(retval); + break; + case 6: /* Miscellaneous */ + retval = + ((BX_VGA_THIS s.graphics_ctrl.memory_mapping & 0x03 ) << 2) | + ((BX_VGA_THIS s.graphics_ctrl.odd_even & 0x01) << 1) | + ((BX_VGA_THIS s.graphics_ctrl.graphics_alpha & 0x01) << 0); + RETURN(retval); + break; + case 7: /* Color Don't Care */ + RETURN(BX_VGA_THIS s.graphics_ctrl.color_dont_care); + break; + case 8: /* Bit Mask */ + RETURN(BX_VGA_THIS s.graphics_ctrl.bitmask); + break; + default: + /* ??? */ + BX_DEBUG(("io read: 0x3cf: index %u unhandled", + (unsigned) BX_VGA_THIS s.graphics_ctrl.index)); + RETURN(0); + } + break; + + case 0x03d4: /* CRTC Index Register (color emulation modes) */ + RETURN(BX_VGA_THIS s.CRTC.address); + break; + + case 0x03b5: /* CRTC Registers (monochrome emulation modes) */ + case 0x03d5: /* CRTC Registers (color emulation modes) */ + if (BX_VGA_THIS s.CRTC.address > 0x18) { + BX_DEBUG(("io read: invalid CRTC register 0x%02x", + (unsigned) BX_VGA_THIS s.CRTC.address)); + RETURN(0); + } + RETURN(BX_VGA_THIS s.CRTC.reg[BX_VGA_THIS s.CRTC.address]); + break; + + case 0x03b4: /* CRTC Index Register (monochrome emulation modes) */ + case 0x03cb: /* not sure but OpenBSD reads it a lot */ + default: + BX_INFO(("io read from vga port 0x%02x", (unsigned) address)); + RETURN(0); /* keep compiler happy */ + } + +#if defined(VGA_TRACE_FEATURE) + read_return: + BX_DEBUG(("8-bit read from %04x = %02x", (unsigned) address, ret)); + return ret; +#endif +} +#if defined(VGA_TRACE_FEATURE) +#undef RETURN +#endif + + // static IO port write callback handler + // redirects to non-static class handler to avoid virtual functions + + void +bx_vga_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_VGA_SMF + bx_vga_c *class_ptr = (bx_vga_c *) this_ptr; + + class_ptr->write(address, value, io_len, 0); +#else + UNUSED(this_ptr); + theVga->write(address, value, io_len, 0); +#endif +} + + void +bx_vga_c::write_handler_no_log(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_VGA_SMF + bx_vga_c *class_ptr = (bx_vga_c *) this_ptr; + + class_ptr->write(address, value, io_len, 1); +#else + UNUSED(this_ptr); + theVga->write(address, value, io_len, 1); +#endif +} + + void +bx_vga_c::write(Bit32u address, Bit32u value, unsigned io_len, bx_bool no_log) +{ + unsigned i; + Bit8u charmap1, charmap2, prev_memory_mapping; + bx_bool prev_video_enabled, prev_line_graphics, prev_int_pal_size; + bx_bool prev_graphics_alpha, prev_chain_odd_even; + bx_bool needs_update = 0; + +#if defined(VGA_TRACE_FEATURE) + if (!no_log) + switch (io_len) { + case 1: + BX_DEBUG(("8-bit write to %04x = %02x", (unsigned)address, (unsigned)value)); + break; + case 2: + BX_DEBUG(("16-bit write to %04x = %04x", (unsigned)address, (unsigned)value)); + break; + default: + BX_PANIC(("Weird VGA write size")); + } +#else + if (io_len == 1) { + BX_DEBUG(("io write to 0x%04x = 0x%02x", (unsigned) address, + (unsigned) value)); + } +#endif + + if (io_len == 2) { +#if BX_USE_VGA_SMF + bx_vga_c::write_handler_no_log(0, address, value & 0xff, 1); + bx_vga_c::write_handler_no_log(0, address+1, (value >> 8) & 0xff, 1); +#else + bx_vga_c::write(address, value & 0xff, 1, 1); + bx_vga_c::write(address+1, (value >> 8) & 0xff, 1, 1); +#endif + return; + } + +#ifdef __OS2__ + if ( bx_options.videomode == BX_VIDEO_DIRECT ) + { + _outp(address,value); + return; + } +#endif + + if ( (address >= 0x03b0) && (address <= 0x03bf) && + (BX_VGA_THIS s.misc_output.color_emulation) ) + return; + if ( (address >= 0x03d0) && (address <= 0x03df) && + (BX_VGA_THIS s.misc_output.color_emulation==0) ) + return; + + switch (address) { + case 0x03ba: /* Feature Control (monochrome emulation modes) */ +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("io write 3ba: feature control: ignoring")); +#endif + break; + + case 0x03c0: /* Attribute Controller */ + if (BX_VGA_THIS s.attribute_ctrl.flip_flop == 0) { /* address mode */ + prev_video_enabled = BX_VGA_THIS s.attribute_ctrl.video_enabled; + BX_VGA_THIS s.attribute_ctrl.video_enabled = (value >> 5) & 0x01; +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("io write 3c0: video_enabled = %u", + (unsigned) BX_VGA_THIS s.attribute_ctrl.video_enabled)); +#endif + if (BX_VGA_THIS s.attribute_ctrl.video_enabled == 0) + bx_gui->clear_screen(); + else if (!prev_video_enabled) { +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("found enable transition")); +#endif + needs_update = 1; + } + value &= 0x1f; /* address = bits 0..4 */ + BX_VGA_THIS s.attribute_ctrl.address = value; + switch (value) { + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + break; + + default: + BX_DEBUG(("io write 3c0: address mode reg=%u", + (unsigned) value)); + } + } + else { /* data-write mode */ + switch (BX_VGA_THIS s.attribute_ctrl.address) { + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + if (value != BX_VGA_THIS s.attribute_ctrl.palette_reg[BX_VGA_THIS s.attribute_ctrl.address]) { + BX_VGA_THIS s.attribute_ctrl.palette_reg[BX_VGA_THIS s.attribute_ctrl.address] = + value; + needs_update = 1; + } + break; + case 0x10: // mode control register + prev_line_graphics = BX_VGA_THIS s.attribute_ctrl.mode_ctrl.enable_line_graphics; + prev_int_pal_size = BX_VGA_THIS s.attribute_ctrl.mode_ctrl.internal_palette_size; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.graphics_alpha = + (value >> 0) & 0x01; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.display_type = + (value >> 1) & 0x01; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.enable_line_graphics = + (value >> 2) & 0x01; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.blink_intensity = + (value >> 3) & 0x01; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_panning_compat = + (value >> 5) & 0x01; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_clock_select = + (value >> 6) & 0x01; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.internal_palette_size = + (value >> 7) & 0x01; + if (((value >> 2) & 0x01) != prev_line_graphics) { + bx_gui->set_text_charmap( + & BX_VGA_THIS s.vga_memory[0x20000 + BX_VGA_THIS s.charmap_address]); + BX_VGA_THIS s.vga_mem_updated = 1; + } + if (((value >> 7) & 0x01) != prev_int_pal_size) { + needs_update = 1; + } +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("io write 3c0: mode control: %02x h", + (unsigned) value)); +#endif + break; + case 0x11: // Overscan Color Register + BX_VGA_THIS s.attribute_ctrl.overscan_color = (value & 0x3f); +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("io write 3c0: overscan color = %02x", + (unsigned) value)); +#endif + break; + case 0x12: // Color Plane Enable Register + BX_VGA_THIS s.attribute_ctrl.color_plane_enable = (value & 0x0f); + needs_update = 1; +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("io write 3c0: color plane enable = %02x", + (unsigned) value)); +#endif + break; + case 0x13: // Horizontal Pixel Panning Register + BX_VGA_THIS s.attribute_ctrl.horiz_pel_panning = (value & 0x0f); + needs_update = 1; +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("io write 3c0: horiz pel panning = %02x", + (unsigned) value)); +#endif + break; + case 0x14: // Color Select Register + BX_VGA_THIS s.attribute_ctrl.color_select = (value & 0x0f); + needs_update = 1; +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("io write 3c0: color select = %02x", + (unsigned) BX_VGA_THIS s.attribute_ctrl.color_select)); +#endif + break; + default: + BX_DEBUG(("io write 3c0: data-write mode %02x h", + (unsigned) BX_VGA_THIS s.attribute_ctrl.address)); + } + } + BX_VGA_THIS s.attribute_ctrl.flip_flop = !BX_VGA_THIS s.attribute_ctrl.flip_flop; + break; + + case 0x03c2: // Miscellaneous Output Register + BX_VGA_THIS s.misc_output.color_emulation = (value >> 0) & 0x01; + BX_VGA_THIS s.misc_output.enable_ram = (value >> 1) & 0x01; + BX_VGA_THIS s.misc_output.clock_select = (value >> 2) & 0x03; + BX_VGA_THIS s.misc_output.select_high_bank = (value >> 5) & 0x01; + BX_VGA_THIS s.misc_output.horiz_sync_pol = (value >> 6) & 0x01; + BX_VGA_THIS s.misc_output.vert_sync_pol = (value >> 7) & 0x01; +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("io write 3c2:")); + BX_DEBUG((" color_emulation (attempted) = %u", + (value >> 0) & 0x01)); + BX_DEBUG((" enable_ram = %u", + (unsigned) BX_VGA_THIS s.misc_output.enable_ram)); + BX_DEBUG((" clock_select = %u", + (unsigned) BX_VGA_THIS s.misc_output.clock_select)); + BX_DEBUG((" select_high_bank = %u", + (unsigned) BX_VGA_THIS s.misc_output.select_high_bank)); + BX_DEBUG((" horiz_sync_pol = %u", + (unsigned) BX_VGA_THIS s.misc_output.horiz_sync_pol)); + BX_DEBUG((" vert_sync_pol = %u", + (unsigned) BX_VGA_THIS s.misc_output.vert_sync_pol)); +#endif + break; + + case 0x03c3: // VGA enable + // bit0: enables VGA display if set +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("io write 3c3: (ignoring) VGA enable = %u", + (unsigned) (value & 0x01) )); +#endif + break; + + case 0x03c4: /* Sequencer Index Register */ + if (value > 4) { + BX_DEBUG(("io write 3c4: value > 4")); + } + BX_VGA_THIS s.sequencer.index = value; + break; + + case 0x03c5: /* Sequencer Registers 00..04 */ + switch (BX_VGA_THIS s.sequencer.index) { + case 0: /* sequencer: reset */ +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("write 0x3c5: sequencer reset: value=0x%02x", + (unsigned) value)); +#endif + if (BX_VGA_THIS s.sequencer.reset1 && ((value & 0x01) == 0)) { + BX_VGA_THIS s.sequencer.char_map_select = 0; + BX_VGA_THIS s.charmap_address = 0; + bx_gui->set_text_charmap( + & BX_VGA_THIS s.vga_memory[0x20000 + BX_VGA_THIS s.charmap_address]); + BX_VGA_THIS s.vga_mem_updated = 1; + } + BX_VGA_THIS s.sequencer.reset1 = (value >> 0) & 0x01; + BX_VGA_THIS s.sequencer.reset2 = (value >> 1) & 0x01; + break; + case 1: /* sequencer: clocking mode */ +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("io write 3c5=%02x: clocking mode reg: ignoring", + (unsigned) value)); +#endif + BX_VGA_THIS s.sequencer.reg1 = value & 0x3f; + BX_VGA_THIS s.x_dotclockdiv2 = ((value & 0x08) > 0); + break; + case 2: /* sequencer: map mask register */ + BX_VGA_THIS s.sequencer.map_mask = (value & 0x0f); + for (i=0; i<4; i++) + BX_VGA_THIS s.sequencer.map_mask_bit[i] = (value >> i) & 0x01; + break; + case 3: /* sequencer: character map select register */ + BX_VGA_THIS s.sequencer.char_map_select = value; + charmap1 = value & 0x13; + if (charmap1 > 3) charmap1 = (charmap1 & 3) + 4; + charmap2 = (value & 0x2C) >> 2; + if (charmap2 > 3) charmap2 = (charmap2 & 3) + 4; + if (BX_VGA_THIS s.CRTC.reg[0x09] > 0) { + BX_VGA_THIS s.charmap_address = (charmap1 << 13); + bx_gui->set_text_charmap( + & BX_VGA_THIS s.vga_memory[0x20000 + BX_VGA_THIS s.charmap_address]); + BX_VGA_THIS s.vga_mem_updated = 1; + } + if (charmap2 != charmap1) + BX_INFO(("char map select: #2=%d (unused)", charmap2)); + break; + case 4: /* sequencer: memory mode register */ + BX_VGA_THIS s.sequencer.extended_mem = (value >> 1) & 0x01; + BX_VGA_THIS s.sequencer.odd_even = (value >> 2) & 0x01; + BX_VGA_THIS s.sequencer.chain_four = (value >> 3) & 0x01; + +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("io write 3c5: index 4:")); + BX_DEBUG((" extended_mem %u", + (unsigned) BX_VGA_THIS s.sequencer.extended_mem)); + BX_DEBUG((" odd_even %u", + (unsigned) BX_VGA_THIS s.sequencer.odd_even)); + BX_DEBUG((" chain_four %u", + (unsigned) BX_VGA_THIS s.sequencer.chain_four)); +#endif + break; + default: + BX_DEBUG(("io write 3c5: index %u unhandled", + (unsigned) BX_VGA_THIS s.sequencer.index)); + } + break; + + case 0x03c6: /* PEL mask */ + BX_VGA_THIS s.pel.mask = value; + if (BX_VGA_THIS s.pel.mask != 0xff) + BX_DEBUG(("io write 3c6: PEL mask=0x%02x != 0xFF", value)); + // BX_VGA_THIS s.pel.mask should be and'd with final value before + // indexing into color register BX_VGA_THIS s.pel.data[] + break; + + case 0x03c7: // PEL address, read mode + BX_VGA_THIS s.pel.read_data_register = value; + BX_VGA_THIS s.pel.read_data_cycle = 0; + BX_VGA_THIS s.pel.dac_state = 0x03; + break; + + case 0x03c8: /* PEL address write mode */ + BX_VGA_THIS s.pel.write_data_register = value; + BX_VGA_THIS s.pel.write_data_cycle = 0; + BX_VGA_THIS s.pel.dac_state = 0x00; + break; + + case 0x03c9: /* PEL Data Register, colors 00..FF */ + switch (BX_VGA_THIS s.pel.write_data_cycle) { + case 0: + BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].red = value; + break; + case 1: + BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].green = value; + break; + case 2: + BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].blue = value; + + needs_update |= bx_gui->palette_change(BX_VGA_THIS s.pel.write_data_register, + BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].red<<2, + BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].green<<2, + BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].blue<<2); + break; + } + + BX_VGA_THIS s.pel.write_data_cycle++; + if (BX_VGA_THIS s.pel.write_data_cycle >= 3) { + //BX_INFO(("BX_VGA_THIS s.pel.data[%u] {r=%u, g=%u, b=%u}", + // (unsigned) BX_VGA_THIS s.pel.write_data_register, + // (unsigned) BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].red, + // (unsigned) BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].green, + // (unsigned) BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].blue); + BX_VGA_THIS s.pel.write_data_cycle = 0; + BX_VGA_THIS s.pel.write_data_register++; + } + break; + + case 0x03ca: /* Graphics 2 Position (EGA) */ + // ignore, EGA only??? + break; + + case 0x03cc: /* Graphics 1 Position (EGA) */ + // ignore, EGA only??? + break; + + case 0x03ce: /* Graphics Controller Index Register */ + if (value > 0x08) /* ??? */ + BX_DEBUG(("io write: 3ce: value > 8")); + BX_VGA_THIS s.graphics_ctrl.index = value; + break; + + case 0x03cd: /* ??? */ + BX_DEBUG(("io write to 03cd = %02x", (unsigned) value)); + break; + + case 0x03cf: /* Graphics Controller Registers 00..08 */ + switch (BX_VGA_THIS s.graphics_ctrl.index) { + case 0: /* Set/Reset */ + BX_VGA_THIS s.graphics_ctrl.set_reset = value & 0x0f; + break; + case 1: /* Enable Set/Reset */ + BX_VGA_THIS s.graphics_ctrl.enable_set_reset = value & 0x0f; + break; + case 2: /* Color Compare */ + BX_VGA_THIS s.graphics_ctrl.color_compare = value & 0x0f; + break; + case 3: /* Data Rotate */ + BX_VGA_THIS s.graphics_ctrl.data_rotate = value & 0x07; + /* ??? is this bits 3..4 or 4..5 */ + BX_VGA_THIS s.graphics_ctrl.raster_op = (value >> 3) & 0x03; /* ??? */ + break; + case 4: /* Read Map Select */ + BX_VGA_THIS s.graphics_ctrl.read_map_select = value & 0x03; +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("io write to 03cf = %02x (RMS)", (unsigned) value)); +#endif + break; + case 5: /* Mode */ + BX_VGA_THIS s.graphics_ctrl.write_mode = value & 0x03; + BX_VGA_THIS s.graphics_ctrl.read_mode = (value >> 3) & 0x01; + BX_VGA_THIS s.graphics_ctrl.odd_even = (value >> 4) & 0x01; + BX_VGA_THIS s.graphics_ctrl.shift_reg = (value >> 5) & 0x03; + + if (BX_VGA_THIS s.graphics_ctrl.odd_even) + BX_DEBUG(("io write: 3cf: reg 05: value = %02xh", + (unsigned) value)); + if (BX_VGA_THIS s.graphics_ctrl.shift_reg) + BX_DEBUG(("io write: 3cf: reg 05: value = %02xh", + (unsigned) value)); + break; + case 6: /* Miscellaneous */ + prev_graphics_alpha = BX_VGA_THIS s.graphics_ctrl.graphics_alpha; + prev_chain_odd_even = BX_VGA_THIS s.graphics_ctrl.chain_odd_even; + prev_memory_mapping = BX_VGA_THIS s.graphics_ctrl.memory_mapping; + + BX_VGA_THIS s.graphics_ctrl.graphics_alpha = value & 0x01; + BX_VGA_THIS s.graphics_ctrl.chain_odd_even = (value >> 1) & 0x01; + BX_VGA_THIS s.graphics_ctrl.memory_mapping = (value >> 2) & 0x03; +#if !defined(VGA_TRACE_FEATURE) + BX_DEBUG(("memory_mapping set to %u", + (unsigned) BX_VGA_THIS s.graphics_ctrl.memory_mapping)); + BX_DEBUG(("graphics mode set to %u", + (unsigned) BX_VGA_THIS s.graphics_ctrl.graphics_alpha)); + BX_DEBUG(("odd_even mode set to %u", + (unsigned) BX_VGA_THIS s.graphics_ctrl.odd_even)); + BX_DEBUG(("io write: 3cf: reg 06: value = %02xh", + (unsigned) value)); +#endif + if (prev_memory_mapping != BX_VGA_THIS s.graphics_ctrl.memory_mapping) + needs_update = 1; + if (prev_graphics_alpha != BX_VGA_THIS s.graphics_ctrl.graphics_alpha) { + needs_update = 1; + old_iHeight = 0; + } + break; + case 7: /* Color Don't Care */ + BX_VGA_THIS s.graphics_ctrl.color_dont_care = value & 0x0f; + break; + case 8: /* Bit Mask */ + BX_VGA_THIS s.graphics_ctrl.bitmask = value; + break; + default: + /* ??? */ + BX_DEBUG(("io write: 3cf: index %u unhandled", + (unsigned) BX_VGA_THIS s.graphics_ctrl.index)); + } + break; + + case 0x03b4: /* CRTC Index Register (monochrome emulation modes) */ + case 0x03d4: /* CRTC Index Register (color emulation modes) */ + BX_VGA_THIS s.CRTC.address = value & 0x7f; + if (BX_VGA_THIS s.CRTC.address > 0x18) + BX_DEBUG(("write: invalid CRTC register 0x%02x selected", + (unsigned) BX_VGA_THIS s.CRTC.address)); + break; + + case 0x03b5: /* CRTC Registers (monochrome emulation modes) */ + case 0x03d5: /* CRTC Registers (color emulation modes) */ + if (BX_VGA_THIS s.CRTC.address > 0x18) { + BX_DEBUG(("write: invalid CRTC register 0x%02x ignored", + (unsigned) BX_VGA_THIS s.CRTC.address)); + return; + } + if (value != BX_VGA_THIS s.CRTC.reg[BX_VGA_THIS s.CRTC.address]) { + BX_VGA_THIS s.CRTC.reg[BX_VGA_THIS s.CRTC.address] = value; + switch (BX_VGA_THIS s.CRTC.address) { + case 0x07: + BX_VGA_THIS s.vertical_display_end &= 0xff; + if (BX_VGA_THIS s.CRTC.reg[0x07] & 0x02) BX_VGA_THIS s.vertical_display_end |= 0x100; + if (BX_VGA_THIS s.CRTC.reg[0x07] & 0x40) BX_VGA_THIS s.vertical_display_end |= 0x200; + BX_VGA_THIS s.line_compare &= 0x2ff; + if (BX_VGA_THIS s.CRTC.reg[0x07] & 0x10) BX_VGA_THIS s.line_compare |= 0x100; + needs_update = 1; + break; + case 0x08: + // Vertical pel panning change + needs_update = 1; + break; + case 0x09: + BX_VGA_THIS s.y_doublescan = ((value & 0x9f) > 0); + BX_VGA_THIS s.line_compare &= 0x1ff; + if (BX_VGA_THIS s.CRTC.reg[0x09] & 0x40) BX_VGA_THIS s.line_compare |= 0x200; + needs_update = 1; + break; + case 0x0A: + case 0x0B: + case 0x0E: + case 0x0F: + // Cursor size / location change + BX_VGA_THIS s.vga_mem_updated = 1; + break; + case 0x0C: + case 0x0D: + // Start address change + if (BX_VGA_THIS s.graphics_ctrl.graphics_alpha) { + needs_update = 1; + } else { + BX_VGA_THIS s.vga_mem_updated = 1; + } + break; + case 0x12: + BX_VGA_THIS s.vertical_display_end &= 0x300; + BX_VGA_THIS s.vertical_display_end |= BX_VGA_THIS s.CRTC.reg[0x12]; + break; + case 0x13: + case 0x14: + case 0x17: + // Line offset change + BX_VGA_THIS s.line_offset = BX_VGA_THIS s.CRTC.reg[0x13] << 1; + if (BX_VGA_THIS s.CRTC.reg[0x14] & 0x40) BX_VGA_THIS s.line_offset <<= 2; + else if ((BX_VGA_THIS s.CRTC.reg[0x17] & 0x40) == 0) BX_VGA_THIS s.line_offset <<= 1; + needs_update = 1; + break; + case 0x18: + BX_VGA_THIS s.line_compare &= 0x300; + BX_VGA_THIS s.line_compare |= BX_VGA_THIS s.CRTC.reg[0x18]; + needs_update = 1; + break; + } + + } + break; + + case 0x03da: /* Feature Control (color emulation modes) */ + BX_DEBUG(("io write: 3da: ignoring: feature ctrl & vert sync")); + break; + + case 0x03c1: /* */ + default: + BX_ERROR(("unsupported io write to port 0x%04x, val=0x%02x", + (unsigned) address, (unsigned) value)); + } + if (needs_update) { + BX_VGA_THIS s.vga_mem_updated = 1; + // Mark all video as updated so the changes will go through + if ((BX_VGA_THIS s.graphics_ctrl.graphics_alpha) +#if BX_SUPPORT_VBE + || (BX_VGA_THIS s.vbe_enabled) +#endif + ) { + for (unsigned xti = 0; xti < BX_NUM_X_TILES; xti++) { + for (unsigned yti = 0; yti < BX_NUM_Y_TILES; yti++) { + SET_TILE_UPDATED (xti, yti, 1); + } + } + } else { + memset(BX_VGA_THIS s.text_snapshot, 0, + sizeof(BX_VGA_THIS s.text_snapshot)); + } + } +} + +void +bx_vga_c::set_update_interval (unsigned interval) +{ + BX_INFO (("Changing timer interval to %d\n", interval)); + BX_VGA_THIS timer_handler (theVga); + bx_pc_system.activate_timer (BX_VGA_THIS timer_id, interval, 1); +} + + void +bx_vga_c::trigger_timer(void *this_ptr) +{ + timer_handler(this_ptr); +} + + void +bx_vga_c::timer_handler(void *this_ptr) +{ +#if !BX_USE_VGA_SMF + + bx_vga_c *class_ptr = (bx_vga_c *) this_ptr; + + class_ptr->timer(); +} + + void +bx_vga_c::timer(void) +{ +#else + UNUSED(this_ptr); +#endif + + update(); + bx_gui->flush(); + +} + + + void +bx_vga_c::update(void) +{ + unsigned iHeight, iWidth; + + /* no screen update necessary */ + if (BX_VGA_THIS s.vga_mem_updated==0) + return; + + /* skip screen update when the sequencer is in reset mode or video is disabled */ + if (!BX_VGA_THIS s.sequencer.reset1 || !BX_VGA_THIS s.sequencer.reset2 + || !BX_VGA_THIS s.attribute_ctrl.video_enabled) + return; + + /* skip screen update if the vertical retrace is in progress + (using 72 Hz vertical frequency) */ + if ((bx_pc_system.time_usec() % 13888) < 70) + return; + +#if BX_SUPPORT_VBE + if ((BX_VGA_THIS s.vbe_enabled) && (BX_VGA_THIS s.vbe_bpp != VBE_DISPI_BPP_4)) + { + // specific VBE code display update code + // this is partly copied/modified from the 320x200x8 update more below + unsigned xc, yc, xti, yti; + unsigned r; + unsigned long pixely, bmp_ofs_y, tile_ofs_y; + + if (BX_VGA_THIS s.vbe_bpp == VBE_DISPI_BPP_32) + { + Bit32u *vidmem = (Bit32u *)(&BX_VGA_THIS s.vbe_memory[BX_VGA_THIS s.vbe_virtual_start]); + Bit32u *tile = (Bit32u *)(BX_VGA_THIS s.tile); + Bit16u width = BX_VGA_THIS s.vbe_virtual_xres; + + Bit32u *vidptr, *tileptr; + + iWidth=BX_VGA_THIS s.vbe_xres; + iHeight=BX_VGA_THIS s.vbe_yres; + + for (yc=0, yti = 0; yc<iHeight; yc+=Y_TILESIZE, yti++) + { + for (xc=0, xti = 0; xc<iWidth; xc+=X_TILESIZE, xti++) + { + if (GET_TILE_UPDATED (xti, yti)) + { + for (r=0; r<Y_TILESIZE; r++) + { + pixely = yc + r; + // calc offsets into video and tile memory + bmp_ofs_y = pixely*width; + tile_ofs_y = r*X_TILESIZE; + // get offsets so that we do less calc in the inner loop + vidptr = &vidmem[bmp_ofs_y+xc]; + tileptr = &tile[tile_ofs_y]; + memmove(tileptr, vidptr, X_TILESIZE<<2); + } + SET_TILE_UPDATED (xti, yti, 0); + bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc); + } + } + } + } + else if (BX_VGA_THIS s.vbe_bpp == VBE_DISPI_BPP_24) + { + Bit8u *vidmem = &BX_VGA_THIS s.vbe_memory[BX_VGA_THIS s.vbe_virtual_start]; + Bit8u *tile = BX_VGA_THIS s.tile; + Bit16u width = BX_VGA_THIS s.vbe_virtual_xres*3; + + Bit8u *vidptr, *tileptr; + + iWidth=BX_VGA_THIS s.vbe_xres; + iHeight=BX_VGA_THIS s.vbe_yres; + + for (yc=0, yti = 0; yc<iHeight; yc+=Y_TILESIZE, yti++) + { + for (xc=0, xti = 0; xc<iWidth; xc+=X_TILESIZE, xti++) + { + if (GET_TILE_UPDATED (xti, yti)) + { + for (r=0; r<Y_TILESIZE; r++) + { + pixely = yc + r; + // calc offsets into video and tile memory + bmp_ofs_y = pixely*width; + tile_ofs_y = r*X_TILESIZE*3; + // get offsets so that we do less calc in the inner loop + vidptr = &vidmem[bmp_ofs_y+xc*3]; + tileptr = &tile[tile_ofs_y]; + memmove(tileptr, vidptr, X_TILESIZE*3); + } + SET_TILE_UPDATED (xti, yti, 0); + bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc); + } + } + } + } + else if ((BX_VGA_THIS s.vbe_bpp == VBE_DISPI_BPP_15) || + (BX_VGA_THIS s.vbe_bpp == VBE_DISPI_BPP_16)) + { + Bit16u *vidmem = (Bit16u *)(&BX_VGA_THIS s.vbe_memory[BX_VGA_THIS s.vbe_virtual_start]); + Bit16u *tile = (Bit16u *)(BX_VGA_THIS s.tile); + Bit16u width = BX_VGA_THIS s.vbe_virtual_xres; + + Bit16u *vidptr, *tileptr; + + iWidth=BX_VGA_THIS s.vbe_xres; + iHeight=BX_VGA_THIS s.vbe_yres; + + for (yc=0, yti = 0; yc<iHeight; yc+=Y_TILESIZE, yti++) + { + for (xc=0, xti = 0; xc<iWidth; xc+=X_TILESIZE, xti++) + { + if (GET_TILE_UPDATED (xti, yti)) + { + for (r=0; r<Y_TILESIZE; r++) + { + pixely = yc + r; + // calc offsets into video and tile memory + bmp_ofs_y = pixely*width; + tile_ofs_y = r*X_TILESIZE; + // get offsets so that we do less calc in the inner loop + vidptr = &vidmem[bmp_ofs_y+xc]; + tileptr = &tile[tile_ofs_y]; + memmove(tileptr, vidptr, X_TILESIZE<<1); + } + SET_TILE_UPDATED (xti, yti, 0); + bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc); + } + } + } + } + else /* Update 8bpp mode */ + { + Bit8u *vidmem = &BX_VGA_THIS s.vbe_memory[BX_VGA_THIS s.vbe_virtual_start]; + Bit8u *tile = BX_VGA_THIS s.tile; + Bit16u width = BX_VGA_THIS s.vbe_virtual_xres; + + Bit8u *vidptr, *tileptr; + + iWidth=BX_VGA_THIS s.vbe_xres; + iHeight=BX_VGA_THIS s.vbe_yres; + + for (yc=0, yti = 0; yc<iHeight; yc+=Y_TILESIZE, yti++) + { + for (xc=0, xti = 0; xc<iWidth; xc+=X_TILESIZE, xti++) + { + // If the tile has not been updated, copy it into the tile buffer for update + if (GET_TILE_UPDATED (xti, yti)) { + for (r=0; r<Y_TILESIZE; r++) { + // actual video y coord is tile_y + y + pixely = yc + r; + // calc offsets into video and tile memory + bmp_ofs_y = pixely*width; + tile_ofs_y = r*X_TILESIZE; + // get offsets so that we do less calc in the inner loop + vidptr = &vidmem[bmp_ofs_y+xc]; + tileptr = &tile[tile_ofs_y]; + memmove(tileptr, vidptr, X_TILESIZE); + } + SET_TILE_UPDATED (xti, yti, 0); + bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc); + } + } + } + } + + old_iWidth = iWidth; + old_iHeight = iHeight; + BX_VGA_THIS s.vga_mem_updated = 0; + // after a vbe display update, don't try to do any 'normal vga' updates anymore + return; + } +#endif + // fields that effect the way video memory is serialized into screen output: + // GRAPHICS CONTROLLER: + // BX_VGA_THIS s.graphics_ctrl.shift_reg: + // 0: output data in standard VGA format or CGA-compatible 640x200 2 color + // graphics mode (mode 6) + // 1: output data in CGA-compatible 320x200 4 color graphics mode + // (modes 4 & 5) + // 2: output data 8 bits at a time from the 4 bit planes + // (mode 13 and variants like modeX) + + // if (BX_VGA_THIS s.vga_mem_updated==0 || BX_VGA_THIS s.attribute_ctrl.video_enabled == 0) + + if (BX_VGA_THIS s.graphics_ctrl.graphics_alpha) { + Bit8u color; + unsigned bit_no, r, c, x, y; + unsigned long byte_offset, start_addr; + unsigned xc, yc, xti, yti; + + start_addr = (BX_VGA_THIS s.CRTC.reg[0x0c] << 8) | BX_VGA_THIS s.CRTC.reg[0x0d]; + +//BX_DEBUG(("update: shiftreg=%u, chain4=%u, mapping=%u", +// (unsigned) BX_VGA_THIS s.graphics_ctrl.shift_reg, +// (unsigned) BX_VGA_THIS s.sequencer.chain_four, +// (unsigned) BX_VGA_THIS s.graphics_ctrl.memory_mapping); + + determine_screen_dimensions(&iHeight, &iWidth); + if( (iWidth != old_iWidth) || (iHeight != old_iHeight) || (old_BPP > 8) ) { + bx_gui->dimension_update(iWidth, iHeight); + old_iWidth = iWidth; + old_iHeight = iHeight; + old_BPP = 8; + } + + switch ( BX_VGA_THIS s.graphics_ctrl.shift_reg ) { + + case 0: + Bit8u attribute, palette_reg_val, DAC_regno; + unsigned long line_compare; + + if (BX_VGA_THIS s.graphics_ctrl.memory_mapping == 3) { // CGA 640x200x2 + + for (yc=0, yti=0; yc<iHeight; yc+=Y_TILESIZE, yti++) { + for (xc=0, xti=0; xc<iWidth; xc+=X_TILESIZE, xti++) { + if (GET_TILE_UPDATED (xti, yti)) { + for (r=0; r<Y_TILESIZE; r++) { + y = yc + r; + if (BX_VGA_THIS s.y_doublescan) y >>= 1; + for (c=0; c<X_TILESIZE; c++) { + + x = xc + c; + /* 0 or 0x2000 */ + byte_offset = start_addr + ((y & 1) << 13); + /* to the start of the line */ + byte_offset += (320 / 4) * (y / 2); + /* to the byte start */ + byte_offset += (x / 8); + + bit_no = 7 - (x % 8); + palette_reg_val = (((BX_VGA_THIS s.vga_memory[byte_offset]) >> bit_no) & 1); + DAC_regno = BX_VGA_THIS s.attribute_ctrl.palette_reg[palette_reg_val]; + BX_VGA_THIS s.tile[r*X_TILESIZE + c] = DAC_regno; + } + } + SET_TILE_UPDATED (xti, yti, 0); + bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc); + } + } + } + } else { // output data in serial fashion with each display plane + // output on its associated serial output. Standard EGA/VGA format + + line_compare = BX_VGA_THIS s.line_compare; + if (BX_VGA_THIS s.y_doublescan) line_compare >>= 1; + + for (yc=0, yti=0; yc<iHeight; yc+=Y_TILESIZE, yti++) { + for (xc=0, xti=0; xc<iWidth; xc+=X_TILESIZE, xti++) { + if (GET_TILE_UPDATED (xti, yti)) { + for (r=0; r<Y_TILESIZE; r++) { + y = yc + r; + if (BX_VGA_THIS s.y_doublescan) y >>= 1; + for (c=0; c<X_TILESIZE; c++) { + x = xc + c; + if (BX_VGA_THIS s.x_dotclockdiv2) x >>= 1; + bit_no = 7 - (x % 8); + if (y > line_compare) { + byte_offset = x / 8 + + ((y - line_compare - 1) * BX_VGA_THIS s.line_offset); + } else { + byte_offset = start_addr + x / 8 + + (y * BX_VGA_THIS s.line_offset); + } + attribute = + (((BX_VGA_THIS s.vga_memory[0*65536 + byte_offset] >> bit_no) & 0x01) << 0) | + (((BX_VGA_THIS s.vga_memory[1*65536 + byte_offset] >> bit_no) & 0x01) << 1) | + (((BX_VGA_THIS s.vga_memory[2*65536 + byte_offset] >> bit_no) & 0x01) << 2) | + (((BX_VGA_THIS s.vga_memory[3*65536 + byte_offset] >> bit_no) & 0x01) << 3); + + attribute &= BX_VGA_THIS s.attribute_ctrl.color_plane_enable; + // undocumented feature ???: colors 0..7 high intensity, colors 8..15 blinking + // using low/high intensity. Blinking is not implemented yet. + if (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.blink_intensity) attribute ^= 0x08; + palette_reg_val = BX_VGA_THIS s.attribute_ctrl.palette_reg[attribute]; + if (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.internal_palette_size) { + // use 4 lower bits from palette register + // use 4 higher bits from color select register + // 16 banks of 16-color registers + DAC_regno = (palette_reg_val & 0x0f) | + (BX_VGA_THIS s.attribute_ctrl.color_select << 4); + } + else { + // use 6 lower bits from palette register + // use 2 higher bits from color select register + // 4 banks of 64-color registers + DAC_regno = (palette_reg_val & 0x3f) | + ((BX_VGA_THIS s.attribute_ctrl.color_select & 0x0c) << 4); + } + // DAC_regno &= video DAC mask register ??? + + BX_VGA_THIS s.tile[r*X_TILESIZE + c] = DAC_regno; + } + } + SET_TILE_UPDATED (xti, yti, 0); + bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc); + } + } + } + } + break; // case 0 + + case 1: // output the data in a CGA-compatible 320x200 4 color graphics + // mode. (modes 4 & 5) + + /* CGA 320x200x4 start */ + + for (yc=0, yti=0; yc<iHeight; yc+=Y_TILESIZE, yti++) { + for (xc=0, xti=0; xc<iWidth; xc+=X_TILESIZE, xti++) { + if (GET_TILE_UPDATED (xti, yti)) { + for (r=0; r<Y_TILESIZE; r++) { + y = yc + r; + if (BX_VGA_THIS s.y_doublescan) y >>= 1; + for (c=0; c<X_TILESIZE; c++) { + + x = xc + c; + if (BX_VGA_THIS s.x_dotclockdiv2) x >>= 1; + /* 0 or 0x2000 */ + byte_offset = start_addr + ((y & 1) << 13); + /* to the start of the line */ + byte_offset += (320 / 4) * (y / 2); + /* to the byte start */ + byte_offset += (x / 4); + + attribute = 6 - 2*(x % 4); + palette_reg_val = (BX_VGA_THIS s.vga_memory[byte_offset]) >> attribute; + palette_reg_val &= 3; + DAC_regno = BX_VGA_THIS s.attribute_ctrl.palette_reg[palette_reg_val]; + BX_VGA_THIS s.tile[r*X_TILESIZE + c] = DAC_regno; + } + } + SET_TILE_UPDATED (xti, yti, 0); + bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc); + } + } + } + /* CGA 320x200x4 end */ + + break; // case 1 + + case 2: // output the data eight bits at a time from the 4 bit plane + // (format for VGA mode 13 hex) + + if ( BX_VGA_THIS s.sequencer.chain_four ) { + unsigned long pixely, pixelx, plane; + // bx_vga_dump_status(); + + if (BX_VGA_THIS s.misc_output.select_high_bank != 1) + BX_PANIC(("update: select_high_bank != 1")); + + for (yc=0, yti=0; yc<iHeight; yc+=Y_TILESIZE, yti++) { + for (xc=0, xti=0; xc<iWidth; xc+=X_TILESIZE, xti++) { + if (GET_TILE_UPDATED (xti, yti)) { + for (r=0; r<Y_TILESIZE; r++) { + pixely = yc + r; + if (BX_VGA_THIS s.y_doublescan) pixely >>= 1; + for (c=0; c<X_TILESIZE; c++) { + pixelx = (xc + c) >> 1; + plane = (pixelx % 4); + byte_offset = start_addr + (plane * 65536) + + (pixely * BX_VGA_THIS s.line_offset) + (pixelx & ~0x03); + color = BX_VGA_THIS s.vga_memory[byte_offset]; + BX_VGA_THIS s.tile[r*X_TILESIZE + c] = color; + } + } + SET_TILE_UPDATED (xti, yti, 0); + bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc); + } + } + } + } + + else { // chain_four == 0, modeX + unsigned long pixely, pixelx, plane; + + for (yc=0, yti=0; yc<iHeight; yc+=Y_TILESIZE, yti++) { + for (xc=0, xti=0; xc<iWidth; xc+=X_TILESIZE, xti++) { + if (GET_TILE_UPDATED (xti, yti)) { + for (r=0; r<Y_TILESIZE; r++) { + pixely = yc + r; + if (BX_VGA_THIS s.y_doublescan) pixely >>= 1; + for (c=0; c<X_TILESIZE; c++) { + pixelx = (xc + c) >> 1; + plane = (pixelx % 4); + byte_offset = (plane * 65536) + + (pixely * BX_VGA_THIS s.line_offset) + + (pixelx >> 2); + color = BX_VGA_THIS s.vga_memory[start_addr + byte_offset]; + BX_VGA_THIS s.tile[r*X_TILESIZE + c] = color; + } + } + SET_TILE_UPDATED (xti, yti, 0); + bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc); + } + } + } + } + break; // case 2 + + default: + BX_PANIC(("update: shift_reg == %u", (unsigned) + BX_VGA_THIS s.graphics_ctrl.shift_reg )); + } + + BX_VGA_THIS s.vga_mem_updated = 0; + return; + } + + else { // text mode + unsigned long start_address; + unsigned long cursor_address, cursor_x, cursor_y; + bx_vga_tminfo_t tm_info; + + + tm_info.cs_start = BX_VGA_THIS s.CRTC.reg[0x0a] & 0x3f; + tm_info.cs_end = BX_VGA_THIS s.CRTC.reg[0x0b] & 0x1f; + tm_info.line_offset = BX_VGA_THIS s.CRTC.reg[0x13] << 2; + tm_info.line_compare = BX_VGA_THIS s.line_compare; + tm_info.h_panning = BX_VGA_THIS s.attribute_ctrl.horiz_pel_panning & 0x0f; + tm_info.v_panning = BX_VGA_THIS s.CRTC.reg[0x08] & 0x1f; + tm_info.line_graphics = BX_VGA_THIS s.attribute_ctrl.mode_ctrl.enable_line_graphics; + if ((BX_VGA_THIS s.sequencer.reg1 & 0x01) == 0) { + if (tm_info.h_panning == 8) + tm_info.h_panning = 0; + else + tm_info.h_panning++; + } + + switch (BX_VGA_THIS s.graphics_ctrl.memory_mapping) { + case 0: // 128K @ A0000 + case 1: // 64K @ A0000 + iWidth = 8*80; // TODO: should use font size + iHeight = 16*25; + if( (iWidth != old_iWidth) || (iHeight != old_iHeight) || (old_BPP > 8) ) + { + bx_gui->dimension_update(iWidth, iHeight, 16, 8); + old_iWidth = iWidth; + old_iHeight = iHeight; + old_BPP = 8; + } + /* pass old text snapshot & new VGA memory contents */ + start_address = 0x0; + cursor_address = 2*((BX_VGA_THIS s.CRTC.reg[0x0e] << 8) | + BX_VGA_THIS s.CRTC.reg[0x0f]); + if (cursor_address < start_address) { + cursor_x = 0xffff; + cursor_y = 0xffff; + } + else { + cursor_x = ((cursor_address - start_address)/2) % 80; + cursor_y = ((cursor_address - start_address)/2) / 80; + } + bx_gui->text_update(BX_VGA_THIS s.text_snapshot, + &BX_VGA_THIS s.vga_memory[start_address], + cursor_x, cursor_y, tm_info, 25); + // screen updated, copy new VGA memory contents into text snapshot + memcpy(BX_VGA_THIS s.text_snapshot, + &BX_VGA_THIS s.vga_memory[start_address], + 2*80*25); + BX_VGA_THIS s.vga_mem_updated = 0; + break; + + case 2: // B0000 .. B7FFF + case 3: // B8000 .. BFFFF + unsigned VDE, MSL, rows, cWidth; + + // Verticle Display End: find out how many lines are displayed + VDE = BX_VGA_THIS s.vertical_display_end; + // Maximum Scan Line: height of character cell + MSL = BX_VGA_THIS s.CRTC.reg[0x09] & 0x1f; + if (MSL == 0) { + //BX_ERROR(("character height = 1, skipping text update")); + return; + } + if ((MSL == 1) && (BX_VGA_THIS s.CRTC.reg[0x06] == 100)) { + // emulated CGA graphics mode 160x100x16 colors + MSL = 3; + rows = 100; + cWidth = 8; + iWidth = cWidth * BX_VGA_THIS s.CRTC.reg[1]; + iHeight = 400; + } else { + rows = (VDE+1)/(MSL+1); + if (rows > BX_MAX_TEXT_LINES) + BX_PANIC(("text rows>%d: %d",BX_MAX_TEXT_LINES,rows)); + cWidth = ((BX_VGA_THIS s.sequencer.reg1 & 0x01) == 1) ? 8 : 9; + iWidth = cWidth * (BX_VGA_THIS s.CRTC.reg[1] + 1); + iHeight = VDE+1; + } + if( (iWidth != old_iWidth) || (iHeight != old_iHeight) || (MSL != old_MSL) || (old_BPP > 8) ) + { + bx_gui->dimension_update(iWidth, iHeight, MSL+1, cWidth); + old_iWidth = iWidth; + old_iHeight = iHeight; + old_MSL = MSL; + old_BPP = 8; + } + // pass old text snapshot & new VGA memory contents + start_address = 2*((BX_VGA_THIS s.CRTC.reg[12] << 8) + BX_VGA_THIS s.CRTC.reg[13]); + cursor_address = 2*((BX_VGA_THIS s.CRTC.reg[0x0e] << 8) | + BX_VGA_THIS s.CRTC.reg[0x0f]); + if (cursor_address < start_address) { + cursor_x = 0xffff; + cursor_y = 0xffff; + } + else { + cursor_x = ((cursor_address - start_address)/2) % (iWidth/cWidth); + cursor_y = ((cursor_address - start_address)/2) / (iWidth/cWidth); + } + bx_gui->text_update(BX_VGA_THIS s.text_snapshot, + &BX_VGA_THIS s.vga_memory[start_address], + cursor_x, cursor_y, tm_info, rows); + // screen updated, copy new VGA memory contents into text snapshot + memcpy(BX_VGA_THIS s.text_snapshot, + &BX_VGA_THIS s.vga_memory[start_address], + 2*80*rows); + BX_VGA_THIS s.vga_mem_updated = 0; + break; + default: + BX_DEBUG(("update(): color text mode: mem map is %u", + (unsigned) BX_VGA_THIS s.graphics_ctrl.memory_mapping)); + } + } +} + + + Bit8u +bx_vga_c::mem_read(Bit32u addr) +{ + Bit32u offset; + +#if BX_SUPPORT_VBE + // if in a vbe enabled mode, read from the vbe_memory + if ((BX_VGA_THIS s.vbe_enabled) && (BX_VGA_THIS s.vbe_bpp != VBE_DISPI_BPP_4)) + { + return vbe_mem_read(addr); + } +#endif + +#if defined(VGA_TRACE_FEATURE) +// BX_DEBUG(("8-bit memory read from %08x", addr)); +#endif + +#ifdef __OS2__ + +#if BX_PLUGINS +#error Fix the code for plugins +#endif + + if ( bx_options.videomode == BX_VIDEO_DIRECT ) + { + char value; + + value = devices->mem->video[addr-0xA0000]; + + return value; + } +#endif + + switch (BX_VGA_THIS s.graphics_ctrl.memory_mapping) { + case 1: // 0xA0000 .. 0xAFFFF + if (addr > 0xAFFFF) return 0xff; + offset = addr - 0xA0000; + break; + case 2: // 0xB0000 .. 0xB7FFF + if ((addr < 0xB0000) || (addr > 0xB7FFF)) return 0xff; + return BX_VGA_THIS s.vga_memory[addr - 0xB0000]; + break; + case 3: // 0xB8000 .. 0xBFFFF + if (addr < 0xB8000) return 0xff; + return BX_VGA_THIS s.vga_memory[addr - 0xB8000]; + break; + default: // 0xA0000 .. 0xBFFFF + return BX_VGA_THIS s.vga_memory[addr - 0xA0000]; + } + + // addr between 0xA0000 and 0xAFFFF + if ( BX_VGA_THIS s.sequencer.chain_four ) { + + // Mode 13h: 320 x 200 256 color mode: chained pixel representation + return BX_VGA_THIS s.vga_memory[(offset & ~0x03) + (offset % 4)*65536]; + } + + /* addr between 0xA0000 and 0xAFFFF */ + switch (BX_VGA_THIS s.graphics_ctrl.read_mode) { + case 0: /* read mode 0 */ + BX_VGA_THIS s.graphics_ctrl.latch[0] = BX_VGA_THIS s.vga_memory[ offset]; + BX_VGA_THIS s.graphics_ctrl.latch[1] = BX_VGA_THIS s.vga_memory[1*65536 + offset]; + BX_VGA_THIS s.graphics_ctrl.latch[2] = BX_VGA_THIS s.vga_memory[2*65536 + offset]; + BX_VGA_THIS s.graphics_ctrl.latch[3] = BX_VGA_THIS s.vga_memory[3*65536 + offset]; + return(BX_VGA_THIS s.graphics_ctrl.latch[BX_VGA_THIS s.graphics_ctrl.read_map_select]); + break; + + case 1: /* read mode 1 */ + { + Bit8u color_compare, color_dont_care; + Bit8u latch0, latch1, latch2, latch3, retval; + + color_compare = BX_VGA_THIS s.graphics_ctrl.color_compare & 0x0f; + color_dont_care = BX_VGA_THIS s.graphics_ctrl.color_dont_care & 0x0f; + latch0 = BX_VGA_THIS s.graphics_ctrl.latch[0] = BX_VGA_THIS s.vga_memory[ offset]; + latch1 = BX_VGA_THIS s.graphics_ctrl.latch[1] = BX_VGA_THIS s.vga_memory[1*65536 + offset]; + latch2 = BX_VGA_THIS s.graphics_ctrl.latch[2] = BX_VGA_THIS s.vga_memory[2*65536 + offset]; + latch3 = BX_VGA_THIS s.graphics_ctrl.latch[3] = BX_VGA_THIS s.vga_memory[3*65536 + offset]; + + latch0 ^= ccdat[color_compare][0]; + latch1 ^= ccdat[color_compare][1]; + latch2 ^= ccdat[color_compare][2]; + latch3 ^= ccdat[color_compare][3]; + + latch0 &= ccdat[color_dont_care][0]; + latch1 &= ccdat[color_dont_care][1]; + latch2 &= ccdat[color_dont_care][2]; + latch3 &= ccdat[color_dont_care][3]; + + retval = ~(latch0 | latch1 | latch2 | latch3); + + return retval; + } + break; + default: + return 0; + } +} + + void +bx_vga_c::mem_write(Bit32u addr, Bit8u value) +{ + Bit32u offset; + Bit8u new_val[4]; + unsigned start_addr; + +#if BX_SUPPORT_VBE + // if in a vbe enabled mode, write to the vbe_memory + if ((BX_VGA_THIS s.vbe_enabled) && (BX_VGA_THIS s.vbe_bpp != VBE_DISPI_BPP_4)) + { + vbe_mem_write(addr,value); + return; + } +#endif + +#if defined(VGA_TRACE_FEATURE) +// BX_DEBUG(("8-bit memory write to %08x = %02x", addr, value)); +#endif + +#ifdef __OS2__ + +#if BX_PLUGINS +#error Fix the code for plugins +#endif + + if ( bx_options.videomode == BX_VIDEO_DIRECT ) + { + devices->mem->video[addr-0xA0000] = value; + + return; + } +#endif + + switch (BX_VGA_THIS s.graphics_ctrl.memory_mapping) { + case 1: // 0xA0000 .. 0xAFFFF + if (addr > 0xAFFFF) return; + offset = addr - 0xA0000; + break; + case 2: // 0xB0000 .. 0xB7FFF + if ((addr < 0xB0000) || (addr > 0xB7FFF)) return; + offset = addr - 0xB0000; + break; + case 3: // 0xB8000 .. 0xBFFFF + if (addr < 0xB8000) return; + offset = addr - 0xB8000; + break; + default: // 0xA0000 .. 0xBFFFF + offset = addr - 0xA0000; + } + + start_addr = (BX_VGA_THIS s.CRTC.reg[0x0c] << 8) | BX_VGA_THIS s.CRTC.reg[0x0d]; + + if (BX_VGA_THIS s.graphics_ctrl.graphics_alpha) { + if (BX_VGA_THIS s.graphics_ctrl.memory_mapping == 3) { // 0xB8000 .. 0xBFFFF + unsigned x_tileno, x_tileno2, y_tileno; + + /* CGA 320x200x4 / 640x200x2 start */ + BX_VGA_THIS s.vga_memory[offset] = value; + offset -= start_addr; + if (offset>=0x2000) { + y_tileno = offset - 0x2000; + y_tileno /= (320/4); + y_tileno <<= 1; //2 * y_tileno; + y_tileno++; + x_tileno = (offset - 0x2000) % (320/4); + x_tileno <<= 2; //*= 4; + } else { + y_tileno = offset / (320/4); + y_tileno <<= 1; //2 * y_tileno; + x_tileno = offset % (320/4); + x_tileno <<= 2; //*=4; + } + x_tileno2=x_tileno; + if (BX_VGA_THIS s.graphics_ctrl.shift_reg==0) { + x_tileno*=2; + x_tileno2+=7; + } else { + x_tileno2+=3; + } + if (BX_VGA_THIS s.x_dotclockdiv2) { + x_tileno/=(X_TILESIZE/2); + x_tileno2/=(X_TILESIZE/2); + } else { + x_tileno/=X_TILESIZE; + x_tileno2/=X_TILESIZE; + } + if (BX_VGA_THIS s.y_doublescan) { + y_tileno/=(Y_TILESIZE/2); + } else { + y_tileno/=Y_TILESIZE; + } + BX_VGA_THIS s.vga_mem_updated = 1; + SET_TILE_UPDATED (x_tileno, y_tileno, 1); + if (x_tileno2!=x_tileno) { + SET_TILE_UPDATED (x_tileno2, y_tileno, 1); + } + return; + /* CGA 320x200x4 / 640x200x2 end */ + } + else if (BX_VGA_THIS s.graphics_ctrl.memory_mapping != 1) { + + BX_PANIC(("mem_write: graphics: mapping = %u", + (unsigned) BX_VGA_THIS s.graphics_ctrl.memory_mapping)); + return; + } + + if ( BX_VGA_THIS s.sequencer.chain_four ) { + unsigned x_tileno, y_tileno; + + // 320 x 200 256 color mode: chained pixel representation + BX_VGA_THIS s.vga_memory[(offset & ~0x03) + (offset % 4)*65536] = value; + offset -= start_addr; + x_tileno = (offset % BX_VGA_THIS s.line_offset) / (X_TILESIZE/2); + if (BX_VGA_THIS s.y_doublescan) { + y_tileno = (offset / BX_VGA_THIS s.line_offset) / (Y_TILESIZE/2); + } else { + y_tileno = (offset / BX_VGA_THIS s.line_offset) / Y_TILESIZE; + } + BX_VGA_THIS s.vga_mem_updated = 1; + SET_TILE_UPDATED (x_tileno, y_tileno, 1); + return; + } + + } + + /* addr between 0xA0000 and 0xAFFFF */ + switch (BX_VGA_THIS s.graphics_ctrl.write_mode) { + unsigned i; + + case 0: /* write mode 0 */ + { + const Bit8u bitmask = BX_VGA_THIS s.graphics_ctrl.bitmask; + const Bit8u set_reset = BX_VGA_THIS s.graphics_ctrl.set_reset; + const Bit8u enable_set_reset = BX_VGA_THIS s.graphics_ctrl.enable_set_reset; + /* perform rotate on CPU data in case its needed */ + if (BX_VGA_THIS s.graphics_ctrl.data_rotate) { + value = (value >> BX_VGA_THIS s.graphics_ctrl.data_rotate) | + (value << (8 - BX_VGA_THIS s.graphics_ctrl.data_rotate)); + } + new_val[0] = BX_VGA_THIS s.graphics_ctrl.latch[0] & ~bitmask; + new_val[1] = BX_VGA_THIS s.graphics_ctrl.latch[1] & ~bitmask; + new_val[2] = BX_VGA_THIS s.graphics_ctrl.latch[2] & ~bitmask; + new_val[3] = BX_VGA_THIS s.graphics_ctrl.latch[3] & ~bitmask; + switch (BX_VGA_THIS s.graphics_ctrl.raster_op) { + case 0: // replace + new_val[0] |= ((enable_set_reset & 1) + ? ((set_reset & 1) ? bitmask : 0) + : (value & bitmask)); + new_val[1] |= ((enable_set_reset & 2) + ? ((set_reset & 2) ? bitmask : 0) + : (value & bitmask)); + new_val[2] |= ((enable_set_reset & 4) + ? ((set_reset & 4) ? bitmask : 0) + : (value & bitmask)); + new_val[3] |= ((enable_set_reset & 8) + ? ((set_reset & 8) ? bitmask : 0) + : (value & bitmask)); + break; + case 1: // AND + new_val[0] |= ((enable_set_reset & 1) + ? ((set_reset & 1) + ? (BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask) + : 0) + : (value & BX_VGA_THIS s.graphics_ctrl.latch[0]) & bitmask); + new_val[1] |= ((enable_set_reset & 2) + ? ((set_reset & 2) + ? (BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask) + : 0) + : (value & BX_VGA_THIS s.graphics_ctrl.latch[1]) & bitmask); + new_val[2] |= ((enable_set_reset & 4) + ? ((set_reset & 4) + ? (BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask) + : 0) + : (value & BX_VGA_THIS s.graphics_ctrl.latch[2]) & bitmask); + new_val[3] |= ((enable_set_reset & 8) + ? ((set_reset & 8) + ? (BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask) + : 0) + : (value & BX_VGA_THIS s.graphics_ctrl.latch[3]) & bitmask); + break; + case 2: // OR + new_val[0] + |= ((enable_set_reset & 1) + ? ((set_reset & 1) + ? bitmask + : (BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask)) + : ((value | BX_VGA_THIS s.graphics_ctrl.latch[0]) & bitmask)); + new_val[1] + |= ((enable_set_reset & 2) + ? ((set_reset & 2) + ? bitmask + : (BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask)) + : ((value | BX_VGA_THIS s.graphics_ctrl.latch[1]) & bitmask)); + new_val[2] + |= ((enable_set_reset & 4) + ? ((set_reset & 4) + ? bitmask + : (BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask)) + : ((value | BX_VGA_THIS s.graphics_ctrl.latch[2]) & bitmask)); + new_val[3] + |= ((enable_set_reset & 8) + ? ((set_reset & 8) + ? bitmask + : (BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask)) + : ((value | BX_VGA_THIS s.graphics_ctrl.latch[3]) & bitmask)); + break; + case 3: // XOR + new_val[0] + |= ((enable_set_reset & 1) + ? ((set_reset & 1) + ? (~BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask) + : (BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask)) + : (value ^ BX_VGA_THIS s.graphics_ctrl.latch[0]) & bitmask); + new_val[1] + |= ((enable_set_reset & 2) + ? ((set_reset & 2) + ? (~BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask) + : (BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask)) + : (value ^ BX_VGA_THIS s.graphics_ctrl.latch[1]) & bitmask); + new_val[2] + |= ((enable_set_reset & 4) + ? ((set_reset & 4) + ? (~BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask) + : (BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask)) + : (value ^ BX_VGA_THIS s.graphics_ctrl.latch[2]) & bitmask); + new_val[3] + |= ((enable_set_reset & 8) + ? ((set_reset & 8) + ? (~BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask) + : (BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask)) + : (value ^ BX_VGA_THIS s.graphics_ctrl.latch[3]) & bitmask); + break; + default: + BX_PANIC(("vga_mem_write: write mode 0: op = %u", + (unsigned) BX_VGA_THIS s.graphics_ctrl.raster_op)); + } + } + break; + + case 1: /* write mode 1 */ + for (i=0; i<4; i++ ) { + new_val[i] = BX_VGA_THIS s.graphics_ctrl.latch[i]; + } + break; + + case 2: /* write mode 2 */ + { + const Bit8u bitmask = BX_VGA_THIS s.graphics_ctrl.bitmask; + + new_val[0] = BX_VGA_THIS s.graphics_ctrl.latch[0] & ~bitmask; + new_val[1] = BX_VGA_THIS s.graphics_ctrl.latch[1] & ~bitmask; + new_val[2] = BX_VGA_THIS s.graphics_ctrl.latch[2] & ~bitmask; + new_val[3] = BX_VGA_THIS s.graphics_ctrl.latch[3] & ~bitmask; + switch (BX_VGA_THIS s.graphics_ctrl.raster_op) { + case 0: // write + new_val[0] |= (value & 1) ? bitmask : 0; + new_val[1] |= (value & 2) ? bitmask : 0; + new_val[2] |= (value & 4) ? bitmask : 0; + new_val[3] |= (value & 8) ? bitmask : 0; + break; + case 1: // AND + new_val[0] |= (value & 1) + ? (BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask) + : 0; + new_val[1] |= (value & 2) + ? (BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask) + : 0; + new_val[2] |= (value & 4) + ? (BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask) + : 0; + new_val[3] |= (value & 8) + ? (BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask) + : 0; + break; + case 2: // OR + new_val[0] |= (value & 1) + ? bitmask + : (BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask); + new_val[1] |= (value & 2) + ? bitmask + : (BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask); + new_val[2] |= (value & 4) + ? bitmask + : (BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask); + new_val[3] |= (value & 8) + ? bitmask + : (BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask); + break; + case 3: // XOR + new_val[0] |= (value & 1) + ? (~BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask) + : (BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask); + new_val[1] |= (value & 2) + ? (~BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask) + : (BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask); + new_val[2] |= (value & 4) + ? (~BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask) + : (BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask); + new_val[3] |= (value & 8) + ? (~BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask) + : (BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask); + break; + } + } + break; + + case 3: /* write mode 3 */ + { + const Bit8u bitmask = BX_VGA_THIS s.graphics_ctrl.bitmask & value; + const Bit8u set_reset = BX_VGA_THIS s.graphics_ctrl.set_reset; + + /* perform rotate on CPU data */ + if (BX_VGA_THIS s.graphics_ctrl.data_rotate) { + value = (value >> BX_VGA_THIS s.graphics_ctrl.data_rotate) | + (value << (8 - BX_VGA_THIS s.graphics_ctrl.data_rotate)); + } + new_val[0] = BX_VGA_THIS s.graphics_ctrl.latch[0] & ~bitmask; + new_val[1] = BX_VGA_THIS s.graphics_ctrl.latch[1] & ~bitmask; + new_val[2] = BX_VGA_THIS s.graphics_ctrl.latch[2] & ~bitmask; + new_val[3] = BX_VGA_THIS s.graphics_ctrl.latch[3] & ~bitmask; + + value &= bitmask; + + switch (BX_VGA_THIS s.graphics_ctrl.raster_op) { + case 0: // write + new_val[0] |= (set_reset & 1) ? value : 0; + new_val[1] |= (set_reset & 2) ? value : 0; + new_val[2] |= (set_reset & 4) ? value : 0; + new_val[3] |= (set_reset & 8) ? value : 0; + break; + case 1: // AND + new_val[0] |= ((set_reset & 1) ? value : 0) + & BX_VGA_THIS s.graphics_ctrl.latch[0]; + new_val[1] |= ((set_reset & 2) ? value : 0) + & BX_VGA_THIS s.graphics_ctrl.latch[1]; + new_val[2] |= ((set_reset & 4) ? value : 0) + & BX_VGA_THIS s.graphics_ctrl.latch[2]; + new_val[3] |= ((set_reset & 8) ? value : 0) + & BX_VGA_THIS s.graphics_ctrl.latch[3]; + break; + case 2: // OR + new_val[0] |= ((set_reset & 1) ? value : 0) + | BX_VGA_THIS s.graphics_ctrl.latch[0]; + new_val[1] |= ((set_reset & 2) ? value : 0) + | BX_VGA_THIS s.graphics_ctrl.latch[1]; + new_val[2] |= ((set_reset & 4) ? value : 0) + | BX_VGA_THIS s.graphics_ctrl.latch[2]; + new_val[3] |= ((set_reset & 8) ? value : 0) + | BX_VGA_THIS s.graphics_ctrl.latch[3]; + break; + case 3: // XOR + new_val[0] |= ((set_reset & 1) ? value : 0) + ^ BX_VGA_THIS s.graphics_ctrl.latch[0]; + new_val[1] |= ((set_reset & 2) ? value : 0) + ^ BX_VGA_THIS s.graphics_ctrl.latch[1]; + new_val[2] |= ((set_reset & 4) ? value : 0) + ^ BX_VGA_THIS s.graphics_ctrl.latch[2]; + new_val[3] |= ((set_reset & 8) ? value : 0) + ^ BX_VGA_THIS s.graphics_ctrl.latch[3]; + break; + } + } + break; + + default: + BX_PANIC(("vga_mem_write: write mode %u ?", + (unsigned) BX_VGA_THIS s.graphics_ctrl.write_mode)); + } + + if (BX_VGA_THIS s.sequencer.map_mask & 0x0f) { + BX_VGA_THIS s.vga_mem_updated = 1; + if (BX_VGA_THIS s.sequencer.map_mask_bit[0]) + BX_VGA_THIS s.vga_memory[0*65536 + offset] = new_val[0]; + if (BX_VGA_THIS s.sequencer.map_mask_bit[1]) + BX_VGA_THIS s.vga_memory[1*65536 + offset] = new_val[1]; + if (BX_VGA_THIS s.sequencer.map_mask_bit[2]) { + if ((!BX_VGA_THIS s.graphics_ctrl.graphics_alpha) && + ((offset & 0xe000) == BX_VGA_THIS s.charmap_address)) { + bx_gui->set_text_charbyte((offset & 0x1fff), new_val[2]); + } + BX_VGA_THIS s.vga_memory[2*65536 + offset] = new_val[2]; + } + if (BX_VGA_THIS s.sequencer.map_mask_bit[3]) + BX_VGA_THIS s.vga_memory[3*65536 + offset] = new_val[3]; + + unsigned x_tileno, y_tileno; + + if (BX_VGA_THIS s.graphics_ctrl.shift_reg == 2) { + offset -= start_addr; + x_tileno = (offset % BX_VGA_THIS s.line_offset) * 4 / (X_TILESIZE / 2); + if (BX_VGA_THIS s.y_doublescan) { + y_tileno = (offset / BX_VGA_THIS s.line_offset) / (Y_TILESIZE / 2); + } else { + y_tileno = (offset / BX_VGA_THIS s.line_offset) / Y_TILESIZE; + } + SET_TILE_UPDATED (x_tileno, y_tileno, 1); + } else { + if (BX_VGA_THIS s.line_compare < BX_VGA_THIS s.vertical_display_end) { + if (BX_VGA_THIS s.x_dotclockdiv2) { + x_tileno = (offset % BX_VGA_THIS s.line_offset) / (X_TILESIZE / 16); + } else { + x_tileno = (offset % BX_VGA_THIS s.line_offset) / (X_TILESIZE / 8); + } + if (BX_VGA_THIS s.y_doublescan) { + y_tileno = ((offset / BX_VGA_THIS s.line_offset) * 2 + BX_VGA_THIS s.line_compare + 1) / Y_TILESIZE; + } else { + y_tileno = ((offset / BX_VGA_THIS s.line_offset) + BX_VGA_THIS s.line_compare + 1) / Y_TILESIZE; + } + SET_TILE_UPDATED (x_tileno, y_tileno, 1); + } + if (offset >= start_addr) { + offset -= start_addr; + if (BX_VGA_THIS s.x_dotclockdiv2) { + x_tileno = (offset % BX_VGA_THIS s.line_offset) / (X_TILESIZE / 16); + } else { + x_tileno = (offset % BX_VGA_THIS s.line_offset) / (X_TILESIZE / 8); + } + if (BX_VGA_THIS s.y_doublescan) { + y_tileno = (offset / BX_VGA_THIS s.line_offset) / (Y_TILESIZE / 2); + } else { + y_tileno = (offset / BX_VGA_THIS s.line_offset) / Y_TILESIZE; + } + SET_TILE_UPDATED (x_tileno, y_tileno, 1); + } + } + } +} + + void +bx_vga_c::get_text_snapshot(Bit8u **text_snapshot, unsigned *txHeight, + unsigned *txWidth) +{ + unsigned VDE, MSL; + + if (!BX_VGA_THIS s.graphics_ctrl.graphics_alpha) { + *text_snapshot = &BX_VGA_THIS s.text_snapshot[0]; + VDE = BX_VGA_THIS s.vertical_display_end; + MSL = BX_VGA_THIS s.CRTC.reg[0x09] & 0x1f; + *txHeight = (VDE+1)/(MSL+1); + *txWidth = BX_VGA_THIS s.CRTC.reg[1] + 1; + } else { + *txHeight = 0; + *txWidth = 0; + } +} + + Bit8u +bx_vga_c::get_actl_palette_idx(Bit8u index) +{ + return BX_VGA_THIS s.attribute_ctrl.palette_reg[index]; +} + + void +bx_vga_c::dump_status(void) +{ + BX_INFO(("s.misc_output.color_emulation = %u", + (unsigned) BX_VGA_THIS s.misc_output.color_emulation)); + BX_INFO(("s.misc_output.enable_ram = %u", + (unsigned) BX_VGA_THIS s.misc_output.enable_ram)); + BX_INFO(("s.misc_output.clock_select = %u", + (unsigned) BX_VGA_THIS s.misc_output.clock_select)); + if (BX_VGA_THIS s.misc_output.clock_select == 0) + BX_INFO((" 25Mhz 640 horiz pixel clock")); + else + BX_INFO((" 28Mhz 720 horiz pixel clock")); + BX_INFO(("s.misc_output.select_high_bank = %u", + (unsigned) BX_VGA_THIS s.misc_output.select_high_bank)); + BX_INFO(("s.misc_output.horiz_sync_pol = %u", + (unsigned) BX_VGA_THIS s.misc_output.horiz_sync_pol)); + BX_INFO(("s.misc_output.vert_sync_pol = %u", + (unsigned) BX_VGA_THIS s.misc_output.vert_sync_pol)); + switch ( (BX_VGA_THIS s.misc_output.vert_sync_pol << 1) | + BX_VGA_THIS s.misc_output.horiz_sync_pol ) { + case 0: BX_INFO((" (reserved")); break; + case 1: BX_INFO((" 400 lines")); break; + case 2: BX_INFO((" 350 lines")); break; + case 3: BX_INFO((" 480 lines")); break; + default: BX_INFO((" ???")); + } + + BX_INFO(("s.graphics_ctrl.odd_even = %u", + (unsigned) BX_VGA_THIS s.graphics_ctrl.odd_even)); + BX_INFO(("s.graphics_ctrl.chain_odd_even = %u", + (unsigned) BX_VGA_THIS s.graphics_ctrl.chain_odd_even)); + BX_INFO(("s.graphics_ctrl.shift_reg = %u", + (unsigned) BX_VGA_THIS s.graphics_ctrl.shift_reg)); + BX_INFO(("s.graphics_ctrl.graphics_alpha = %u", + (unsigned) BX_VGA_THIS s.graphics_ctrl.graphics_alpha)); + BX_INFO(("s.graphics_ctrl.memory_mapping = %u", + (unsigned) BX_VGA_THIS s.graphics_ctrl.memory_mapping)); + switch (BX_VGA_THIS s.graphics_ctrl.memory_mapping) { + case 0: BX_INFO((" A0000-BFFFF")); break; + case 1: BX_INFO((" A0000-AFFFF")); break; + case 2: BX_INFO((" B0000-B7FFF")); break; + case 3: BX_INFO((" B8000-BFFFF")); break; + default: BX_INFO((" ???")); + } + + BX_INFO(("s.sequencer.extended_mem = %u", + (unsigned) BX_VGA_THIS s.sequencer.extended_mem)); + BX_INFO(("s.sequencer.odd_even = %u (inverted)", + (unsigned) BX_VGA_THIS s.sequencer.odd_even)); + BX_INFO(("s.sequencer.chain_four = %u", + (unsigned) BX_VGA_THIS s.sequencer.chain_four)); + + BX_INFO(("s.attribute_ctrl.video_enabled = %u", + (unsigned) BX_VGA_THIS s.attribute_ctrl.video_enabled)); + BX_INFO(("s.attribute_ctrl.mode_ctrl.graphics_alpha = %u", + (unsigned) BX_VGA_THIS s.attribute_ctrl.mode_ctrl.graphics_alpha)); + BX_INFO(("s.attribute_ctrl.mode_ctrl.display_type = %u", + (unsigned) BX_VGA_THIS s.attribute_ctrl.mode_ctrl.display_type)); + BX_INFO(("s.attribute_ctrl.mode_ctrl.internal_palette_size = %u", + (unsigned) BX_VGA_THIS s.attribute_ctrl.mode_ctrl.internal_palette_size)); + BX_INFO(("s.attribute_ctrl.mode_ctrl.pixel_clock_select = %u", + (unsigned) BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_clock_select)); +} + + + void +bx_vga_c::redraw_area(unsigned x0, unsigned y0, unsigned width, + unsigned height) +{ + unsigned xi, yi, x1, y1, xmax, ymax; + + BX_VGA_THIS s.vga_mem_updated = 1; + +#if BX_SUPPORT_VBE + if (BX_VGA_THIS s.graphics_ctrl.graphics_alpha || BX_VGA_THIS s.vbe_enabled) { +#else + if (BX_VGA_THIS s.graphics_ctrl.graphics_alpha) { +#endif + // graphics mode + x1 = x0 + width - 1; + y1 = y0 + height - 1; + + xmax = old_iWidth; + ymax = old_iHeight; +#if BX_SUPPORT_VBE + if (BX_VGA_THIS s.vbe_enabled) { + xmax = BX_VGA_THIS s.vbe_xres; + ymax = BX_VGA_THIS s.vbe_yres; + } +#endif + for (yi=0; yi<ymax; yi+=Y_TILESIZE) { + for (xi=0; xi<xmax; xi+=X_TILESIZE) { + // is redraw rectangle outside x boundaries of this tile? + if (x1 < xi) continue; + if (x0 > (xi+X_TILESIZE-1)) continue; + + // is redraw rectangle outside y boundaries of this tile? + if (y1 < yi) continue; + if (y0 > (yi+Y_TILESIZE-1)) continue; + unsigned xti = xi/X_TILESIZE; + unsigned yti = yi/Y_TILESIZE; + SET_TILE_UPDATED (xti, yti, 1); + } + } + } + else { + // text mode + memset(BX_VGA_THIS s.text_snapshot, 0, + sizeof(BX_VGA_THIS s.text_snapshot)); + } +} + + +#if BX_SUPPORT_VBE + Bit8u BX_CPP_AttrRegparmN(1) +bx_vga_c::vbe_mem_read(Bit32u addr) +{ + Bit32u offset; + + if (addr >= VBE_DISPI_LFB_PHYSICAL_ADDRESS) + { + // LFB read + offset = addr - VBE_DISPI_LFB_PHYSICAL_ADDRESS; + } + else + { + // banked mode read + offset = BX_VGA_THIS s.vbe_bank*65536 + addr - 0xA0000; + } + + // check for out of memory read + if (offset > VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES) + return 0; + + return (BX_VGA_THIS s.vbe_memory[offset]); +} + + void BX_CPP_AttrRegparmN(2) +bx_vga_c::vbe_mem_write(Bit32u addr, Bit8u value) +{ + Bit32u offset; + unsigned x_tileno, y_tileno; + + if (BX_VGA_THIS s.vbe_lfb_enabled) + { + if (addr >= VBE_DISPI_LFB_PHYSICAL_ADDRESS) + { + // LFB write + offset = addr - VBE_DISPI_LFB_PHYSICAL_ADDRESS; + } + else + { + // banked mode write while in LFB mode -> ignore + return; + } + } + else + { + if (addr < VBE_DISPI_LFB_PHYSICAL_ADDRESS) + { + // banked mode write + offset = (BX_VGA_THIS s.vbe_bank*65536) + (addr - 0xA0000); + } + else + { + // LFB write while in banked mode -> ignore + return; + } + } + + // check for out of memory write + if (offset < VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES) + { + BX_VGA_THIS s.vbe_memory[offset]=value; + } + else + { + // make sure we don't flood the logfile + static int count=0; + if (count<100) + { + count ++; + BX_INFO(("VBE_mem_write out of video memory write at %x",offset)); + } + } + + offset-=BX_VGA_THIS s.vbe_virtual_start; + + // only update the UI when writing 'onscreen' + if (offset < BX_VGA_THIS s.vbe_visable_screen_size) + { + y_tileno = ((offset / BX_VGA_THIS s.vbe_bpp_multiplier) / BX_VGA_THIS s.vbe_virtual_xres) / Y_TILESIZE; + x_tileno = ((offset / BX_VGA_THIS s.vbe_bpp_multiplier) % BX_VGA_THIS s.vbe_virtual_xres) / X_TILESIZE; + + if ((y_tileno < BX_NUM_Y_TILES) && (x_tileno < BX_NUM_X_TILES)) + { + BX_VGA_THIS s.vga_mem_updated = 1; + SET_TILE_UPDATED (x_tileno, y_tileno, 1); + } + } +} + + Bit32u +bx_vga_c::vbe_read_handler(void *this_ptr, Bit32u address, unsigned io_len) +{ +#if !BX_USE_VGA_SMF + bx_vga_c *class_ptr = (bx_vga_c *) this_ptr; + + return( class_ptr->vbe_read(address, io_len) ); +} + + + Bit32u +bx_vga_c::vbe_read(Bit32u address, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif // !BX_USE_VGA_SMF + +// BX_INFO(("VBE_read %x (len %x)", address, io_len)); + + if ((address==VBE_DISPI_IOPORT_INDEX) || + (address==VBE_DISPI_IOPORT_INDEX_OLD)) + { + // index register + return (Bit32u) BX_VGA_THIS s.vbe_curindex; + } + else + { + // data register read + + switch (BX_VGA_THIS s.vbe_curindex) + { + case VBE_DISPI_INDEX_ID: // Display Interface ID check + { + return BX_VGA_THIS s.vbe_cur_dispi; + } break; + + case VBE_DISPI_INDEX_XRES: // x resolution + { + return BX_VGA_THIS s.vbe_xres; + } break; + + case VBE_DISPI_INDEX_YRES: // y resolution + { + return BX_VGA_THIS s.vbe_yres; + } break; + + case VBE_DISPI_INDEX_BPP: // bpp + { + return BX_VGA_THIS s.vbe_bpp; + } break; + + case VBE_DISPI_INDEX_ENABLE: // vbe enabled + { + return BX_VGA_THIS s.vbe_enabled; + } break; + + case VBE_DISPI_INDEX_BANK: // current bank + { + return BX_VGA_THIS s.vbe_bank; + } break; + + case VBE_DISPI_INDEX_X_OFFSET: + { + return BX_VGA_THIS s.vbe_offset_x; + } break; + + case VBE_DISPI_INDEX_Y_OFFSET: + { + return BX_VGA_THIS s.vbe_offset_y; + } break; + + case VBE_DISPI_INDEX_VIRT_WIDTH: + { + return BX_VGA_THIS s.vbe_virtual_xres; + + } break; + + case VBE_DISPI_INDEX_VIRT_HEIGHT: + { + return BX_VGA_THIS s.vbe_virtual_yres; + } break; + + + default: + { + BX_PANIC(("VBE unknown data read index 0x%x",BX_VGA_THIS s.vbe_curindex)); + } break; + } + } + BX_PANIC(("VBE_read shouldn't reach this")); + return 0; /* keep compiler happy */ +} + + void +bx_vga_c::vbe_write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len) +{ +#if !BX_USE_VGA_SMF + bx_vga_c *class_ptr = (bx_vga_c *) this_ptr; + + class_ptr->vbe_write(address, value, io_len); +} + + Bit32u +bx_vga_c::vbe_write(Bit32u address, Bit32u value, unsigned io_len) +{ +#else + UNUSED(this_ptr); +#endif + +// BX_INFO(("VBE_write %x = %x (len %x)", address, value, io_len)); + + switch(address) + { + // index register + case VBE_DISPI_IOPORT_INDEX: + // legacy index register + case VBE_DISPI_IOPORT_INDEX_OLD: + + BX_VGA_THIS s.vbe_curindex = (Bit16u) value; + break; + + // data register + // FIXME: maybe do some 'sanity' checks on received data? + case VBE_DISPI_IOPORT_DATA: + // legacy data register + case VBE_DISPI_IOPORT_DATA_OLD: + switch (BX_VGA_THIS s.vbe_curindex) + { + case VBE_DISPI_INDEX_ID: // Display Interface ID check + { + if ( (value == VBE_DISPI_ID0) || + (value == VBE_DISPI_ID1) || + (value == VBE_DISPI_ID2) ) + { + // allow backwards compatible with previous dispi bioses + BX_VGA_THIS s.vbe_cur_dispi=value; + } + else + { + BX_PANIC(("VBE unknown Display Interface %x",value)); + } + + // make sure we don't flood the logfile + static int count=0; + if (count < 100) + { + count++; + BX_INFO(("VBE known Display Interface %x",value)); + } + } break; + + case VBE_DISPI_INDEX_XRES: // set xres + { + // check that we don't set xres during vbe enabled + if (!BX_VGA_THIS s.vbe_enabled) + { + // check for within max xres range + if (value <= VBE_DISPI_MAX_XRES) + { + BX_VGA_THIS s.vbe_xres=(Bit16u) value; + BX_INFO(("VBE set xres (%d)",value)); + } + else + { + BX_INFO(("VBE set xres more then max xres (%d)",value)); + } + } + else + { + BX_INFO(("VBE set xres during vbe enabled!")); + } + } break; + + case VBE_DISPI_INDEX_YRES: // set yres + { + // check that we don't set yres during vbe enabled + if (!BX_VGA_THIS s.vbe_enabled) + { + // check for within max yres range + if (value <= VBE_DISPI_MAX_YRES) + { + BX_VGA_THIS s.vbe_yres=(Bit16u) value; + BX_INFO(("VBE set yres (%d)",value)); + } + else + { + BX_INFO(("VBE set yres more then max yres (%d)",value)); + } + } + else + { + BX_INFO(("VBE set yres during vbe enabled!")); + } + } break; + + case VBE_DISPI_INDEX_BPP: // set bpp + { + // check that we don't set bpp during vbe enabled + if (!BX_VGA_THIS s.vbe_enabled) + { + // for backward compatiblity + if (value == 0) value = VBE_DISPI_BPP_8; + // check for correct bpp range + if ((value == VBE_DISPI_BPP_4) || (value == VBE_DISPI_BPP_8) || (value == VBE_DISPI_BPP_15) || + (value == VBE_DISPI_BPP_16) || (value == VBE_DISPI_BPP_24) || (value == VBE_DISPI_BPP_32)) + { + BX_VGA_THIS s.vbe_bpp=(Bit16u) value; + BX_INFO(("VBE set bpp (%d)",value)); + } + else + { + BX_INFO(("VBE set bpp with unknown bpp (%d)",value)); + } + } + else + { + BX_INFO(("VBE set bpp during vbe enabled!")); + } + } break; + + case VBE_DISPI_INDEX_BANK: // set bank + { + value=value & 0xff ; // FIXME lobyte = vbe bank A? + + // check for max bank nr + if (value < (VBE_DISPI_TOTAL_VIDEO_MEMORY_KB /64)) + { + if (!BX_VGA_THIS s.vbe_lfb_enabled) + { + BX_DEBUG(("VBE set bank to %d", value)); + BX_VGA_THIS s.vbe_bank=value; + } + else + { + BX_ERROR(("VBE set bank in LFB mode ignored")); + } + } + else + { + BX_INFO(("VBE set invalid bank (%d)",value)); + } + } break; + + case VBE_DISPI_INDEX_ENABLE: // enable video + { + if (value & VBE_DISPI_ENABLED) + { + unsigned depth=0; + + // setup virtual resolution to be the same as current reso + BX_VGA_THIS s.vbe_virtual_yres=BX_VGA_THIS s.vbe_yres; + BX_VGA_THIS s.vbe_virtual_xres=BX_VGA_THIS s.vbe_xres; + + // reset offset + BX_VGA_THIS s.vbe_offset_x=0; + BX_VGA_THIS s.vbe_offset_y=0; + BX_VGA_THIS s.vbe_virtual_start=0; + + switch((BX_VGA_THIS s.vbe_bpp)) + { + // Default pixel sizes + case VBE_DISPI_BPP_8: + BX_VGA_THIS s.vbe_bpp_multiplier = 1; + BX_VGA_THIS s.vbe_line_byte_width = BX_VGA_THIS s.vbe_virtual_xres; + BX_VGA_THIS s.vbe_visable_screen_size = ((BX_VGA_THIS s.vbe_xres) * (BX_VGA_THIS s.vbe_yres)); + depth=8; + break; + + case VBE_DISPI_BPP_4: + BX_VGA_THIS s.vbe_bpp_multiplier = 1; + BX_VGA_THIS s.vbe_line_byte_width = BX_VGA_THIS s.vbe_virtual_xres; + BX_VGA_THIS s.vbe_visable_screen_size = ((BX_VGA_THIS s.vbe_xres) * (BX_VGA_THIS s.vbe_yres)); + depth=4; + break; + + case VBE_DISPI_BPP_15: + BX_VGA_THIS s.vbe_bpp_multiplier = 2; + BX_VGA_THIS s.vbe_line_byte_width = BX_VGA_THIS s.vbe_virtual_xres * 2; + BX_VGA_THIS s.vbe_visable_screen_size = ((BX_VGA_THIS s.vbe_xres) * (BX_VGA_THIS s.vbe_yres)) * 2; + depth=15; + break; + + case VBE_DISPI_BPP_16: + BX_VGA_THIS s.vbe_bpp_multiplier = 2; + BX_VGA_THIS s.vbe_line_byte_width = BX_VGA_THIS s.vbe_virtual_xres * 2; + BX_VGA_THIS s.vbe_visable_screen_size = ((BX_VGA_THIS s.vbe_xres) * (BX_VGA_THIS s.vbe_yres)) * 2; + depth=16; + break; + + case VBE_DISPI_BPP_24: + BX_VGA_THIS s.vbe_bpp_multiplier = 3; + BX_VGA_THIS s.vbe_line_byte_width = BX_VGA_THIS s.vbe_virtual_xres * 3; + BX_VGA_THIS s.vbe_visable_screen_size = ((BX_VGA_THIS s.vbe_xres) * (BX_VGA_THIS s.vbe_yres)) * 3; + depth=24; + break; + + case VBE_DISPI_BPP_32: + BX_VGA_THIS s.vbe_bpp_multiplier = 4; + BX_VGA_THIS s.vbe_line_byte_width = BX_VGA_THIS s.vbe_virtual_xres << 2; + BX_VGA_THIS s.vbe_visable_screen_size = ((BX_VGA_THIS s.vbe_xres) * (BX_VGA_THIS s.vbe_yres)) << 2; + depth=32; + break; + } + + BX_INFO(("VBE enabling x %d, y %d, bpp %d, %u bytes visible", BX_VGA_THIS s.vbe_xres, BX_VGA_THIS s.vbe_yres, BX_VGA_THIS s.vbe_bpp, BX_VGA_THIS s.vbe_visable_screen_size)); + + if (depth > 4) + { + BX_VGA_THIS s.vbe_lfb_enabled=(bx_bool)(value & VBE_DISPI_LFB_ENABLED); + if ((value & VBE_DISPI_NOCLEARMEM) == 0) + { + memset(BX_VGA_THIS s.vbe_memory, 0, BX_VGA_THIS s.vbe_visable_screen_size); + } + bx_gui->dimension_update(BX_VGA_THIS s.vbe_xres, BX_VGA_THIS s.vbe_yres, 0, 0, depth); + old_BPP = depth; + // some test applications expect these standard VGA settings + BX_VGA_THIS s.CRTC.reg[9] = 0x00; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.graphics_alpha = 1; + BX_VGA_THIS s.graphics_ctrl.memory_mapping = 1; + BX_VGA_THIS s.CRTC.reg[1] = (BX_VGA_THIS s.vbe_xres / 8) - 1; + BX_VGA_THIS s.CRTC.reg[19] = BX_VGA_THIS s.vbe_xres >> 2; + BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_clock_select = 1; + BX_VGA_THIS s.CRTC.reg[18] = (BX_VGA_THIS s.vbe_yres - 1) & 0xff; + BX_VGA_THIS s.CRTC.reg[7] &= ~0x42; + if ((BX_VGA_THIS s.vbe_yres - 1) & 0x0100) { + BX_VGA_THIS s.CRTC.reg[7] |= 0x02; + } + if ((BX_VGA_THIS s.vbe_yres - 1) & 0x0200) { + BX_VGA_THIS s.CRTC.reg[7] |= 0x40; + } + } + } + else + { + if (BX_VGA_THIS s.vbe_enabled) BX_INFO(("VBE disabling")); + BX_VGA_THIS s.vbe_lfb_enabled=0; + } + BX_VGA_THIS s.vbe_enabled=(bx_bool)(value & VBE_DISPI_ENABLED); + } break; + + case VBE_DISPI_INDEX_X_OFFSET: + { + // BX_INFO(("VBE offset x %x",value)); + BX_VGA_THIS s.vbe_offset_x=(Bit16u)value; + + BX_VGA_THIS s.vbe_virtual_start = ((BX_VGA_THIS s.vbe_offset_y) * (BX_VGA_THIS s.vbe_line_byte_width)) + + ((BX_VGA_THIS s.vbe_offset_x) * (BX_VGA_THIS s.vbe_bpp_multiplier)); + + BX_VGA_THIS s.vga_mem_updated = 1; + for (unsigned xti = 0; xti < BX_NUM_X_TILES; xti++) { + for (unsigned yti = 0; yti < BX_NUM_Y_TILES; yti++) { + SET_TILE_UPDATED (xti, yti, 1); + } + } + } break; + + case VBE_DISPI_INDEX_Y_OFFSET: + { + // BX_INFO(("VBE offset y %x",value)); + BX_VGA_THIS s.vbe_offset_y=(Bit16u)value; + BX_VGA_THIS s.vbe_virtual_start = ((BX_VGA_THIS s.vbe_offset_y) * (BX_VGA_THIS s.vbe_line_byte_width)) + + ((BX_VGA_THIS s.vbe_offset_x) * (BX_VGA_THIS s.vbe_bpp_multiplier)); + + BX_VGA_THIS s.vga_mem_updated = 1; + for (unsigned xti = 0; xti < BX_NUM_X_TILES; xti++) { + for (unsigned yti = 0; yti < BX_NUM_Y_TILES; yti++) { + SET_TILE_UPDATED (xti, yti, 1); + } + } + } break; + + case VBE_DISPI_INDEX_VIRT_WIDTH: + { + BX_INFO(("VBE requested virtual width %d",value)); + + // calculate virtual width & height dimensions + // req: + // virt_width > xres + // virt_height >=yres + // virt_width*virt_height < MAX_VIDEO_MEMORY + + // basicly 2 situations + + // situation 1: + // MAX_VIDEO_MEMORY / virt_width >= yres + // adjust result height + // else + // adjust result width based upon virt_height=yres + Bit16u new_width=value; + Bit16u new_height=(sizeof(BX_VGA_THIS s.vbe_memory) / BX_VGA_THIS s.vbe_bpp_multiplier) / new_width; + if (new_height >=BX_VGA_THIS s.vbe_yres) + { + // we have a decent virtual width & new_height + BX_INFO(("VBE decent virtual height %d",new_height)); + } + else + { + // no decent virtual height: adjust width & height + new_height=BX_VGA_THIS s.vbe_yres; + new_width=(sizeof(BX_VGA_THIS s.vbe_memory) / BX_VGA_THIS s.vbe_bpp_multiplier) / new_height; + + BX_INFO(("VBE recalc virtual width %d height %d",new_width, new_height)); + } + + BX_VGA_THIS s.vbe_virtual_xres=new_width; + BX_VGA_THIS s.vbe_virtual_yres=new_height; + BX_VGA_THIS s.vbe_visable_screen_size = (new_width * (BX_VGA_THIS s.vbe_yres)) * BX_VGA_THIS s.vbe_bpp_multiplier; + + } break; + /* + case VBE_DISPI_INDEX_VIRT_HEIGHT: + { + BX_INFO(("VBE virtual height %x",value)); + + } break; + */ + default: + { + BX_PANIC(("VBE unknown data write index 0x%x",BX_VGA_THIS s.vbe_curindex)); + } break; + } + break; + + } // end switch address +} + +#endif diff --git a/tools/ioemu/iodev/vga.h b/tools/ioemu/iodev/vga.h new file mode 100644 index 0000000000..cafb60cd6b --- /dev/null +++ b/tools/ioemu/iodev/vga.h @@ -0,0 +1,300 @@ +///////////////////////////////////////////////////////////////////////// +// $Id: vga.h,v 1.36 2003/12/31 10:33:27 vruppert Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + +#if BX_SUPPORT_VBE + #define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB 4 + + #define VBE_DISPI_BANK_ADDRESS 0xA0000 + #define VBE_DISPI_BANK_SIZE_KB 64 + + #define VBE_DISPI_MAX_XRES 1024 + #define VBE_DISPI_MAX_YRES 768 + + #define VBE_DISPI_IOPORT_INDEX 0x01CE + #define VBE_DISPI_IOPORT_DATA 0x01CF + + #define VBE_DISPI_IOPORT_INDEX_OLD 0xFF80 + #define VBE_DISPI_IOPORT_DATA_OLD 0xFF81 + + #define VBE_DISPI_INDEX_ID 0x0 + #define VBE_DISPI_INDEX_XRES 0x1 + #define VBE_DISPI_INDEX_YRES 0x2 + #define VBE_DISPI_INDEX_BPP 0x3 + #define VBE_DISPI_INDEX_ENABLE 0x4 + #define VBE_DISPI_INDEX_BANK 0x5 + #define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 + #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 + #define VBE_DISPI_INDEX_X_OFFSET 0x8 + #define VBE_DISPI_INDEX_Y_OFFSET 0x9 + + #define VBE_DISPI_ID0 0xB0C0 + #define VBE_DISPI_ID1 0xB0C1 + #define VBE_DISPI_ID2 0xB0C2 + + #define VBE_DISPI_BPP_4 0x04 + #define VBE_DISPI_BPP_8 0x08 + #define VBE_DISPI_BPP_15 0x0F + #define VBE_DISPI_BPP_16 0x10 + #define VBE_DISPI_BPP_24 0x18 + #define VBE_DISPI_BPP_32 0x20 + + #define VBE_DISPI_DISABLED 0x00 + #define VBE_DISPI_ENABLED 0x01 + #define VBE_DISPI_NOCLEARMEM 0x80 + #define VBE_DISPI_LFB_ENABLED 0x40 + + #define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 + + +#define VBE_DISPI_TOTAL_VIDEO_MEMORY_KB (VBE_DISPI_TOTAL_VIDEO_MEMORY_MB * 1024) +#define VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES (VBE_DISPI_TOTAL_VIDEO_MEMORY_KB * 1024) + +#define BX_MAX_XRES VBE_DISPI_MAX_XRES +#define BX_MAX_YRES VBE_DISPI_MAX_YRES + +#else + +#define BX_MAX_XRES 800 +#define BX_MAX_YRES 600 + +#endif //BX_SUPPORT_VBE + +#define X_TILESIZE 16 +#define Y_TILESIZE 24 +#define BX_NUM_X_TILES (BX_MAX_XRES /X_TILESIZE) +#define BX_NUM_Y_TILES (BX_MAX_YRES /Y_TILESIZE) + +// Support varying number of rows of text. This used to +// be limited to only 25 lines. +#define BX_MAX_TEXT_LINES 100 + +#if BX_USE_VGA_SMF +# define BX_VGA_SMF static +# define BX_VGA_THIS theVga-> +#else +# define BX_VGA_SMF +# define BX_VGA_THIS this-> +#endif + + +class bx_vga_c : public bx_vga_stub_c { +public: + + bx_vga_c(void); + ~bx_vga_c(void); + virtual void init(void); + virtual void bios_init(void); + virtual void reset(unsigned type); + virtual Bit8u mem_read(Bit32u addr); + // Note: either leave value of type Bit8u, or mask it when + // used to 8 bits, in memory.cc + virtual void mem_write(Bit32u addr, Bit8u value); + virtual void trigger_timer(void *this_ptr); + +#if BX_SUPPORT_VBE + BX_VGA_SMF Bit8u vbe_mem_read(Bit32u addr) BX_CPP_AttrRegparmN(1); + BX_VGA_SMF void vbe_mem_write(Bit32u addr, Bit8u value) BX_CPP_AttrRegparmN(2); +#endif + + virtual void redraw_area(unsigned x0, unsigned y0, + unsigned width, unsigned height); + + virtual void set_update_interval (unsigned interval); + virtual void get_text_snapshot(Bit8u **text_snapshot, unsigned *txHeight, + unsigned *txWidth); + virtual Bit8u get_actl_palette_idx(Bit8u index); + +private: + + static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); + static void write_handler_no_log(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); + +#if BX_SUPPORT_VBE + static Bit32u vbe_read_handler(void *this_ptr, Bit32u address, unsigned io_len); + static void vbe_write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len); +#endif + + struct { + struct { + bx_bool color_emulation; // 1=color emulation, base address = 3Dx + // 0=mono emulation, base address = 3Bx + bx_bool enable_ram; // enable CPU access to video memory if set + Bit8u clock_select; // 0=25Mhz 1=28Mhz + bx_bool select_high_bank; // when in odd/even modes, select + // high 64k bank if set + bx_bool horiz_sync_pol; // bit6: negative if set + bx_bool vert_sync_pol; // bit7: negative if set + // bit7,bit6 represent number of lines on display: + // 0 = reserved + // 1 = 400 lines + // 2 = 350 lines + // 3 - 480 lines + } misc_output; + + struct { + Bit8u address; + Bit8u reg[0x19]; + } CRTC; + + struct { + bx_bool flip_flop; /* 0 = address, 1 = data-write */ + unsigned address; /* register number */ + bx_bool video_enabled; + Bit8u palette_reg[16]; + Bit8u overscan_color; + Bit8u color_plane_enable; + Bit8u horiz_pel_panning; + Bit8u color_select; + struct { + bx_bool graphics_alpha; + bx_bool display_type; + bx_bool enable_line_graphics; + bx_bool blink_intensity; + bx_bool pixel_panning_compat; + bx_bool pixel_clock_select; + bx_bool internal_palette_size; + } mode_ctrl; + } attribute_ctrl; + + struct { + Bit8u write_data_register; + Bit8u write_data_cycle; /* 0, 1, 2 */ + Bit8u read_data_register; + Bit8u read_data_cycle; /* 0, 1, 2 */ + Bit8u dac_state; + struct { + Bit8u red; + Bit8u green; + Bit8u blue; + } data[256]; + Bit8u mask; + } pel; + + + struct { + Bit8u index; + Bit8u set_reset; + Bit8u enable_set_reset; + Bit8u color_compare; + Bit8u data_rotate; + Bit8u raster_op; + Bit8u read_map_select; + Bit8u write_mode; + bx_bool read_mode; + bx_bool odd_even; + bx_bool chain_odd_even; + Bit8u shift_reg; + bx_bool graphics_alpha; + Bit8u memory_mapping; /* 0 = use A0000-BFFFF + * 1 = use A0000-AFFFF EGA/VGA graphics modes + * 2 = use B0000-B7FFF Monochrome modes + * 3 = use B8000-BFFFF CGA modes + */ + Bit8u color_dont_care; + Bit8u bitmask; + Bit8u latch[4]; + } graphics_ctrl; + + struct { + Bit8u index; + Bit8u map_mask; + bx_bool map_mask_bit[4]; + bx_bool reset1; + bx_bool reset2; + Bit8u reg1; + Bit8u char_map_select; + bx_bool extended_mem; + bx_bool odd_even; + bx_bool chain_four; + } sequencer; + + bx_bool vga_mem_updated; + unsigned x_tilesize; + unsigned y_tilesize; + unsigned line_offset; + unsigned line_compare; + unsigned vertical_display_end; + bx_bool vga_tile_updated[BX_NUM_X_TILES][BX_NUM_Y_TILES]; + Bit8u vga_memory[256 * 1024]; + Bit8u text_snapshot[32 * 1024]; // current text snapshot + Bit8u rgb[3 * 256]; + Bit8u tile[X_TILESIZE * Y_TILESIZE * 4]; /**< Currently allocates the tile as large as needed. */ + Bit16u charmap_address; + bx_bool x_dotclockdiv2; + bx_bool y_doublescan; + +#if BX_SUPPORT_VBE + Bit8u vbe_memory[VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES]; + Bit16u vbe_cur_dispi; + Bit16u vbe_xres; + Bit16u vbe_yres; + Bit16u vbe_bpp; + Bit16u vbe_bank; + bx_bool vbe_enabled; + Bit16u vbe_curindex; + Bit32u vbe_visable_screen_size; /**< in bytes */ + Bit16u vbe_offset_x; /**< Virtual screen x start (in pixels) */ + Bit16u vbe_offset_y; /**< Virtual screen y start (in pixels) */ + Bit16u vbe_virtual_xres; + Bit16u vbe_virtual_yres; + Bit16u vbe_line_byte_width; /**< For dealing with bpp>8, this is they width of a line in bytes. */ + Bit32u vbe_virtual_start; /**< For dealing with bpp>8, this is where the virtual screen starts. */ + Bit8u vbe_bpp_multiplier; /**< We have to save this b/c sometimes we need to recalculate stuff with it. */ + bx_bool vbe_lfb_enabled; +#endif + } s; // state information + + +#if !BX_USE_VGA_SMF + Bit32u read(Bit32u address, unsigned io_len); + void write(Bit32u address, Bit32u value, unsigned io_len, bx_bool no_log); +#else + void write(Bit32u address, Bit32u value, unsigned io_len, bx_bool no_log); +#endif + +#if BX_SUPPORT_VBE + +#if !BX_USE_VGA_SMF + Bit32u vbe_read(Bit32u address, unsigned io_len); + void vbe_write(Bit32u address, Bit32u value, unsigned io_len, bx_bool no_log); +#else + void vbe_write(Bit32u address, Bit32u value, unsigned io_len, bx_bool no_log); +#endif +#endif + + int timer_id; + + public: + static void timer_handler(void *); + BX_VGA_SMF void timer(void); + + private: + BX_VGA_SMF void update(void); + BX_VGA_SMF void dump_status(void); + BX_VGA_SMF void determine_screen_dimensions(unsigned *piHeight, + unsigned *piWidth); + }; diff --git a/tools/ioemu/iodev/virt_timer.cc b/tools/ioemu/iodev/virt_timer.cc new file mode 100644 index 0000000000..eb108a025b --- /dev/null +++ b/tools/ioemu/iodev/virt_timer.cc @@ -0,0 +1,552 @@ +//////////////////////////////////////////////////////////////////////// +// $Id: virt_timer.cc,v 1.19.2.1 2004/02/06 22:14:36 danielg4 Exp $ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 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 + +///////////////////////////////////////////////////////////////////////// +// +//Realtime Algorithm (with gettimeofday) +// HAVE: +// Real number of usec. +// Emulated number of usec. +// WANT: +// Number of ticks to use. +// Number of emulated usec to wait until next try. +// +// ticks=number of ticks needed to match total real usec. +// if(desired ticks > max ticks for elapsed real time) +// ticks = max ticks for elapsed real time. +// if(desired ticks > max ticks for elapsed emulated usec) +// ticks = max ticks for emulated usec. +// next wait ticks = number of ticks until next event. +// next wait real usec = (current ticks + next wait ticks) * usec per ticks +// next wait emulated usec = next wait real usec * emulated usec / real usec +// if(next wait emulated usec < minimum emulated usec for next wait ticks) +// next wait emulated usec = minimum emulated usec for next wait ticks. +// if(next wait emulated usec > max emulated usec wait) +// next wait emulated usec = max emulated usec wait. +// +// How to calculate elapsed real time: +// store an unused time value whenever no ticks are used in a given time. +// add this to the current elapsed time. +// How to calculate elapsed emulated time: +// same as above. +// Above can be done by not updating last_usec and last_sec. +// +// How to calculate emulated usec/real usec: +// Each time there are actual ticks: +// Alpha_product(old emulated usec, emulated usec); +// Alpha_product(old real usec, real usec); +// Divide resulting values. +// +///////////////////////////////////////////////////////////////////////// + +#include "bochs.h" + +#define BX_USE_VIRTUAL_TIMERS 1 +#define BX_VIRTUAL_TIMERS_REALTIME 1 + +//Important constant #defines: +#define USEC_PER_SECOND (1000000) + + +// define a macro to convert floating point numbers into 64-bit integers. +// In MSVC++ you can convert a 64-bit float into a 64-bit signed integer, +// but it will not convert a 64-bit float into a 64-bit unsigned integer. +// This macro works around that. +#define F2I(x) ((Bit64u)(Bit64s) (x)) +#define I2F(x) ((double)(Bit64s) (x)) + +//CONFIGURATION #defines: + + +//MAINLINE Configuration (For realtime PIT): + +//How much faster than real time we can go: +#define MAX_MULT (1.25) + +//Minimum number of emulated useconds per second. +// Now calculated using BX_MIN_IPS, the minimum number of +// instructions per second. +#define MIN_USEC_PER_SECOND (((((Bit64u)USEC_PER_SECOND)*((Bit64u)BX_MIN_IPS))/((Bit64u)(bx_options.Oips->get())))+(Bit64u)1) + + +//DEBUG configuration: + +//Debug with printf options. +#define DEBUG_REALTIME_WITH_PRINTF 0 + +//Use to test execution at multiples of real time. +#define TIME_DIVIDER (1) +#define TIME_MULTIPLIER (1) +#define TIME_HEADSTART (0) + + +#define GET_VIRT_REALTIME64_USEC() (((bx_get_realtime64_usec()*(Bit64u)TIME_MULTIPLIER/(Bit64u)TIME_DIVIDER))) +//Set up Logging. +#define LOG_THIS bx_virt_timer. + +//A single instance. +bx_virt_timer_c bx_virt_timer; + + +//Generic MAX and MIN Functions +#define BX_MAX(a,b) ( ((a)>(b))?(a):(b) ) +#define BX_MIN(a,b) ( ((a)>(b))?(b):(a) ) + + +//USEC_ALPHA is multiplier for the past. +//USEC_ALPHA_B is 1-USEC_ALPHA, or multiplier for the present. +#define USEC_ALPHA ((double)(.8)) +#define USEC_ALPHA_B ((double)(((double)1)-USEC_ALPHA)) +#define USEC_ALPHA2 ((double)(.5)) +#define USEC_ALPHA2_B ((double)(((double)1)-USEC_ALPHA2)) +#define ALPHA_LOWER(old,new) ((Bit64u)((old<new)?((USEC_ALPHA*(I2F(old)))+(USEC_ALPHA_B*(I2F(new)))):((USEC_ALPHA2*(I2F(old)))+(USEC_ALPHA2_B*(I2F(new)))))) + + +//Conversion between emulated useconds and optionally realtime ticks. +#define TICKS_TO_USEC(a) ( ((a)*usec_per_second)/ticks_per_second ) +#define USEC_TO_TICKS(a) ( ((a)*ticks_per_second)/usec_per_second ) + +bx_virt_timer_c::bx_virt_timer_c( void ) +{ + put("VTIMER"); + settype(VTIMERLOG); + + numTimers = 0; + current_timers_time = 0; + timers_next_event_time = BX_MAX_VIRTUAL_TIME; + last_sequential_time = 0; + in_timer_handler = 0; + virtual_next_event_time = BX_MAX_VIRTUAL_TIME; + current_virtual_time = 0; + + use_virtual_timers = BX_USE_VIRTUAL_TIMERS; + init_done = 0; +} + +bx_virt_timer_c::~bx_virt_timer_c( void ) +{ +} + + + +const Bit64u bx_virt_timer_c::NullTimerInterval = BX_MAX_VIRTUAL_TIME; + +void +bx_virt_timer_c::nullTimer(void* this_ptr) { + UNUSED(this_ptr); +} + +void +bx_virt_timer_c::periodic(Bit64u time_passed) { + //Assert that we haven't skipped any events. + BX_ASSERT (time_passed <= timers_next_event_time); + BX_ASSERT(!in_timer_handler); + + //Update time variables. + timers_next_event_time -= time_passed; + current_timers_time += time_passed; + + //If no events are occurring, just pass the time and we're done. + if( time_passed < timers_next_event_time ) { + return; + } + //Starting timer handler calls. + in_timer_handler = 1; + //Otherwise, cause any events to occur that should. + unsigned i; + for(i=0;i<numTimers;i++) { + if( timer[i].inUse && timer[i].active ) { + //Assert that we haven't skipped any timers. + BX_ASSERT(current_timers_time <= timer[i].timeToFire); + if(timer[i].timeToFire == current_timers_time) { + if(timer[i].continuous) { + timer[i].timeToFire+=timer[i].period; + } else { + timer[i].active = 0; + } + //This function MUST return, or the timer mechanism + // will be broken. + timer[i].funct(timer[i].this_ptr); + } + } + } + //Finished timer handler calls. + in_timer_handler = 0; + //Use a second FOR loop so that a timer function call can + // change the behavior of another timer. + //timers_next_event_time normally contains a cycle count, not a cycle time. + // here we use it as a temporary variable that IS a cycle time, + // but then convert it back to a cycle count afterwards. + timers_next_event_time = current_timers_time + BX_MAX_VIRTUAL_TIME; + for(i=0;i<numTimers;i++) { + if( timer[i].inUse && timer[i].active && ((timer[i].timeToFire)<timers_next_event_time) ) { + timers_next_event_time = timer[i].timeToFire; + } + } + timers_next_event_time-=current_timers_time; + next_event_time_update(); + //FIXME +} + + +//Get the current virtual time. +// This may return the same value on subsequent calls. +Bit64u +bx_virt_timer_c::time_usec(void) { + if(!use_virtual_timers) { + return bx_pc_system.time_usec(); + } + + //Update the time here only if we're not in a timer handler. + //If we're in a timer handler we're up-to-date, and otherwise + // this prevents call stack loops. + if(!in_timer_handler) { + timer_handler(); + } + + return current_timers_time; +} + +//Get the current virtual time. +// This will return a monotonically increasing value. +// MUST NOT be called from within a timer interrupt. +Bit64u +bx_virt_timer_c::time_usec_sequential(void) { + if(!use_virtual_timers) { + return bx_pc_system.time_usec_sequential(); + } + + //Can't prevent call stack loops here, so this + // MUST NOT be called from within a timer handler. + BX_ASSERT(timers_next_event_time>0); + BX_ASSERT(!in_timer_handler); + + if(last_sequential_time >= current_timers_time) { + periodic(1); + last_sequential_time = current_timers_time; + } + return current_timers_time; +} + + + +//Register a timer handler to go off after a given interval. +//Register a timer handler to go off with a periodic interval. +int +bx_virt_timer_c::register_timer( void *this_ptr, bx_timer_handler_t handler, + Bit32u useconds, + bx_bool continuous, bx_bool active, + const char *id) { + if(!use_virtual_timers) { + return bx_pc_system.register_timer(this_ptr, handler, useconds, + continuous, active, id); + } + + //We don't like starting with a zero period timer. + BX_ASSERT((!active) || (useconds>0)); + + //Search for an unused timer. + unsigned int i; + for (i=0; i < numTimers; i++) { + if (timer[i].inUse == 0 || i==numTimers) + break; + } + // If we didn't find a free slot, increment the bound, numTimers. + if (i==numTimers) + numTimers++; // One new timer installed. + BX_ASSERT(numTimers<BX_MAX_VIRTUAL_TIMERS); + + timer[i].inUse = 1; + timer[i].period = useconds; + timer[i].timeToFire = current_timers_time + (Bit64u)useconds; + timer[i].active = active; + timer[i].continuous = continuous; + timer[i].funct = handler; + timer[i].this_ptr = this_ptr; + strncpy(timer[i].id, id, BxMaxTimerIDLen); + timer[i].id[BxMaxTimerIDLen-1]=0; //I like null terminated strings. + + if(useconds < timers_next_event_time) { + timers_next_event_time = useconds; + next_event_time_update(); + //FIXME + } + return i; +} + +//unregister a previously registered timer. +unsigned +bx_virt_timer_c::unregisterTimer(int timerID) { + if(!use_virtual_timers) { + return bx_pc_system.unregisterTimer(timerID); + } + + BX_ASSERT(timerID >= 0); + BX_ASSERT(timerID < BX_MAX_VIRTUAL_TIMERS); + + if (timer[timerID].active) { + BX_PANIC(("unregisterTimer: timer '%s' is still active!", timer[timerID].id)); + return(0); // Fail. + } + + + //No need to prevent doing this to unused timers. + timer[timerID].inUse = 0; + return(1); +} + +void +bx_virt_timer_c::start_timers(void) { + if(!use_virtual_timers) { + bx_pc_system.start_timers(); + return; + } + //FIXME +} + +//activate a deactivated but registered timer. +void +bx_virt_timer_c::activate_timer( unsigned timer_index, Bit32u useconds, + bx_bool continuous ) { + if(!use_virtual_timers) { + bx_pc_system.activate_timer(timer_index, useconds, continuous); + return; + } + + BX_ASSERT(timer_index >= 0); + BX_ASSERT(timer_index < BX_MAX_VIRTUAL_TIMERS); + + BX_ASSERT(timer[timer_index].inUse); + BX_ASSERT(useconds>0); + + timer[timer_index].period=useconds; + timer[timer_index].timeToFire = current_timers_time + (Bit64u)useconds; + timer[timer_index].active=1; + timer[timer_index].continuous=continuous; + + if(useconds < timers_next_event_time) { + timers_next_event_time = useconds; + next_event_time_update(); + //FIXME + } +} + +//deactivate (but don't unregister) a currently registered timer. +void +bx_virt_timer_c::deactivate_timer( unsigned timer_index ) { + if(!use_virtual_timers) { + bx_pc_system.deactivate_timer(timer_index); + return; + } + + BX_ASSERT(timer_index >= 0); + BX_ASSERT(timer_index < BX_MAX_VIRTUAL_TIMERS); + + //No need to prevent doing this to unused/inactive timers. + timer[timer_index].active = 0; +} + +void +bx_virt_timer_c::advance_virtual_time(Bit64u time_passed) { + BX_ASSERT(time_passed <= virtual_next_event_time); + + current_virtual_time += time_passed; + virtual_next_event_time -= time_passed; + + if(current_virtual_time > current_timers_time) { + periodic(current_virtual_time - current_timers_time); + } +} + +//Called when next_event_time changes. +void +bx_virt_timer_c::next_event_time_update(void) { + virtual_next_event_time = timers_next_event_time + current_timers_time - current_virtual_time; + if(init_done) { + bx_pc_system.deactivate_timer(system_timer_id); + BX_ASSERT(virtual_next_event_time); + bx_pc_system.activate_timer(system_timer_id, + (Bit32u)BX_MIN(0x7FFFFFFF,BX_MAX(1,TICKS_TO_USEC(virtual_next_event_time))), + 0); + } +} + +void +bx_virt_timer_c::init(void) { + + if ( (bx_options.clock.Osync->get ()!=BX_CLOCK_SYNC_REALTIME) + && (bx_options.clock.Osync->get ()!=BX_CLOCK_SYNC_BOTH) ) + virtual_timers_realtime = 0; + else + virtual_timers_realtime = 1; + + if (virtual_timers_realtime) { + BX_INFO(("using 'realtime pit' synchronization method")); + } + + register_timer(this, nullTimer, (Bit32u)NullTimerInterval, 1, 1, "Null Timer"); + + system_timer_id = bx_pc_system.register_timer(this, pc_system_timer_handler,virtual_next_event_time , 0, 1, "Virtual Timer"); + + //Real time variables: +#if BX_HAVE_REALTIME_USEC + last_real_time=GET_VIRT_REALTIME64_USEC()+(Bit64u)TIME_HEADSTART*(Bit64u)USEC_PER_SECOND; +#endif + total_real_usec=0; + last_realtime_delta=0; + //System time variables: + last_usec = 0 +; + usec_per_second = USEC_PER_SECOND; + stored_delta=0; + last_system_usec=0; + em_last_realtime=0; + //Virtual timer variables: + total_ticks=0; + last_realtime_ticks=0; + ticks_per_second = USEC_PER_SECOND; + + init_done = 1; +} + +void +bx_virt_timer_c::timer_handler(void) { + if(!virtual_timers_realtime) { + Bit64u temp_final_time = bx_pc_system.time_usec(); + temp_final_time-=current_virtual_time; + while(temp_final_time) { + if((temp_final_time)>(virtual_next_event_time)) { + temp_final_time-=virtual_next_event_time; + advance_virtual_time(virtual_next_event_time); + } else { + advance_virtual_time(temp_final_time); + temp_final_time-=temp_final_time; + } + } + bx_pc_system.activate_timer(system_timer_id, + (Bit32u)BX_MIN(0x7FFFFFFF,(virtual_next_event_time>2)?(virtual_next_event_time-2):1), + 0); + return; + } + + Bit64u usec_delta = bx_pc_system.time_usec()-last_usec; + + if (usec_delta) { +#if BX_HAVE_REALTIME_USEC + Bit64u ticks_delta = 0; + Bit64u real_time_delta = GET_VIRT_REALTIME64_USEC() - last_real_time; + Bit64u real_time_total = real_time_delta + total_real_usec; + Bit64u system_time_delta = (Bit64u)usec_delta + (Bit64u)stored_delta; + if(real_time_delta) { + last_realtime_delta = real_time_delta; + last_realtime_ticks = total_ticks; + } + ticks_per_second = USEC_PER_SECOND; + + //Start out with the number of ticks we would like + // to have to line up with real time. + ticks_delta = real_time_total - total_ticks; + if(real_time_total < total_ticks) { + //This slows us down if we're already ahead. + // probably only an issue on startup, but it solves some problems. + ticks_delta = 0; + } + if(ticks_delta + total_ticks - last_realtime_ticks > (F2I(MAX_MULT * I2F(last_realtime_delta)))) { + //This keeps us from going too fast in relation to real time. +#if 0 + ticks_delta = (F2I(MAX_MULT * I2F(last_realtime_delta))) + last_realtime_ticks - total_ticks; +#endif + ticks_per_second = F2I(MAX_MULT * I2F(USEC_PER_SECOND)); + } + if(ticks_delta > system_time_delta * USEC_PER_SECOND / MIN_USEC_PER_SECOND) { + //This keeps us from having too few instructions between ticks. + ticks_delta = system_time_delta * USEC_PER_SECOND / MIN_USEC_PER_SECOND; + } + if(ticks_delta > virtual_next_event_time) { + //This keeps us from missing ticks. + ticks_delta = virtual_next_event_time; + } + + if(ticks_delta) { + +# if DEBUG_REALTIME_WITH_PRINTF + //Every second print some info. + if(((last_real_time + real_time_delta) / USEC_PER_SECOND) > (last_real_time / USEC_PER_SECOND)) { + Bit64u temp1, temp2, temp3, temp4; + temp1 = (Bit64u) total_real_usec; + temp2 = (total_real_usec); + temp3 = (Bit64u)total_ticks; + temp4 = (Bit64u)((total_real_usec) - total_ticks); + printf("useconds: %llu, ",temp1); + printf("expect ticks: %llu, ",temp2); + printf("ticks: %llu, ",temp3); + printf("diff: %llu\n",temp4); + } +# endif + + last_real_time += real_time_delta; + total_real_usec += real_time_delta; + last_system_usec += system_time_delta; + stored_delta = 0; + total_ticks += ticks_delta; + } else { + stored_delta = system_time_delta; + } + + + Bit64u a,b; + a=(usec_per_second); + if(real_time_delta) { + //FIXME + Bit64u em_realtime_delta = last_system_usec + stored_delta - em_last_realtime; + b=((Bit64u)USEC_PER_SECOND * em_realtime_delta / real_time_delta); + em_last_realtime = last_system_usec + stored_delta; + } else { + b=a; + } + usec_per_second = ALPHA_LOWER(a,b); +#else + BX_ASSERT(0); +#endif +#if BX_HAVE_REALTIME_USEC + advance_virtual_time(ticks_delta); +#endif + } + + last_usec=last_usec + usec_delta; + bx_pc_system.deactivate_timer(system_timer_id); + BX_ASSERT(virtual_next_event_time); + bx_pc_system.activate_timer(system_timer_id, + (Bit32u)BX_MIN(0x7FFFFFFF,BX_MAX(1,TICKS_TO_USEC(virtual_next_event_time))), + 0); + +} + +void +bx_virt_timer_c::pc_system_timer_handler(void* this_ptr) { + ((bx_virt_timer_c *)this_ptr)->timer_handler(); +} + diff --git a/tools/ioemu/iodev/virt_timer.h b/tools/ioemu/iodev/virt_timer.h new file mode 100644 index 0000000000..a10b16cd0e --- /dev/null +++ b/tools/ioemu/iodev/virt_timer.h @@ -0,0 +1,131 @@ + +#ifndef _BX_VIRT_TIMER_H +#define _BX_VIRT_TIMER_H + + +#define BX_MAX_VIRTUAL_TIMERS (15+BX_SMP_PROCESSORS) +#define BX_NULL_VIRTUAL_TIMER_HANDLE 10000 + +#define BX_MAX_VIRTUAL_TIME (0x7fffffff) + +class bx_virt_timer_c : public logfunctions { + private: + + struct { + bx_bool inUse; // Timer slot is in-use (currently registered). + Bit64u period; // Timer periodocity in virtual useconds. + Bit64u timeToFire; // Time to fire next (in virtual useconds). + bx_bool active; // 0=inactive, 1=active. + bx_bool continuous; // 0=one-shot timer, 1=continuous periodicity. + bx_timer_handler_t funct; // A callback function for when the + // timer fires. + // This function MUST return. + void *this_ptr; // The this-> pointer for C++ callbacks + // has to be stored as well. + char id[BxMaxTimerIDLen]; // String ID of timer. + } timer[BX_MAX_VIRTUAL_TIMERS]; + + unsigned numTimers; // Number of currently allocated timers. + + //Variables for the timer subsystem: + Bit64u current_timers_time; + Bit64u timers_next_event_time; + + Bit64u last_sequential_time; + bx_bool in_timer_handler; + + //Variables for the time sync subsystem: + Bit64u virtual_next_event_time; + Bit64u current_virtual_time; + + //Real time variables: + Bit64u last_real_time; + Bit64u total_real_usec; + Bit64u last_realtime_delta; + //System time variables: + Bit64u last_usec; + Bit64u usec_per_second; + Bit64u stored_delta; + Bit64u last_system_usec; + Bit64u em_last_realtime; + //Virtual timer variables: + Bit64u total_ticks; + Bit64u last_realtime_ticks; + Bit64u ticks_per_second; + + bx_bool init_done; + + int system_timer_id; + + //Whether or not to use virtual timers. + bx_bool use_virtual_timers; + bx_bool virtual_timers_realtime; + + // A special null timer is always inserted in the timer[0] slot. This + // make sure that at least one timer is always active, and that the + // duration is always less than a maximum 32-bit integer, so a 32-bit + // counter can be used for the current countdown. + static const Bit64u NullTimerInterval; + static void nullTimer(void* this_ptr); + + + //Step the given number of cycles, optionally calling any timer handlers. + void periodic(Bit64u time_passed); + + + //Called when next_event_time changes. + void next_event_time_update(void); + + //Called to advance the virtual time. + // calls periodic as needed. + void advance_virtual_time(Bit64u time_passed); + + public: + + + //Get the current virtual time. + // This may return the same value on subsequent calls. + Bit64u time_usec(void); + + //Get the current virtual time. + // This will return a monotonically increasing value. + // MUST NOT be called from within a timer handler. + Bit64u time_usec_sequential(void); + + //Register a timer handler to go off after a given interval. + //Register a timer handler to go off with a periodic interval. + int register_timer( void *this_ptr, bx_timer_handler_t handler, + Bit32u useconds, + bx_bool continuous, bx_bool active, const char *id); + + //unregister a previously registered timer. + unsigned unregisterTimer(int timerID); + + void start_timers(void); + + //activate a deactivated but registered timer. + void activate_timer( unsigned timer_index, Bit32u useconds, + bx_bool continuous ); + + //deactivate (but don't unregister) a currently registered timer. + void deactivate_timer( unsigned timer_index ); + + + //Timer handler passed to pc_system + static void pc_system_timer_handler(void* this_ptr); + + //The real timer handler. + void timer_handler(); + + //Initialization + void init(void); + bx_virt_timer_c(void); + ~bx_virt_timer_c(void); + +}; + + + +extern bx_virt_timer_c bx_virt_timer; + +#endif // _BX_VIRT_TIMER_H |