aboutsummaryrefslogtreecommitdiffstats
path: root/tools/ioemu/iodev
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ioemu/iodev')
-rw-r--r--tools/ioemu/iodev/Makefile15
-rw-r--r--tools/ioemu/iodev/aspi-win32.h210
-rw-r--r--tools/ioemu/iodev/biosdev.cc212
-rw-r--r--tools/ioemu/iodev/biosdev.h63
-rw-r--r--tools/ioemu/iodev/cdrom.cc1338
-rw-r--r--tools/ioemu/iodev/cdrom.h67
-rw-r--r--tools/ioemu/iodev/cdrom_beos.h10
-rw-r--r--tools/ioemu/iodev/cmos.cc824
-rw-r--r--tools/ioemu/iodev/cmos.h90
-rw-r--r--tools/ioemu/iodev/cpu.cc334
-rw-r--r--tools/ioemu/iodev/crc32.cc49
-rw-r--r--tools/ioemu/iodev/crc32.h25
-rw-r--r--tools/ioemu/iodev/devices.cc685
-rw-r--r--tools/ioemu/iodev/dma.cc825
-rw-r--r--tools/ioemu/iodev/dma.h114
-rw-r--r--tools/ioemu/iodev/eth.cc194
-rw-r--r--tools/ioemu/iodev/eth.h76
-rw-r--r--tools/ioemu/iodev/eth_arpback.cc214
-rw-r--r--tools/ioemu/iodev/eth_fbsd.cc385
-rw-r--r--tools/ioemu/iodev/eth_linux.cc286
-rw-r--r--tools/ioemu/iodev/eth_null.cc164
-rw-r--r--tools/ioemu/iodev/eth_packetmaker.cc184
-rw-r--r--tools/ioemu/iodev/eth_packetmaker.h135
-rw-r--r--tools/ioemu/iodev/eth_tap.cc370
-rw-r--r--tools/ioemu/iodev/eth_tuntap.cc401
-rw-r--r--tools/ioemu/iodev/extfpuirq.cc107
-rw-r--r--tools/ioemu/iodev/extfpuirq.h51
-rw-r--r--tools/ioemu/iodev/floppy.cc1633
-rw-r--r--tools/ioemu/iodev/floppy.h138
-rw-r--r--tools/ioemu/iodev/gameport.cc242
-rw-r--r--tools/ioemu/iodev/gameport.h63
-rw-r--r--tools/ioemu/iodev/guest2host.h77
-rw-r--r--tools/ioemu/iodev/harddrv.cc4880
-rw-r--r--tools/ioemu/iodev/harddrv.h765
-rw-r--r--tools/ioemu/iodev/ioapic.cc175
-rw-r--r--tools/ioemu/iodev/ioapic.h54
-rw-r--r--tools/ioemu/iodev/iodebug.cc354
-rw-r--r--tools/ioemu/iodev/iodebug.h35
-rw-r--r--tools/ioemu/iodev/iodev.h422
-rw-r--r--tools/ioemu/iodev/keyboard.cc1611
-rw-r--r--tools/ioemu/iodev/keyboard.h234
-rw-r--r--tools/ioemu/iodev/load32bitOShack.cc322
-rw-r--r--tools/ioemu/iodev/logio.cc631
-rw-r--r--tools/ioemu/iodev/main.cc4067
-rw-r--r--tools/ioemu/iodev/ne2k.cc1608
-rw-r--r--tools/ioemu/iodev/ne2k.h239
-rw-r--r--tools/ioemu/iodev/osdep.cc340
-rw-r--r--tools/ioemu/iodev/parallel.cc300
-rw-r--r--tools/ioemu/iodev/parallel.h78
-rw-r--r--tools/ioemu/iodev/pc_system.cc570
-rw-r--r--tools/ioemu/iodev/pci.cc467
-rw-r--r--tools/ioemu/iodev/pci.h90
-rw-r--r--tools/ioemu/iodev/pci2isa.cc294
-rw-r--r--tools/ioemu/iodev/pci2isa.h63
-rw-r--r--tools/ioemu/iodev/pciusb.cc668
-rw-r--r--tools/ioemu/iodev/pciusb.h195
-rw-r--r--tools/ioemu/iodev/pcivga.cc248
-rw-r--r--tools/ioemu/iodev/pcivga.h48
-rw-r--r--tools/ioemu/iodev/pic.cc887
-rw-r--r--tools/ioemu/iodev/pic.h97
-rw-r--r--tools/ioemu/iodev/pit.cc856
-rw-r--r--tools/ioemu/iodev/pit.h103
-rw-r--r--tools/ioemu/iodev/pit82c54.cc930
-rw-r--r--tools/ioemu/iodev/pit82c54.h143
-rw-r--r--tools/ioemu/iodev/pit_wrap.cc444
-rw-r--r--tools/ioemu/iodev/pit_wrap.h104
-rw-r--r--tools/ioemu/iodev/plugin.cc554
-rw-r--r--tools/ioemu/iodev/scancodes.cc770
-rw-r--r--tools/ioemu/iodev/scancodes.h31
-rw-r--r--tools/ioemu/iodev/scsi_commands.h418
-rw-r--r--tools/ioemu/iodev/scsidefs.h286
-rw-r--r--tools/ioemu/iodev/scsipt.h144
-rw-r--r--tools/ioemu/iodev/serial.cc1001
-rw-r--r--tools/ioemu/iodev/serial.h193
-rw-r--r--tools/ioemu/iodev/serial_raw.h23
-rw-r--r--tools/ioemu/iodev/slowdown_timer.cc182
-rw-r--r--tools/ioemu/iodev/slowdown_timer.h33
-rw-r--r--tools/ioemu/iodev/soundlnx.cc227
-rw-r--r--tools/ioemu/iodev/soundlnx.h69
-rw-r--r--tools/ioemu/iodev/soundwin.cc521
-rw-r--r--tools/ioemu/iodev/soundwin.h229
-rw-r--r--tools/ioemu/iodev/state_file.cc136
-rw-r--r--tools/ioemu/iodev/unmapped.cc305
-rw-r--r--tools/ioemu/iodev/unmapped.h64
-rw-r--r--tools/ioemu/iodev/vga.cc3116
-rw-r--r--tools/ioemu/iodev/vga.h300
-rw-r--r--tools/ioemu/iodev/virt_timer.cc552
-rw-r--r--tools/ioemu/iodev/virt_timer.h131
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, &current_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, &params[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 (&params[i][5]);
+ bx_options.floppya.Otype->set (BX_FLOPPY_2_88);
+ }
+ else if (!strncmp(params[i], "1_44=", 5)) {
+ bx_options.floppya.Opath->set (&params[i][5]);
+ bx_options.floppya.Otype->set (BX_FLOPPY_1_44);
+ }
+ else if (!strncmp(params[i], "1_2=", 4)) {
+ bx_options.floppya.Opath->set (&params[i][4]);
+ bx_options.floppya.Otype->set (BX_FLOPPY_1_2);
+ }
+ else if (!strncmp(params[i], "720k=", 5)) {
+ bx_options.floppya.Opath->set (&params[i][5]);
+ bx_options.floppya.Otype->set (BX_FLOPPY_720K);
+ }
+ else if (!strncmp(params[i], "360k=", 5)) {
+ bx_options.floppya.Opath->set (&params[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 (&params[i][5]);
+ bx_options.floppya.Otype->set (BX_FLOPPY_160K);
+ }
+ else if (!strncmp(params[i], "180k=", 5)) {
+ bx_options.floppya.Opath->set (&params[i][5]);
+ bx_options.floppya.Otype->set (BX_FLOPPY_180K);
+ }
+ else if (!strncmp(params[i], "320k=", 5)) {
+ bx_options.floppya.Opath->set (&params[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 (&params[i][5]);
+ bx_options.floppyb.Otype->set (BX_FLOPPY_2_88);
+ }
+ else if (!strncmp(params[i], "1_44=", 5)) {
+ bx_options.floppyb.Opath->set (&params[i][5]);
+ bx_options.floppyb.Otype->set (BX_FLOPPY_1_44);
+ }
+ else if (!strncmp(params[i], "1_2=", 4)) {
+ bx_options.floppyb.Opath->set (&params[i][4]);
+ bx_options.floppyb.Otype->set (BX_FLOPPY_1_2);
+ }
+ else if (!strncmp(params[i], "720k=", 5)) {
+ bx_options.floppyb.Opath->set (&params[i][5]);
+ bx_options.floppyb.Otype->set (BX_FLOPPY_720K);
+ }
+ else if (!strncmp(params[i], "360k=", 5)) {
+ bx_options.floppyb.Opath->set (&params[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 (&params[i][5]);
+ bx_options.floppyb.Otype->set (BX_FLOPPY_160K);
+ }
+ else if (!strncmp(params[i], "180k=", 5)) {
+ bx_options.floppyb.Opath->set (&params[i][5]);
+ bx_options.floppyb.Otype->set (BX_FLOPPY_180K);
+ }
+ else if (!strncmp(params[i], "320k=", 5)) {
+ bx_options.floppyb.Opath->set (&params[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(&params[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 (&params[2][8], NULL, 16));
+ else
+ bx_options.ata[channel].Oioaddr1->set (strtoul (&params[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 (&params[3][8], NULL, 16));
+ else
+ bx_options.ata[channel].Oioaddr2->set (strtoul (&params[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(&params[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(&params[0][4], "-slave")) &&
+ (strcmp(&params[0][4], "-master"))) {
+ PARSE_ERR(("%s: ataX-master/slave directive malformed.", context));
+ }
+
+ if (!strcmp(&params[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 (&params[i][5]);
+ }
+ else if (!strncmp(params[i], "cylinders=", 10)) {
+ bx_options.atadevice[channel][slave].Ocylinders->set (atol(&params[i][10]));
+ }
+ else if (!strncmp(params[i], "heads=", 6)) {
+ bx_options.atadevice[channel][slave].Oheads->set (atol(&params[i][6]));
+ }
+ else if (!strncmp(params[i], "spt=", 4)) {
+ bx_options.atadevice[channel][slave].Ospt->set (atol(&params[i][4]));
+ }
+ else if (!strncmp(params[i], "model=", 6)) {
+ bx_options.atadevice[channel][slave].Omodel->set(&params[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(&params[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 (&params[1][5]);
+ bx_options.atadevice[0][0].Ocylinders->set (atol(&params[2][4]));
+ bx_options.atadevice[0][0].Oheads->set (atol(&params[3][6]));
+ bx_options.atadevice[0][0].Ospt->set (atol(&params[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 (&params[1][5]);
+ bx_options.atadevice[0][1].Ocylinders->set (atol( &params[2][4]));
+ bx_options.atadevice[0][1].Oheads->set (atol( &params[3][6]));
+ bx_options.atadevice[0][1].Ospt->set (atol( &params[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 (&params[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(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "dev=", 4)) {
+ bx_options.com[0].Odev->set (&params[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(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "dev=", 4)) {
+ bx_options.com[1].Odev->set (&params[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(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "dev=", 4)) {
+ bx_options.com[2].Odev->set (&params[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(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "dev=", 4)) {
+ bx_options.com[3].Odev->set (&params[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(&params[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 (&params[i][7], NULL, 16));
+ else
+ bx_options.usb[0].Oioaddr->set (strtoul (&params[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(&params[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 (&params[1][5]);
+ if ( (params[2][8] == '0') && (params[2][9] == 'x') )
+ bx_options.rom.Oaddress->set (strtoul (&params[2][8], NULL, 16));
+ else
+ bx_options.rom.Oaddress->set (strtoul (&params[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 (&params[1][5]);
+ if ( (params[2][8] == '0') && (params[2][9] == 'x') )
+ bx_options.optrom[0].Oaddress->set (strtoul (&params[2][8], NULL, 16));
+ else
+ bx_options.optrom[0].Oaddress->set (strtoul (&params[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 (&params[1][5]);
+ if ( (params[2][8] == '0') && (params[2][9] == 'x') )
+ bx_options.optrom[1].Oaddress->set (strtoul (&params[2][8], NULL, 16));
+ else
+ bx_options.optrom[1].Oaddress->set (strtoul (&params[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 (&params[1][5]);
+ if ( (params[2][8] == '0') && (params[2][9] == 'x') )
+ bx_options.optrom[2].Oaddress->set (strtoul (&params[2][8], NULL, 16));
+ else
+ bx_options.optrom[2].Oaddress->set (strtoul (&params[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 (&params[1][5]);
+ if ( (params[2][8] == '0') && (params[2][9] == 'x') )
+ bx_options.optrom[3].Oaddress->set (strtoul (&params[2][8], NULL, 16));
+ else
+ bx_options.optrom[3].Oaddress->set (strtoul (&params[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(&params[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(&params[i][5]));
+ }
+ else if (!strncmp(params[i], "midimode=", 9)) {
+ bx_options.sb16.Omidimode->set (atol(&params[i][9]));
+ }
+ else if (!strncmp(params[i], "wave=", 5)) {
+ bx_options.sb16.Owavefile->set (strdup(&params[i][5]));
+ }
+ else if (!strncmp(params[i], "wavemode=", 9)) {
+ bx_options.sb16.Owavemode->set (atol(&params[i][9]));
+ }
+ else if (!strncmp(params[i], "log=", 4)) {
+ bx_options.sb16.Ologfile->set (strdup(&params[i][4]));
+ }
+ else if (!strncmp(params[i], "loglevel=", 9)) {
+ bx_options.sb16.Ologlevel->set (atol(&params[i][9]));
+ }
+ else if (!strncmp(params[i], "dmatimer=", 9)) {
+ bx_options.sb16.Odmatimer->set (atol(&params[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(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "file=", 5)) {
+ bx_options.par[0].Ooutfile->set (strdup(&params[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(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "file=", 5)) {
+ bx_options.par[1].Ooutfile->set (strdup(&params[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 (&params[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(&params[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(&params[i][7], NULL, 16));
+ valid |= 0x01;
+ }
+ else if (!strncmp(params[i], "irq=", 4)) {
+ bx_options.ne2k.Oirq->set (atol(&params[i][4]));
+ valid |= 0x02;
+ }
+ else if (!strncmp(params[i], "mac=", 4)) {
+ n = sscanf(&params[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(&params[i][7])))
+ PARSE_ERR(("%s: ethernet module '%s' not available", context, strdup(&params[i][7])));
+ }
+ else if (!strncmp(params[i], "ethdev=", 7)) {
+ bx_options.ne2k.Oethdev->set (strdup(&params[i][7]));
+ }
+ else if (!strncmp(params[i], "script=", 7)) {
+ bx_options.ne2k.Oscript->set (strdup(&params[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(&params[1][3], "nullkernel")) {
+ bx_options.load32bitOSImage.OwhichOS->set (Load32bitOSNullKernel);
+ }
+ else if (!strcmp(&params[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(&params[2][5]));
+ bx_options.load32bitOSImage.Oiolog->set (strdup(&params[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(&params[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(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "map=", 4)) {
+ bx_options.keyboard.Okeymap->set (strdup(&params[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(&params[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 *)(&param));
+ 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 *)(&param));
+ 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