/************************************************************************\
 * File Version Information
 * $Header: /Altair32v2/taperdr.c 17    11/08/06 9:21p Racini $
 ************************************************************************/
/************************************************************************\
  MITS Altair Emulator
  PTP Device
 
  Copyright (c) 2001-2006 Richard A. Cini


  This module represents the secondary serial port of a typical two-port
  system configuration. The virtual paper tape device can connect to the
  emulator space through a file image, TCP/IP, or through a physical COM
  port on the host machine. This may change in the future when the serial
  ports on the Altair32 Front Panel are enabled. If the user console
  (2SIO-1) is using either the TCP/IP or COMx connection, that connection
  will not be available for use with a paper tape punch/reader.

  The layout of the port configuration is as follows. Only the second port is
  shown here; port 1 of the 2SIO card is handled in console.c.

   	   12/13 2SIO-2 -- CP/M RDR/PUN device
	   12		W		Configuration register (not meaningful here)
	   12		R		Status register
       13		W		Write PUN data
       13		R		Read RDR data

  These registers call through to function pointers which are set in syscfg.c
  to the servicing routine in tcp_io.c or ser_io.c as appropriate.


Change Log:  
  2001/10/01  RAC -- Initial coding.
  2001/10/15  RAC -- Added file buffering to speed-up reads
  2001/12/14  RAC -- RELEASE MARKER -- v2.1
  2002/01/31  RAC -- RELEASE MARKER -- v2.2
  2002/07/07  RAC -- RELEASE MARKER -- v2.3
  2002/08/23  RAC -- RELEASE MARKER -- v2.30.10
  2002/10/16  RAC -- Merged header file into sio.h or altair32.h
  2002/10/23  RAC -- Changed fopen mode to "rb" from "r"
  2002/11/15  RAC -- RELEASE MARKER -- v2.40.2100
  2003/04/26  RAC -- RELEASE MARKER -- v2.50.2045
  2004/05/15  RAC -- Changes to support only PTP under CP/M
  2004/07/30  RAC -- RELEASE MARKER -- v3.00.0135
  2004/11/21  FJS -- Added message crackers
  2006/05/12  RAC -- RELEASE MARKER -- v3.10.0200
  2006/11/15  RAC -- RELEASE MARKER -- v3.20.0400
\************************************************************************/
#include <windows.h>
#include <windowsx.h>
#include <commdlg.h>
#include <commctrl.h>
#include <stdio.h>			// C-lib - I/O 
#include <string.h>
#include "altair32.h"		// includes "resource.h"
//#include "extensions.h" 	// corrections for bad functions in windowsx
#include "comcthlp.h"		// common control macro file


/**  Typefefs  ************************************************/
// mode definitions
#define PTP_READ   0	// READ mode
#define PTP_WRITE  1	// WRITE mode

// Port status
#define RxStat_BIT	0x01	// bit0=RxReady (1=DAV)
#define TxStat_BIT 	0x02	// bit1=TxReady (1=OK to write)


/**  Forward Prototypes  **************************************/
// static	BOOL  StartupPTP( HWND, BOOL );
static void PTP_Write( UCHAR data );
// static LRESULT CALLBACK WndProcPTP( HWND, UINT, WPARAM, LPARAM );


/**  Module Globals - Private  ********************************/
static char	szFile[_MAX_PATH];
static char	szTitle[_MAX_FNAME+_MAX_EXT];
static char szFileDir[_MAX_PATH + _MAX_FNAME + _MAX_EXT];
static FILE	*pFileIO = NULL;
static int  iFileLength = 0;
static int  iPTPStatus = 0xff;


/**  Module Globals - Public  *********************************/
HWND hwndPTP = NULL;				// PTP window handle


/**  Private Routines  ****************************************/

/*--  StartupPTP  ---------------------------------------------
	Called by dialog UI code to get name of tape image and open
	file.  Returns error status.
	
-------------------------------------------------------------*/
static BOOL StartupPTP(HWND hwnd, BOOL bDir)
{

	int iCurrentPos;
	OPENFILENAME ptp_ofn;
	static	char szFilter[] =
		"Paper Tape Files (.TAP;.PTP)\0*.tap;*.ptp\0" \
		"All Files (*.*)\0*.*\0" \
		"\0";


	// On each pass through, make sure that any existing files are closed.
	// This allows for automatically switching tapes from the dialog. 
	PTP_Shutdown();
	strcpy(szFileDir, winstate.szFilesDir);

	// setup for open
	ptp_ofn.lStructSize       = sizeof(OPENFILENAME);
	ptp_ofn.hwndOwner         = hwnd;
	ptp_ofn.hInstance         = (HINSTANCE)NULL;
	ptp_ofn.lpstrFilter       = szFilter;
	ptp_ofn.lpstrCustomFilter = NULL;
	ptp_ofn.nMaxCustFilter    = 0;
	ptp_ofn.nFilterIndex      = 0;
	ptp_ofn.lpstrFile         = szFile;	// fully-qualified path and filename
	ptp_ofn.nMaxFile          = _MAX_PATH;
	ptp_ofn.lpstrFileTitle    = szTitle;	// name and extension only
	ptp_ofn.nMaxFileTitle     = _MAX_FNAME + _MAX_EXT;
	ptp_ofn.lpstrInitialDir   = szFileDir;	//NULL;
	//ptp_ofn.lpstrTitle        = NULL;	// dialog box title - BELOW
	ptp_ofn.Flags             = OFN_HIDEREADONLY | OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT;
	ptp_ofn.nFileOffset       = 0;
	ptp_ofn.nFileExtension    = 0;	
	ptp_ofn.lpstrDefExt       = "tap";
	ptp_ofn.lCustData         = 0L;
	ptp_ofn.lpfnHook          = NULL;
	ptp_ofn.lpTemplateName    = NULL;

	if (bDir == PTP_WRITE) {		// WRITE
		ptp_ofn.lpstrTitle = "Write Paper Tape File";
		if (!GetSaveFileName(&ptp_ofn)) return FALSE;

		if (NULL == (pFileIO = fopen(szFile, "a+b"))){
			Status_SetText(hwndStatusBar, STATBAR_READY, 0, "Error opening PTP file.");
			return FALSE;
		}

		Status_SetText(hwndStatusBar, STATBAR_READY, 0, "Successfully opened PTP file for writing.");
		Status_SetText(hwndStatusBar, STATBAR_CASS, 0, "PTP Tx");
		iPTPStatus |= TxStat_BIT;		// turn on write OK
	} else {
		ptp_ofn.lpstrTitle = "Open Paper Tape File";
		if (!GetOpenFileName(&ptp_ofn)) return FALSE;	// cancel or error

		if (NULL == (pFileIO = fopen(szFile, "rb"))){
			Status_SetText(hwndStatusBar, STATBAR_READY, 0, "Error opening PTP file.");
			return FALSE;
		}

		Status_SetText(hwndStatusBar, STATBAR_READY, 0, "Successfully opened PTP file for reading.");
		Status_SetText(hwndStatusBar, STATBAR_CASS, 0, "PTP Rx");

		// get the file length 
		iCurrentPos = ftell(pFileIO);
		fseek(pFileIO, 0, SEEK_END);
		iFileLength = ftell(pFileIO);
		fseek(pFileIO, iCurrentPos, SEEK_SET);
		iPTPStatus |= RxStat_BIT;		// turn on read OK
	}

//	iPTPStatus = SIO_RDR;
	SetDlgItemText(hwnd, IDC_EDIT_TAPE, szTitle);	// show short name only
	return TRUE;
}


/*--  PTP_Stat  -----------------------------------------------
	PTP device status register for I/O handler.  Returns three
	values:_WDB (busy), _RDE2 (no input but can write) or RDE3
	(input and can write) for use with CP/M
	
-------------------------------------------------------------*/
static int PTP_Stat( void )
{
	return iPTPStatus;
}


/*--  PTP_Read  -----------------------------------------------
	Read character from file stream. Used by I/O handler.
	Sets status variable to be returned by PTP_Stat. Will
	return character read or 0xff on error (on 8080, reading
	from unconnected port address returns 0xff).
	
-------------------------------------------------------------*/
static int PTP_Read( void )
{

	static int eof_flag;			// preserve between calls
	static byte bufchar = 0xff;	// character read from stream
	byte buftemp = 0xff;			// throw-away char
	fpos_t	temp_pos = 0;


	if (pFileIO == NULL) return 0xff;		// stream closed; exit

	if (!eof_flag){							// not EOF yet
		fread(&bufchar, 1, 1, pFileIO);	// get char. this one we return to caller
		fgetpos(pFileIO, &temp_pos);		// to remember where to go

		fread(&buftemp, 1, 1, pFileIO);	// get next char; throw away
		if (feof(pFileIO)){					// if error after 2d read
			Status_SetText(hwndStatusBar, STATBAR_READY, 0, "EOF error on read from file stream.");
			iPTPStatus &= ~RxStat_BIT;	// no Rx data available (0=no data)
			eof_flag = 1;
		}
		else {
			iPTPStatus |= RxStat_BIT;		// signal Rx data available
			eof_flag = 0;
		}
		fsetpos(pFileIO, &temp_pos);		// go back to where we were
		if (hwndPTP){						// do only if window is open
			SetDlgItemInt(hwndPTP, IDC_EDIT_PTR, (int) temp_pos, FALSE);
			// maybe we should blink some status bar indicator if the
			// window is closed???
		}
		return bufchar;
	}
	// else									// already EOF
	return bufchar;						// 0 after EOF
}



/*--  PTP_Write  ----------------------------------------------
	Writes character to file stream. Used by I/O handler.
	Sets status variable to be returned by PTP_Stat. 
	
-------------------------------------------------------------*/
static void PTP_Write( UCHAR data )
{

	fpos_t	temp_pos = 0;

	
	if (pFileIO == NULL) return;		// stream closed; exit

	if (fputc(data, pFileIO) != EOF){	// write character to stream
		iPTPStatus |= TxStat_BIT;		// TxStat=OK
		if (hwndPTP){					// do only if window is open
			fgetpos(pFileIO, &temp_pos);
			SetDlgItemInt(hwndPTP, IDC_EDIT_PTR, (int)temp_pos, FALSE);
			// maybe we should blink some status bar indicator if the
			// window is closed???
		}
		return;
	}

	iPTPStatus &= ~TxStat_BIT;		// assume TxStat=busy
	Status_SetText(hwndStatusBar, STATBAR_READY, 0, "Write error on PTP device.");
	return;
}


/**************************************************************\
*   Windows Callback Routines  ********************************|
\**************************************************************/
static LRESULT CALLBACK WndProcPTP(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	char *ProfBuf = NULL;
	char buf2[80];
	fpos_t temp_pos = 0;
	int x_size, y_size = 0;
	RECT rc;
	int sio_desired; // FJS


    switch (iMsg) {
		case WM_INITDIALOG:
			// Move window into position -
			GetWindowRect(hDlg, &rc);	// per parent window?
			x_size = (int)(rc.right - rc.left);	// base size x
			y_size = (int)(rc.bottom - rc.top);	// base size y
			ProfBuf = SYSCFG_ReadConfigInfo("windows", "taperdr");
		    if (sscanf(ProfBuf, "%d,%d", &rc.left, &rc.top) == 2) {
				MoveWindow(hDlg, rc.left, rc.top, x_size, y_size , TRUE);
		    }

			// FJS show or hide TCP/COMx radio buttons -
			EnableWindow(GetDlgItem(hDlg, IDC_RADIO_TCP),  simstate.iConsoleType != 0);
			EnableWindow(GetDlgItem(hDlg, IDC_RADIO_COMx), simstate.iConsoleType != 2);

			if (pFileIO != NULL) { // restore file already in use -
				EnableWindow(GetDlgItem(hDlg, IDB_LOAD), FALSE);
				EnableWindow(GetDlgItem(hDlg, IDB_SAVE), FALSE);
				EnableWindow(GetDlgItem(hDlg, IDB_REW_TAPE), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDB_EJECT), TRUE);
				fgetpos(pFileIO, &temp_pos);
				SetDlgItemInt(hDlg, IDC_EDIT_PTR, (int) temp_pos, FALSE);
				SetDlgItemText(hDlg, IDC_EDIT_TAPE, szTitle);	// show short name only

				simstate.iSioType = 0; // FJS for safety
				CheckRadioButton(hDlg, IDC_RADIO_TCP, IDC_RADIO_COMx, 0); // FJS
				SetFocus(GetDlgItem (hDlg, IDB_EJECT));
			} 
			else {		// no file pointer open so enable attach buttons -
				EnableWindow(GetDlgItem(hDlg, IDB_LOAD), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDB_SAVE), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDB_REW_TAPE), FALSE);

				// FJS safety checks -
				if ((simstate.iSioType == 1) && (simstate.iConsoleType == 0))
					simstate.iSioType = 0 /* NO */;
				if ((simstate.iSioType == 2) && (simstate.iConsoleType == 2))
					simstate.iSioType = 0 /* NO */;

				if (simstate.iSioType /* != NO */) { // FJS Sio elected
					// check or uncheck TCP or COMx button(s) -
					CheckRadioButton(hDlg, IDC_RADIO_TCP, IDC_RADIO_COMx,
						(simstate.iSioType == 1) ? IDC_RADIO_TCP : IDC_RADIO_COMx);
					EnableWindow(GetDlgItem(hDlg, IDB_EJECT), TRUE);
					SetFocus(GetDlgItem (hDlg, IDB_EJECT));
				}
				else { // FJS show no Sio elected (& no file)
					CheckRadioButton(hDlg, IDC_RADIO_TCP, IDC_RADIO_COMx, 0);
					EnableWindow(GetDlgItem(hDlg, IDB_EJECT), FALSE);
					SetFocus(GetDlgItem (hDlg, IDB_LOAD));
				}
			}
		return FALSE;				// don't let Windows set default focus

		case WM_COMMAND:
	    	switch (LOWORD(wParam)) {
				case IDC_RADIO_TCP:		// FJS connect TCP/IP for read/write
					sio_desired = 1;	// fall through to COMx
				case IDC_RADIO_COMx:	// FJS COMx port
					if (LOWORD(wParam) == IDC_RADIO_COMx) sio_desired = 2; 
					if (simstate.iSioType != sio_desired) { // desired Sio not currently elected
						if (simstate.iConsoleType == ((sio_desired == 1) ? 0 : 2)) {
							// console has TCP/COMx; cannot elect -

							// complain that console has TCP or COMx -
							HOST_ThrowErrMsg("Console already using connection!");

							// restore TCP or (and) COMx button(s)
							CheckRadioButton(hDlg, IDC_RADIO_TCP, IDC_RADIO_COMx,
								(simstate.iSioType == 0) ? 0 :
								(simstate.iSioType == 1) ? IDC_RADIO_TCP : IDC_RADIO_COMx);
							// no net change performed!
						}
						else { // warn and detach, then elect
							// Query user to confirm any detachment -
							*buf2 = 0 /* EOS */; // assume no detachment required
							if (pFileIO != NULL)
								wsprintf(buf2, "Do you really want to detach this tape file from the device?");
							else if (simstate.Siostati_func /* != NULL */) {
								if (simstate.iSioType == 1) // TCP 
									wsprintf(buf2, "Do you really want to detach this TCP/IP connection?");
								else // COMx
									wsprintf(buf2, "Do you really want to detach this COMx connection?");
							}
							if (*buf2 /* != EOS */) { // possible detachment
								if (MessageBox(hDlg, buf2, winstate.szAppName,
										MB_YESNO | MB_SYSTEMMODAL |	MB_DEFBUTTON2 | MB_ICONWARNING)
										== IDNO) {
									// FJS restore TCP and COMx buttons
									CheckRadioButton(hDlg, IDC_RADIO_TCP, IDC_RADIO_COMx,
										pFileIO ? 0 :
										(simstate.iSioType == 1) ? IDC_RADIO_TCP : IDC_RADIO_COMx);
									break; // abandon detachment and election
								}
							}
							if (simstate.Siostati_func /* != NULL */) // FJS
								PTP_Shutdown();
							if (pFileIO /* != NULL */) {
								PTP_Shutdown();
								SetDlgItemInt(hDlg, IDC_EDIT_PTR, 0, FALSE);
								SetDlgItemText(hDlg, IDC_EDIT_TAPE, "");	// delete name
							}
							pFileIO = NULL;

							// detach done, now elect -
							if ((simstate.iSioType = sio_desired) == 1) { // TCP elect
								simstate.Sioread_func  = TELNET_TcpRead;
								simstate.Siowrite_func = TELNET_TcpWrite;
								simstate.Siostato_func = TELNET_TcpOutStatus;
								simstate.Sioshtdn_func = TELNET_TcpShutdown;
							}
							else { // COMx elect
								simstate.Sioread_func  = SERIAL_SerRead;
								simstate.Siowrite_func = SERIAL_SerWrite;
								simstate.Siostato_func = SERIAL_SerOutStatus;
								simstate.Sioshtdn_func = SERIAL_SerShutdown;
							}

							EnableWindow(GetDlgItem(hDlg, IDB_REW_TAPE), FALSE);
							EnableWindow(GetDlgItem(hDlg, IDB_EJECT), TRUE);
							EnableWindow(GetDlgItem(hDlg, IDB_LOAD), TRUE);
							EnableWindow(GetDlgItem(hDlg, IDB_SAVE), TRUE);
							SetFocus(GetDlgItem (hDlg, IDB_EJECT));
						}
					} else { // FJS toggle TCP or COMx from elected to un-elected
						if (simstate.Siostati_func /* != NULL; Sio active */) {
							// Query user to confirm detachment -
							if (simstate.iSioType == 1) // TCP
								wsprintf(buf2, "Do you really want to detach this TCP/IP connection?");
							else // COMx
								wsprintf(buf2, "Do you really want to detach this COMx connection?");
							if (MessageBox(hDlg, buf2, winstate.szAppName,
									MB_YESNO | MB_SYSTEMMODAL |	MB_DEFBUTTON2 | MB_ICONWARNING)
									== IDNO) {
								// FJS restore TCP and COMx buttons
								CheckRadioButton(hDlg, IDC_RADIO_TCP, IDC_RADIO_COMx,
									((simstate.iSioType == 1) && (simstate.iConsoleType!= 0))
									? IDC_RADIO_TCP :
									(simstate.iConsoleType!= 2) ? IDC_RADIO_COMx : 0);
								break; // abandon detachment
							}

							// kill TCP or COMx connection -
							simstate.Sioshtdn_func();
							simstate.Siostati_func = NULL;
						}

						// uncheck TCP or (and) COMx button(s)
						CheckRadioButton(hDlg, IDC_RADIO_TCP, IDC_RADIO_COMx, 0);
						simstate.iSioType = 0; // file default
						EnableWindow(GetDlgItem(hDlg, IDB_EJECT), FALSE);
						EnableWindow(GetDlgItem(hDlg, IDB_REW_TAPE), FALSE);
						EnableWindow(GetDlgItem(hDlg, IDB_LOAD), TRUE);
						EnableWindow(GetDlgItem(hDlg, IDB_SAVE), TRUE);
						SetFocus(GetDlgItem (hDlg, IDB_LOAD));
					}
				break;

				case IDB_LOAD:		// connect tape image for reading
					if (simstate.Siostati_func /* != NULL */) { // FJS Sio active
						if (simstate.iSioType == 1) // TCP
							wsprintf(buf2, "Do you really want to detach this TCP/IP connection?");
						else // COMx
							wsprintf(buf2, "Do you really want to detach this COMx connection?");
						if (MessageBox(hDlg, buf2, winstate.szAppName,
								MB_YESNO | MB_SYSTEMMODAL |	MB_DEFBUTTON2 | MB_ICONWARNING)
								== IDNO) {
							SetFocus(GetDlgItem (hDlg, IDB_EJECT));
							break; // abandon detachment
						}

						// kill TCP or COMx connection -
						simstate.Sioshtdn_func();
						simstate.Siostati_func = NULL;
						simstate.iSioType = 0; // FJS file default
						// FJS uncheck TCP or (and) COMx button(s)
						CheckRadioButton(hDlg, IDC_RADIO_TCP, IDC_RADIO_COMx, 0);
					} // detach done (or un-needed)
					if (StartupPTP(hDlg, PTP_READ)) {
						simstate.iSioType = 0; // FJS file default
						// FJS uncheck TCP or (and) COMx button(s)
						CheckRadioButton(hDlg, IDC_RADIO_TCP, IDC_RADIO_COMx, 0);
						EnableWindow(GetDlgItem(hDlg, IDB_REW_TAPE), TRUE);
						EnableWindow(GetDlgItem(hDlg, IDB_EJECT), TRUE);
						EnableWindow(GetDlgItem(hDlg, IDB_LOAD), FALSE);
						EnableWindow(GetDlgItem(hDlg, IDB_SAVE), FALSE);
						SetFocus(GetDlgItem(hDlg, IDB_EJECT));
					}
				break;

				case IDB_SAVE:		// connect tape image for writing
					if (simstate.Siostati_func /* != NULL */) { // FJS Sio active
						if (simstate.iSioType == 1) // TCP
							wsprintf(buf2, "Do you really want to detach this TCP/IP connection?");
						else // COMx
							wsprintf(buf2, "Do you really want to detach this COMx connection?");
						if (MessageBox(hDlg, buf2, winstate.szAppName,
								MB_YESNO | MB_SYSTEMMODAL |	MB_DEFBUTTON2 | MB_ICONWARNING)
								== IDNO) {
							SetFocus(GetDlgItem (hDlg, IDB_EJECT));
							break; // abandon detachment
						}

						// kill TCP or COMx connection -
						simstate.Sioshtdn_func();
						simstate.Siostati_func = NULL;
						simstate.iSioType = 0; // FJS file default
						// FJS uncheck TCP or (and) COMx button(s)
						CheckRadioButton(hDlg, IDC_RADIO_TCP, IDC_RADIO_COMx, 0);
					}
					if (StartupPTP(hDlg, PTP_WRITE)) {
						simstate.iSioType = 0; // FJS file
						// FJS uncheck TCP or (and) COMx button(s)
						CheckRadioButton(hDlg, IDC_RADIO_TCP, IDC_RADIO_COMx, 0);

						EnableWindow(GetDlgItem(hDlg, IDB_REW_TAPE), TRUE);
						EnableWindow(GetDlgItem(hDlg, IDB_EJECT), TRUE);
						EnableWindow(GetDlgItem(hDlg, IDB_LOAD), FALSE);
						EnableWindow(GetDlgItem(hDlg, IDB_SAVE), FALSE);
						SetFocus(GetDlgItem (hDlg, IDB_EJECT));
					}
				break;

				case IDB_REW_TAPE:	// rewind tape to start
					if (pFileIO /*!= NULL*/) {
						rewind(pFileIO);
						fgetpos(pFileIO, &temp_pos);
						SetDlgItemInt(hDlg, IDC_EDIT_PTR, (int) temp_pos, FALSE);
					}
				break;

				case IDB_EJECT:
					// Query user to confirm detachment, as needed -
					*buf2 = 0 /* EOS */; // FJS assume no detach needed
					if (pFileIO /*!= NULL*/) {
						wsprintf(buf2, "Do you really want to detach this tape file from the device?");
					} else if (simstate.Siostati_func /* != NULL; TCP active */) {
						if (simstate.iSioType == 1) // only TCP for now (no Serial)
							wsprintf(buf2, "Do you really want to detach this TCP/IP connection?");
						else // COMx
							wsprintf(buf2, "Do you really want to detach this COMx connection?");
					}
					if (*buf2 && MessageBox (hDlg, buf2, winstate.szAppName,
							MB_YESNO | MB_SYSTEMMODAL |	MB_DEFBUTTON2 | MB_ICONWARNING)
							== IDNO) {
						SetFocus(GetDlgItem (hDlg, IDB_EJECT));
						break; // abandon detachment
					}

					if (simstate.Siostati_func /* != NULL*/) // FJS
						PTP_Shutdown();
					if (pFileIO /* != NULL */) {
						PTP_Shutdown();
						SetDlgItemInt(hDlg, IDC_EDIT_PTR, 0, FALSE);
						SetDlgItemText(hDlg, IDC_EDIT_TAPE, "");	// delete name
					}
					// FJS uncheck TCP or (and) COMx button(s)
					CheckRadioButton(hDlg, IDC_RADIO_TCP, IDC_RADIO_COMx, 0);
					simstate.iSioType = 0; // FJS file default

					pFileIO = NULL; // FJS
					EnableWindow(GetDlgItem(hDlg, IDB_REW_TAPE), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDB_EJECT), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDB_LOAD), TRUE);
					EnableWindow(GetDlgItem(hDlg, IDB_SAVE), TRUE);
					SetFocus(GetDlgItem (hDlg, IDB_LOAD));
				break;

				case IDCANCEL:	// leave connection(s) running w/o window
					GetWindowRect(hDlg, &rc);
				    sprintf(buf2, "%d,%d", rc.left, rc.top);
				    SYSCFG_WriteConfigInfo("windows", "taperdr", buf2);

					// leave file/stream open, but destroy window
					DestroyWindow(hDlg);
					hwndPTP = NULL;
					SetForegroundWindow(winstate.hWnd);	// main window
				return TRUE;

				// default: break;
			}	// end of switch (wParam)
		return TRUE;

		// default: break;
	}	// message case
	return FALSE;
}


/**  Public Routines  *****************************************/
/**************************************************************\
*   I/O Instruction Handlers  *********************************|
***************************************************************|
*
*	I/O instruction handlers are called from the CPU module
*  	when an IN or OUT instruction is issued.
*
*  	Each function is passed an 'io' flag, where 0 means a read
*  	from the port, and 1 means a write to the port.  On input,
*	the actual input is passed as the return value.  On output,
*	'data' is written to the device.
*
*	Note: Sio connections are not actually made until I/O or
*	a status check is atempted.
\**************************************************************/
/*--  SIO12h  -------------------------------------------------
	Control control register (on write) and status register
	(on read) for the secondary serial port on the 2SIO board.
	io=0=read, 1=write.  This handler connects to the CP/M PUN
	device (an output-only device) and the RDR device (an
	input-only device), or the Sio channel(s) (R/W).

	TBD: xlate control reg. writes to Sio control actions
	(serial port break signal, etc.)

-------------------------------------------------------------*/
int sio12h(int io, int data)
{

	// FJS switch on simulator serial port config -
	switch (simstate.iSioType) {
		case 0:				// file I/O
			if (!io) return PTP_Stat(); // READ

			break;		// write to status register unsupported
			
		case 1:				// TCP/IP I/O
			if (simstate.Siostati_func == NULL)
				simstate.Siostati_func = TELNET_TcpInStatus;
			if (!io) return simstate.Siostati_func(); // READ status

			break;		// write to status register unsupported
			
		default:			// COMx I/O
			if (simstate.Siostati_func == NULL)
				simstate.Siostati_func = SERIAL_SerInStatus;
			if (!io) return simstate.Siostati_func(); // READ status

			// TBD hardware handshake for COMx port
		break;		// write to status register unsupported
	}
	return 0;		// should be ignored by caller
}


/*--  SIO13h  -------------------------------------------------
	Data register for the secondary serial port on the 2SIO
	board. io=0=read, 1=write.  This handler connects to the
	CP/M PUN device (an output-only device) and RDR device (an
	input-only device), or the Sio channel(s) (R/W).

-------------------------------------------------------------*/
int sio13h(int io, int data)
{

	// FJS switch on simulator serial port config -
	switch (simstate.iSioType) {
		case 0:				// file I/O
			if (!io) return PTP_Read(); // READ file

			PTP_Write((byte) data);	// else WRITE file
			return 0; // done; skip Sio I/O
			
		case 1:				// TCP/IP I/O
			if (simstate.Siostati_func == NULL)
				simstate.Siostati_func = TELNET_TcpInStatus;
			break;
			
		default:			// COMx I/O
			if (simstate.Siostati_func == NULL)
				simstate.Siostati_func = SERIAL_SerInStatus;
		// break;
	}
	if (!io) return simstate.Sioread_func(); // READ

	simstate.Siowrite_func((byte) data);	// else WRITE
	return 0;
}


/*--  PTP_OpenWin  --------------------------------------------
	Called by main UI code to open up a	modeless dialog for
	the PTP device.
	
-------------------------------------------------------------*/
void PTP_OpenWin ( void )
{

	// CreateDialog returns NULL on failure
	if (!IsWindow(hwndPTP)) {
		hwndPTP = CreateDialog(winstate.hInst,
								"Tape_Drv",
								winstate.hWnd,
								(DLGPROC) WndProcPTP);
		ShowWindow(hwndPTP, SW_SHOW);
	}
}


/*--  PTP_Shutdown  -------------------------------------------
	Called by StartupPTP, emulator shutdown	code to make sure
	that any opened tape image files are closed before opening
	another image, closing the control dialog or exiting the
	emulator.
	
-------------------------------------------------------------*/
void PTP_Shutdown (void)
{
	int disconnect; // FJS

	disconnect = 0 /* NO */;	//NULLCHG
	if (simstate.Siostati_func != NULL) { // FJS shutdown Sio, if needed
		simstate.Sioshtdn_func();
		simstate.Siostati_func = NULL;
		disconnect = 1 /* YES */;
	}

	// switch on simulator serial port config -
	switch (simstate.iSioType) { // FJS
		default :	// COMx
			if (!disconnect) return;
			Status_SetText(hwndStatusBar, STATBAR_READY, 0, "Closed PTP COMx stream.");
		break;

		case 1:		// TCP/IP I/O
			if (disconnect) return;
			Status_SetText(hwndStatusBar, STATBAR_READY, 0, "Closed PTP TCP stream.");
		break;

		case 0 : // file I/O
			if (pFileIO == NULL) return;	// file closed-nothing to do

			fclose (pFileIO);
			Status_SetText(hwndStatusBar, STATBAR_READY, 0, "Successfully closed PTP file.");
		// break;

	}
	Status_SetText(hwndStatusBar, STATBAR_CASS, 0, "PTP Off"); // FJS
	iPTPStatus = 0;
	return;
}

/*  end of file: taperdr.c  */
