emu48plus-mirror/SOURCE/EMU48DLL.C
Gwenhael Le Moine 390c734a61
2007-01-13: Updated to version 42
Signed-off-by: Gwenhael Le Moine <gwenhael.le.moine@gmail.com>
2024-03-20 07:41:14 +01:00

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);
}