390c734a61
Signed-off-by: Gwenhael Le Moine <gwenhael.le.moine@gmail.com>
585 lines
No EOL
16 KiB
C
585 lines
No EOL
16 KiB
C
/*
|
|
* Emu48Dll.c
|
|
*
|
|
* This file is part of Emu48
|
|
*
|
|
* Copyright (C) 2000 Christoph Gießelink
|
|
*
|
|
*/
|
|
|
|
#include "pch.h"
|
|
#include "resource.h"
|
|
#include "Emu48.h"
|
|
#include "io.h"
|
|
#include "kml.h"
|
|
#include "debugger.h"
|
|
|
|
#include "Emu48Dll.h"
|
|
|
|
static LPCTSTR pArgv[3]; // command line memory
|
|
static HACCEL hAccel; // accelerator table
|
|
|
|
static HSZ hszService, hszTopic; // variables for DDE server
|
|
|
|
extern LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
|
|
|
|
// callback function notify document filename
|
|
VOID (CALLBACK *pEmuDocumentNotify)(LPCTSTR lpszFilename) = NULL;
|
|
|
|
// callback function notify Emu48 closed
|
|
static VOID (CALLBACK *pEmuClose)(VOID) = NULL;
|
|
|
|
//################
|
|
//#
|
|
//# Public internal functions
|
|
//#
|
|
//################
|
|
|
|
//
|
|
// DLLCreateWnd
|
|
//
|
|
BOOL DLLCreateWnd(LPCTSTR lpszFilename, LPCTSTR lpszPort2Name)
|
|
{
|
|
WNDCLASS wc;
|
|
RECT rectWindow;
|
|
|
|
BOOL bFileExist = FALSE; // state file don't exist
|
|
|
|
hApp = GetModuleHandle(_T("EMU48.DLL"));
|
|
if (hApp == NULL) return TRUE;
|
|
|
|
hHeap = GetProcessHeap(); // get process heap
|
|
if (hHeap == NULL) return TRUE;
|
|
|
|
nArgc = 1; // no argument
|
|
if (lpszFilename[0])
|
|
{
|
|
// try to open given filename
|
|
HANDLE hFile = CreateFile(lpszFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
|
|
if (bFileExist = (hFile != INVALID_HANDLE_VALUE))
|
|
CloseHandle(hFile);
|
|
|
|
ppArgv = pArgv; // command line arguments
|
|
nArgc = 2; // one argument: state file, no port2 file
|
|
pArgv[1] = lpszFilename; // name of state file
|
|
|
|
if (lpszPort2Name) // port2 filename
|
|
{
|
|
nArgc = 3; // two arguments: state file, port2 file
|
|
pArgv[2] = lpszPort2Name; // name of port2 file
|
|
}
|
|
}
|
|
|
|
wc.style = CS_BYTEALIGNCLIENT;
|
|
wc.lpfnWndProc = (WNDPROC)MainWndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = hApp;
|
|
wc.hIcon = LoadIcon(hApp, MAKEINTRESOURCE(IDI_EMU48));
|
|
wc.hCursor = NULL;
|
|
wc.hbrBackground = NULL;
|
|
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
|
|
wc.lpszClassName = _T("CEmu48");
|
|
RegisterClass(&wc);
|
|
|
|
// Create window
|
|
rectWindow.left = 0;
|
|
rectWindow.top = 0;
|
|
rectWindow.right = 256;
|
|
rectWindow.bottom = 0;
|
|
AdjustWindowRect(&rectWindow, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, TRUE);
|
|
|
|
hWnd = CreateWindow(_T("CEmu48"), _T("Emu48"),
|
|
WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
rectWindow.right - rectWindow.left,
|
|
rectWindow.bottom - rectWindow.top,
|
|
NULL,NULL,hApp,NULL
|
|
);
|
|
|
|
if (hWnd == NULL)
|
|
{
|
|
UnregisterClass(_T("CEmu48"),hApp);
|
|
return TRUE;
|
|
}
|
|
|
|
VERIFY(hAccel = LoadAccelerators(hApp,MAKEINTRESOURCE(IDR_MENU)));
|
|
|
|
// remove debugger menu entry from resource
|
|
DeleteMenu(GetMenu(hWnd),ID_TOOL_DEBUG,MF_BYCOMMAND);
|
|
|
|
// initialization
|
|
EmuClearAllBreakpoints();
|
|
QueryPerformanceFrequency(&lFreq); // init high resolution counter
|
|
|
|
GetCurrentDirectory(sizeof(szCurrentDirectory), szCurrentDirectory);
|
|
szCurrentKml[0] = 0; // no KML file selected
|
|
|
|
ReadSettings();
|
|
|
|
UpdateWindowStatus();
|
|
|
|
// create shutdown auto event handle
|
|
hEventShutdn = CreateEvent(NULL,FALSE,FALSE,NULL);
|
|
if (hEventShutdn == NULL)
|
|
{
|
|
DestroyWindow(hWnd);
|
|
return TRUE;
|
|
}
|
|
|
|
// create debugger auto event handle
|
|
hEventDebug = CreateEvent(NULL,FALSE,FALSE,NULL);
|
|
if (hEventDebug == NULL)
|
|
{
|
|
CloseHandle(hEventShutdn); // close shutdown event handle
|
|
DestroyWindow(hWnd);
|
|
return TRUE;
|
|
}
|
|
|
|
nState = SM_RUN; // init state must be <> nNextState
|
|
nNextState = SM_INVALID; // go into invalid state
|
|
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&WorkerThread, NULL, CREATE_SUSPENDED, &lThreadId);
|
|
if (hThread == NULL)
|
|
{
|
|
CloseHandle(hEventDebug); // close debugger event handle
|
|
CloseHandle(hEventShutdn); // close event handle
|
|
DestroyWindow(hWnd);
|
|
return TRUE;
|
|
}
|
|
// on multiprocessor machines for QueryPerformanceCounter()
|
|
SetThreadAffinityMask(hThread,1);
|
|
ResumeThread(hThread); // start thread
|
|
while (nState!=nNextState) Sleep(0); // wait for thread initialized
|
|
|
|
idDdeInst = 0; // initialize DDE server
|
|
if (DdeInitialize(&idDdeInst,(PFNCALLBACK) &DdeCallback,
|
|
APPCLASS_STANDARD |
|
|
CBF_FAIL_EXECUTES | CBF_FAIL_ADVISES |
|
|
CBF_SKIP_REGISTRATIONS | CBF_SKIP_UNREGISTRATIONS,0))
|
|
{
|
|
TerminateThread(hThread, 0); // kill emulation thread
|
|
CloseHandle(hEventDebug); // close debugger event handle
|
|
CloseHandle(hEventShutdn); // close event handle
|
|
DestroyWindow(hWnd);
|
|
return TRUE;
|
|
}
|
|
|
|
_ASSERT(hWnd != NULL);
|
|
_ASSERT(hWindowDC != NULL);
|
|
|
|
szBufferFilename[0] = 0;
|
|
if (bFileExist) // open existing file
|
|
{
|
|
lstrcpyn(szBufferFilename,ppArgv[1],ARRAYSIZEOF(szBufferFilename));
|
|
}
|
|
if (nArgc == 1) // no argument
|
|
{
|
|
ReadLastDocument(szBufferFilename,ARRAYSIZEOF(szBufferFilename));
|
|
}
|
|
|
|
if (szBufferFilename[0]) // given default document
|
|
{
|
|
TCHAR szTemp[MAX_PATH+8] = _T("Loading ");
|
|
RECT rectClient;
|
|
|
|
_ASSERT(hWnd != NULL);
|
|
VERIFY(GetClientRect(hWnd,&rectClient));
|
|
GetCutPathName(szBufferFilename,&szTemp[8],MAX_PATH,rectClient.right/10-8);
|
|
SetWindowTitle(szTemp);
|
|
if (OpenDocument(szBufferFilename))
|
|
goto start;
|
|
}
|
|
|
|
SetWindowTitle(_T("New Document"));
|
|
if (NewDocument())
|
|
{
|
|
if (nArgc >= 2)
|
|
SaveDocumentAs(ppArgv[1]);
|
|
else
|
|
SetWindowTitle(_T("Untitled"));
|
|
goto start;
|
|
}
|
|
|
|
DestroyWindow(hWnd); // clean up system
|
|
return TRUE;
|
|
|
|
start:
|
|
// init clipboard format and name service
|
|
uCF_HpObj = RegisterClipboardFormat(_T(CF_HPOBJ));
|
|
hszService = DdeCreateStringHandle(idDdeInst,szAppName,0);
|
|
hszTopic = DdeCreateStringHandle(idDdeInst,szTopic,0);
|
|
DdeNameService(idDdeInst,hszService,NULL,DNS_REGISTER);
|
|
|
|
ShowWindow(hWnd, SW_SHOW);
|
|
|
|
if (pbyRom) SwitchToState(SM_RUN);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// DLLDestroyWnd
|
|
//
|
|
BOOL DLLDestroyWnd(VOID)
|
|
{
|
|
// clean up DDE server
|
|
DdeNameService(idDdeInst, hszService, NULL, DNS_UNREGISTER);
|
|
DdeFreeStringHandle(idDdeInst, hszService);
|
|
DdeFreeStringHandle(idDdeInst, hszTopic);
|
|
DdeUninitialize(idDdeInst);
|
|
|
|
WriteSettings(); // save variable settings
|
|
|
|
CloseHandle(hThread); // close emulation thread handle
|
|
CloseHandle(hEventShutdn); // close shutdown event handle
|
|
CloseHandle(hEventDebug); // close debugger event handle
|
|
_ASSERT(nState == SM_RETURN); // emulation thread down?
|
|
ResetDocument();
|
|
ResetBackup();
|
|
_ASSERT(pbyRom == NULL); // rom file unmapped
|
|
_ASSERT(pbyPort2 == NULL); // port2 file unmapped
|
|
_ASSERT(pKml == NULL); // KML script not closed
|
|
_ASSERT(szTitle == NULL); // freed allocated memory
|
|
_ASSERT(hPalette == NULL); // freed resource memory
|
|
if (pEmuClose) pEmuClose(); // call notify function
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//################
|
|
//#
|
|
//# Public external functions
|
|
//#
|
|
//################
|
|
|
|
/****************************************************************************
|
|
* EmuCreate
|
|
*****************************************************************************
|
|
*
|
|
* @func start Emu48 and load Ram file into emulator, if Ram file don't
|
|
* exist create a new one and save it under the given name
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc BOOL: FALSE = OK, TRUE = Error
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC BOOL CALLBACK EmuCreate(
|
|
LPCTSTR lpszFilename) // @parm String with RAM filename
|
|
{
|
|
return DLLCreateWnd(lpszFilename, NULL);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuCreateEx
|
|
*****************************************************************************
|
|
*
|
|
* @func start Emu48 and load Ram and Port2 file into emulator, if Ram file
|
|
* don't exist create a new one and save it under the given name
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc BOOL: FALSE = OK, TRUE = Error
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC BOOL CALLBACK EmuCreateEx(
|
|
LPCTSTR lpszFilename, // @parm String with RAM filename
|
|
LPCTSTR lpszPort2Name) // @parm String with Port2 filename
|
|
// or NULL for using name inside INI file
|
|
{
|
|
return DLLCreateWnd(lpszFilename, lpszPort2Name);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuDestroy
|
|
*****************************************************************************
|
|
*
|
|
* @func close Emu48, free all memory
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc BOOL: FALSE = OK, TRUE = Error
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC BOOL CALLBACK EmuDestroy(VOID)
|
|
{
|
|
if (hWnd == NULL) return TRUE; // Emu48 closed
|
|
|
|
// close Emu48 via exit
|
|
SendMessage(hWnd,WM_SYSCOMMAND,SC_CLOSE,0);
|
|
return FALSE;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuAcceleratorTable
|
|
*****************************************************************************
|
|
*
|
|
* @func load accelerator table of emulator
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc HACCEL: handle of the loaded accelerator table
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC HACCEL CALLBACK EmuAcceleratorTable(
|
|
HWND *phEmuWnd) // @parm return of emulator window handle
|
|
{
|
|
*phEmuWnd = hWnd;
|
|
return hAccel;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuCallBackClose
|
|
*****************************************************************************
|
|
*
|
|
* @func init CallBack handler to notify caller when Emu48 window close
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuCallBackClose(
|
|
VOID (CALLBACK *EmuClose)(VOID)) // @parm CallBack function notify caller Emu48 closed
|
|
{
|
|
pEmuClose = EmuClose; // set new handler
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuCallBackDocumentNotify
|
|
*****************************************************************************
|
|
*
|
|
* @func init CallBack handler to notify caller for actual document file
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuCallBackDocumentNotify(
|
|
VOID (CALLBACK *EmuDocumentNotify)(LPCTSTR lpszFilename)) // @parm CallBack function notify document filename
|
|
{
|
|
pEmuDocumentNotify = EmuDocumentNotify; // set new handler
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuLoadRamFile
|
|
*****************************************************************************
|
|
*
|
|
* @func load Ram file into emulator
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc BOOL: FALSE = OK, TRUE = Error (old file reloaded)
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC BOOL CALLBACK EmuLoadRamFile(
|
|
LPCTSTR lpszFilename) // @parm String with RAM filename
|
|
{
|
|
BOOL bErr;
|
|
|
|
if (pbyRom) SwitchToState(SM_INVALID); // stop emulation thread
|
|
bErr = !OpenDocument(lpszFilename); // load state file
|
|
if (pbyRom) SwitchToState(SM_RUN); // restart emulation thread
|
|
return bErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuSaveRamFile
|
|
*****************************************************************************
|
|
*
|
|
* @func save the current emulator Ram to file
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc BOOL: FALSE = OK, TRUE = Error (old file reloaded)
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC BOOL CALLBACK EmuSaveRamFile(VOID)
|
|
{
|
|
BOOL bErr;
|
|
|
|
if (pbyRom == NULL) return TRUE; // fail
|
|
SwitchToState(SM_INVALID); // stop emulation thread
|
|
bErr = !SaveDocument(); // save current state file
|
|
SwitchToState(SM_RUN); // restart emulation thread
|
|
return bErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuLoadObject
|
|
*****************************************************************************
|
|
*
|
|
* @func load object file to stack
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc BOOL: FALSE = OK, TRUE = Error
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC BOOL CALLBACK EmuLoadObject(
|
|
LPCTSTR lpszObjectFilename) // @parm String with object filename
|
|
{
|
|
HANDLE hFile;
|
|
DWORD dwFileSizeLow;
|
|
DWORD dwFileSizeHigh;
|
|
LPBYTE lpBuf;
|
|
WORD wError = S_ERR_BINARY; // set into error state
|
|
|
|
SuspendDebugger(); // suspend debugger
|
|
bDbgAutoStateCtrl = FALSE; // disable automatic debugger state control
|
|
|
|
if (!(Chipset.IORam[BITOFFSET]&DON)) // calculator off, turn on
|
|
{
|
|
KeyboardEvent(TRUE,0,0x8000);
|
|
Sleep(200);
|
|
KeyboardEvent(FALSE,0,0x8000);
|
|
Sleep(200);
|
|
// wait for sleep mode
|
|
while(Chipset.Shutdn == FALSE) Sleep(0);
|
|
}
|
|
|
|
if (nState != SM_RUN) goto cancel; // emulator must run to load on object
|
|
if (WaitForSleepState()) goto cancel; // wait for cpu SHUTDN then sleep state
|
|
|
|
_ASSERT(nState == SM_SLEEP);
|
|
|
|
hFile = CreateFile(lpszObjectFilename,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
SwitchToState(SM_RUN); // run state
|
|
goto cancel;
|
|
}
|
|
dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
|
|
if (dwFileSizeHigh != 0)
|
|
{ // file is too large.
|
|
SwitchToState(SM_RUN); // run state
|
|
CloseHandle(hFile);
|
|
goto cancel;
|
|
}
|
|
lpBuf = HeapAlloc(hHeap,0,dwFileSizeLow*2);
|
|
if (lpBuf == NULL)
|
|
{
|
|
SwitchToState(SM_RUN); // run state
|
|
CloseHandle(hFile);
|
|
goto cancel;
|
|
}
|
|
ReadFile(hFile, lpBuf+dwFileSizeLow, dwFileSizeLow, &dwFileSizeHigh, NULL);
|
|
CloseHandle(hFile);
|
|
|
|
wError = WriteStack(1,lpBuf,dwFileSizeLow);
|
|
|
|
HeapFree(hHeap,0,lpBuf);
|
|
|
|
SwitchToState(SM_RUN); // run state
|
|
while (nState!=nNextState) Sleep(0);
|
|
_ASSERT(nState == SM_RUN);
|
|
KeyboardEvent(TRUE,0,0x8000);
|
|
Sleep(200);
|
|
KeyboardEvent(FALSE,0,0x8000);
|
|
while(Chipset.Shutdn == FALSE) Sleep(0);
|
|
|
|
cancel:
|
|
bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control
|
|
ResumeDebugger();
|
|
return wError != S_ERR_NO;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuSaveObject
|
|
*****************************************************************************
|
|
*
|
|
* @func save object on stack to file
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc BOOL: FALSE = OK, TRUE = Error
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC BOOL CALLBACK EmuSaveObject(
|
|
LPCTSTR lpszObjectFilename) // @parm String with object filename
|
|
{
|
|
BOOL bErr;
|
|
|
|
if (nState != SM_RUN) return TRUE; // emulator must run to load on object
|
|
if (WaitForSleepState()) return TRUE; // wait for cpu SHUTDN then sleep state
|
|
_ASSERT(nState == SM_SLEEP);
|
|
bErr = !SaveObject(lpszObjectFilename);
|
|
SwitchToState(SM_RUN);
|
|
return bErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuCalculatorType
|
|
*****************************************************************************
|
|
*
|
|
* @func get ID of current calculator type
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc BYTE: '6' = HP38G with 64KB RAM
|
|
* 'A' = HP38G
|
|
* 'E' = HP39/40G
|
|
* 'S' = HP48SX
|
|
* 'G' = HP48GX
|
|
* 'X' = HP49G
|
|
* 'P' = HP39G+
|
|
* '2' = HP48GII
|
|
* 'Q' = HP49G+
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC BYTE CALLBACK EmuCalculatorType(VOID)
|
|
{
|
|
return Chipset.type;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuSimulateKey
|
|
*****************************************************************************
|
|
*
|
|
* @func simulate a key action
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuSimulateKey(
|
|
BOOL bKeyState, // @parm TRUE = pressed, FALSE = released
|
|
UINT out, // @parm key out line
|
|
UINT in) // @parm key in line
|
|
{
|
|
KeyboardEvent(bKeyState,out,in);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuPressOn
|
|
*****************************************************************************
|
|
*
|
|
* @func press On key (emulation must run)
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuPressOn(
|
|
BOOL bKeyState) // @parm TRUE = pressed, FALSE = released
|
|
{
|
|
KeyboardEvent(bKeyState,0,0x8000);
|
|
} |