2014-10-18: Updated to version 1.56

Signed-off-by: Gwenhael Le Moine <gwenhael.le.moine@gmail.com>
This commit is contained in:
Gwenhael Le Moine 2024-03-19 23:35:30 +01:00
parent 769c17c88a
commit 625fd3f663
No known key found for this signature in database
GPG key ID: FDFE3669426707A7
30 changed files with 1115 additions and 976 deletions

BIN
EMU48.EXE

Binary file not shown.

BIN
Emu48.exe Executable file

Binary file not shown.

View file

@ -1,4 +1,4 @@
Known bugs and restrictions of Emu48 V1.55 Known bugs and restrictions of Emu48 V1.56
------------------------------------------ ------------------------------------------
- the following I/O bits aren't emulated (incomplete) - the following I/O bits aren't emulated (incomplete)
@ -47,4 +47,4 @@ Known bugs and restrictions of Emu48 V1.55
- quitting the emulator while programming the flash isn't allowed, - quitting the emulator while programming the flash isn't allowed,
because the content of flash state machine isn't saved so far because the content of flash state machine isn't saved so far
05/21/13 (c) by Christoph Gießelink, c dot giesselink at gmx dot de 04/29/14 (c) by Christoph Gießelink, c dot giesselink at gmx dot de

View file

@ -1,3 +1,114 @@
Service Pack 56 for Emu48 Version 1.0
DEBUGGER.C
- removed INSTRSIZE definition
- changed function GetAddr(), made function more general by adding
address range check and the ability of decoding a symbolic entry
into an address
- bugfix in function OnSetCursor(), on "Activation follows mouse"
enabled child dialogs lost focus when cursor moved over parent
debugger dialog
- changed function Debugger(), removed initialization of global
variable wInstrSize
- changed function EnterAddr(), changed parameter storage from
static variable to window user data and replaced address decoder
code by GetAddr() function call
- changed function EnterBreakpoint(), replaced address decoder code
by GetAddr() function call and so added decoding a symbolic entry
into an address
- bugfix in function LoadMemData(), forgot to check if file content
fit into Saturn address range, loading now stops also after last
Saturn address written
- changed function SaveMemData(), added assertion for Saturn address
verification
- bugfix in function DebugMemLoad() and DebugMemSave(), adjusted to
new prototype of function GetAddr() and so added checking if the
given addresses are in the Saturn address range because Saturn
read/write functions don't do the range check; this also cause a
change in behavior, on empty address fields the operation fails
and the dialog still remains open
EMU48.C
- changed function SetSoundDeviceList(), replaced "Standard Audio"
device ID from constant to definition
- bugfix in function SettingsGeneralProc(), on "Activation follows
mouse" enabled debugger dialog maybe got focus even if it's not
the last active one
- bugfix in function SettingsPeripheralProc(), restart the sound
engine by calling the clean up and initialization functions now
- changed function OnFileNew(), removed call of function
SaveBackup() which is already a part of function NewDocument()
- changed function OnFileNew(), OnFileOpen(), OnFileMruOpen(),
OnFileSave(), OnFileSaveAs() and OnFileClose(), switched detection
of document is available from variable pbyRom (ROM image loaded)
to variable bDocumentAvail
- changed function OnViewCopy(), removed MONOCHROME source code part
- changed function WinMain(), added sound engine initialization and
clean up
EMU48.RC
- changed version and copyright
ENGINE.C
- initialized variable wInstrSize with size of last instruction
array (the circular buffer can hold (wInstrSize-1) instructions)
- bugfix in function WorkerThread(), moved sound engine
initialization and clean up from SM_RUN state to main function,
this prevents a non responding Saturn CPU emulation when changing
from SM_SLEEP to SM_RUN state during sound engine initialization
FETCH.C
- made typedef of jump table struct constant, so the const keyword
was removed from all references of JMPTAB
- changed function EvalOpcode(), minor code optimization
FILES.C
- bugfix in function PatchRom(), removed limitation that patch file
must have 6 characters minimum, fixed a buffer overflow when end
of patch file consists of white space characters, recalculate
CRC's only on address patch written and return failture on syntax
errors now
PCH.H
- added GWLP_USERDATA, SetWindowLongPtr, GetWindowLongPtr and
LONG_PTR definitions for MSVC6.0 SDK and earlier
SETTINGS.C
- changed function ReadSettings() and WriteSettings(), added item
"LastInstrBufSize" in section [Debugger] in the INI-File
SOUND.C
- bugfix in function DestroyWaveThread(), the WM_QUIT message may
failed and so the sound message thread wasn't shut down
- changed function SoundOpen(), added additional check if the sound
device support the necessary audio format
STACK.C
- added function Trim(), create a trimmed copy of the input string
- added function SetZInt(), contain implementation of prior function
RPL_SetZInt()
- changed function RPL_SetZInt(), wrapper for function SetZInt() to
trim data before decoding
- added function SetBcd(), contain implementation of prior function
RPL_SetBcd()
- changed function RPL_SetBcd(), wrapper for function SetBcd() to
trim data before decoding
- changed function RPL_SetComplex(), create a trimmed working copy
before decoding the outline brackets and because of the changed
function RPL_SetBcd() the real number arguments are accepted with
white spaces around now
- removed disabled function IsRealNumber(), it was more or less
functional compatible with actual modified function RPL_SetBcd()
TIMER.C
- removed UNIX_0_TIME definition
UDP.C
- changed function SendByteUdp(), replaced malloc() by _alloca()
call for UNICODE wchar to char string translation to get rid of
freeing the allocated memory manually
Service Pack 55 for Emu48 Version 1.0 Service Pack 55 for Emu48 Version 1.0
DISMEM.C DISMEM.C

View file

@ -27,8 +27,6 @@
// assert for register update // assert for register update
#define _ASSERTREG(r) _ASSERT(r >= REG_START && r <= REG_STOP) #define _ASSERTREG(r) _ASSERT(r >= REG_START && r <= REG_STOP)
#define INSTRSIZE 256 // size of last instruction buffer
#define WM_UPDATE (WM_USER+0x1000) // update debugger dialog box #define WM_UPDATE (WM_USER+0x1000) // update debugger dialog box
#define MEMWNDMAX (sizeof(nCol) / sizeof(nCol[0])) #define MEMWNDMAX (sizeof(nCol) / sizeof(nCol[0]))
@ -158,6 +156,50 @@ static VOID DisableMenuKeys(HWND hDlg)
return; return;
} }
//
// read edit control and decode content as hex number or if enabled as symbol name
//
static BOOL GetAddr(HWND hDlg,INT nID,DWORD *pdwAddr,DWORD dwMaxAddr,BOOL bSymbEnable)
{
TCHAR szBuffer[48];
INT i;
BOOL bSucc = TRUE;
HWND hWnd = GetDlgItem(hDlg,nID);
GetWindowText(hWnd,szBuffer,ARRAYSIZEOF(szBuffer));
if (*szBuffer != 0)
{
// if address is not a symbol name decode number
if ( !bSymbEnable || szBuffer[0] != _T('=')
|| RplGetAddr(&szBuffer[1],pdwAddr))
{
// test if valid hex address
for (i = 0; bSucc && i < (LONG) lstrlen(szBuffer); ++i)
{
bSucc = (_istxdigit(szBuffer[i]) != 0);
}
if (bSucc) // valid characters
{
// convert string to number
*pdwAddr = _tcstoul(szBuffer,NULL,16);
}
}
// inside address range?
bSucc = bSucc && (*pdwAddr <= dwMaxAddr);
if (!bSucc) // invalid address
{
SendMessage(hWnd,EM_SETSEL,0,-1);
SetFocus(hWnd); // focus to edit control
}
}
return bSucc;
}
// //
// set mapping menu // set mapping menu
// //
@ -1426,7 +1468,8 @@ static BOOL OnSetCursor(HWND hDlg)
// debugger not active but cursor is over debugger window // debugger not active but cursor is over debugger window
if (bActFollowsMouse && GetActiveWindow() != hDlg) if (bActFollowsMouse && GetActiveWindow() != hDlg)
{ {
ForceForegroundWindow(hDlg); // force debugger window to foreground // force debugger window to foreground
ForceForegroundWindow(GetLastActivePopup(hDlg));
} }
return FALSE; return FALSE;
} }
@ -1866,8 +1909,7 @@ static INT_PTR CALLBACK Debugger(HWND hDlg, UINT message, WPARAM wParam, LPARAM
SendDlgItemMessage(hDlg,IDC_STATIC_MISC, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); SendDlgItemMessage(hDlg,IDC_STATIC_MISC, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0));
// init last instruction circular buffer // init last instruction circular buffer
pdwInstrArray = (DWORD *) malloc(INSTRSIZE*sizeof(*pdwInstrArray)); pdwInstrArray = (DWORD *) malloc(wInstrSize*sizeof(*pdwInstrArray));
wInstrSize = INSTRSIZE; // size of last instruction array
wInstrWp = wInstrRp = 0; // write/read pointer wInstrWp = wInstrRp = 0; // write/read pointer
// init "Follow" menu entry in debugger "Memory" context menu // init "Follow" menu entry in debugger "Memory" context menu
@ -2749,41 +2791,22 @@ static INT_PTR OnNewValue(LPTSTR lpszValue)
// //
static INT_PTR CALLBACK EnterAddr(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) static INT_PTR CALLBACK EnterAddr(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{ {
static DWORD *pdwAddress;
HWND hWnd;
TCHAR szBuffer[64];
LONG i;
switch (message) switch (message)
{ {
case WM_INITDIALOG: case WM_INITDIALOG:
pdwAddress = (DWORD *) lParam; SetWindowLongPtr(hDlg,GWLP_USERDATA,(LONG_PTR) lParam);
return TRUE; return TRUE;
case WM_COMMAND: case WM_COMMAND:
wParam = LOWORD(wParam); wParam = LOWORD(wParam);
switch(wParam) switch(wParam)
{ {
case IDOK: case IDOK:
hWnd = GetDlgItem(hDlg,IDC_ENTERADR); if (!GetAddr(hDlg,
GetWindowText(hWnd,szBuffer,ARRAYSIZEOF(szBuffer)); IDC_ENTERADR,
(DWORD *) GetWindowLongPtr(hDlg,GWLP_USERDATA),
// if address is not a symbol name decode number 0xFFFFFFFF,
if ( !disassembler_symb || szBuffer[0] != _T('=') disassembler_symb))
|| RplGetAddr(&szBuffer[1],pdwAddress))
{
// test if valid hex address
for (i = 0; i < (LONG) lstrlen(szBuffer); ++i)
{
if (_istxdigit(szBuffer[i]) == 0)
{
SendMessage(hWnd,EM_SETSEL,0,-1);
SetFocus(hWnd); // focus to edit control
return FALSE; return FALSE;
}
}
if (*szBuffer) _stscanf(szBuffer,_T("%8X"),pdwAddress);
}
// no break // no break
case IDCANCEL: case IDCANCEL:
EndDialog(hDlg,wParam); EndDialog(hDlg,wParam);
@ -2814,9 +2837,7 @@ static INT_PTR CALLBACK EnterBreakpoint(HWND hDlg, UINT message, WPARAM wParam,
{ {
static BP_T *sBp; static BP_T *sBp;
HWND hWnd; DWORD dwAddr;
TCHAR szBuffer[8];
LONG i;
switch (message) switch (message)
{ {
@ -2836,19 +2857,9 @@ static INT_PTR CALLBACK EnterBreakpoint(HWND hDlg, UINT message, WPARAM wParam,
case IDC_BPREAD: sBp->nType = BP_READ; return TRUE; case IDC_BPREAD: sBp->nType = BP_READ; return TRUE;
case IDC_BPWRITE: sBp->nType = BP_WRITE; return TRUE; case IDC_BPWRITE: sBp->nType = BP_WRITE; return TRUE;
case IDOK: case IDOK:
hWnd = GetDlgItem(hDlg,IDC_ENTERADR); if (!GetAddr(hDlg,IDC_ENTERADR,&dwAddr,0xFFFFF,disassembler_symb))
GetWindowText(hWnd,szBuffer,ARRAYSIZEOF(szBuffer));
// test if valid hex address
for (i = 0; i < (LONG) lstrlen(szBuffer); ++i)
{
if (_istxdigit(szBuffer[i]) == 0)
{
SendMessage(hWnd,EM_SETSEL,0,-1);
SetFocus(hWnd);
return FALSE; return FALSE;
} sBp->dwAddr = dwAddr;
}
if (*szBuffer) _stscanf(szBuffer,_T("%5X"),&sBp->dwAddr);
// no break // no break
case IDCANCEL: case IDCANCEL:
EndDialog(hDlg,wParam); EndDialog(hDlg,wParam);
@ -3448,14 +3459,23 @@ static BOOL LoadMemData(LPCTSTR lpszFilename,DWORD dwStartAddr)
if (hFile == INVALID_HANDLE_VALUE) // error, couldn't create a new file if (hFile == INVALID_HANDLE_VALUE) // error, couldn't create a new file
return FALSE; return FALSE;
while (TRUE) // read until EOF while (dwStartAddr <= 0xFFFFF) // read until EOF or end of Saturn address space
{ {
ReadFile(hFile,&byData,sizeof(byData),&dwRead,NULL); ReadFile(hFile,&byData,sizeof(byData),&dwRead,NULL);
if (dwRead == 0) break; // EOF if (dwRead == 0) break; // EOF
if (dwStartAddr < 0xFFFFF)
{
Write2(dwStartAddr,byData); // write byte in map mode Write2(dwStartAddr,byData); // write byte in map mode
dwStartAddr += 2; dwStartAddr += 2;
} }
else // special handling to avoid address wrap around
{
byData &= 0xF;
Nwrite(&byData,dwStartAddr,1); // write nibble in map mode
++dwStartAddr;
}
}
CloseHandle(hFile); CloseHandle(hFile);
return TRUE; return TRUE;
@ -3476,6 +3496,7 @@ static BOOL SaveMemData(LPCTSTR lpszFilename,DWORD dwStartAddr,DWORD dwEndAddr)
for (dwAddr = dwStartAddr; dwAddr <= dwEndAddr; dwAddr += 2) for (dwAddr = dwStartAddr; dwAddr <= dwEndAddr; dwAddr += 2)
{ {
_ASSERT(dwAddr <= 0xFFFFF);
byData = Read2(dwAddr); // read byte in map mode byData = Read2(dwAddr); // read byte in map mode
WriteFile(hFile,&byData,sizeof(byData),&dwWritten,NULL); WriteFile(hFile,&byData,sizeof(byData),&dwWritten,NULL);
} }
@ -3484,32 +3505,6 @@ static BOOL SaveMemData(LPCTSTR lpszFilename,DWORD dwStartAddr,DWORD dwEndAddr)
return TRUE; return TRUE;
} }
//
// read edit control and decode content as hex number
//
static BOOL GetAddr(HWND hDlg, INT nID, DWORD *pdwAddr)
{
TCHAR szBuffer[8];
INT i;
HWND hWnd = GetDlgItem(hDlg,nID);
GetWindowText(hWnd,szBuffer,ARRAYSIZEOF(szBuffer));
// test if valid hex address
for (i = 0; i < (LONG) lstrlen(szBuffer); ++i)
{
if (_istxdigit(szBuffer[i]) == 0)
{
SendMessage(hWnd,EM_SETSEL,0,-1);
SetFocus(hWnd); // focus to edit control
return FALSE;
}
}
_stscanf(szBuffer,_T("%6X"),pdwAddr);
return TRUE;
}
// //
// memory load data // memory load data
// //
@ -3527,13 +3522,18 @@ static INT_PTR CALLBACK DebugMemLoad(HWND hDlg, UINT message, WPARAM wParam, LPA
return OnBrowseLoadMem(hDlg); return OnBrowseLoadMem(hDlg);
case IDOK: case IDOK:
dwStartAddr = -1; // no address given
// get filename // get filename
GetDlgItemText(hDlg,IDC_DEBUG_DATA_FILE,szFilename,ARRAYSIZEOF(szFilename)); GetDlgItemText(hDlg,IDC_DEBUG_DATA_FILE,szFilename,ARRAYSIZEOF(szFilename));
// decode address field // decode address field
if (!GetAddr(hDlg,IDC_DEBUG_DATA_STARTADDR,&dwStartAddr)) if ( !GetAddr(hDlg,IDC_DEBUG_DATA_STARTADDR,&dwStartAddr,0xFFFFF,FALSE)
|| dwStartAddr == -1)
return FALSE; return FALSE;
_ASSERT(dwStartAddr <= 0xFFFFF);
// load memory dump file // load memory dump file
if (!LoadMemData(szFilename,dwStartAddr)) if (!LoadMemData(szFilename,dwStartAddr))
return FALSE; return FALSE;
@ -3577,15 +3577,22 @@ static INT_PTR CALLBACK DebugMemSave(HWND hDlg, UINT message, WPARAM wParam, LPA
return OnBrowseSaveMem(hDlg); return OnBrowseSaveMem(hDlg);
case IDOK: case IDOK:
dwStartAddr = dwEndAddr = -1; // no address given
// get filename // get filename
GetDlgItemText(hDlg,IDC_DEBUG_DATA_FILE,szFilename,ARRAYSIZEOF(szFilename)); GetDlgItemText(hDlg,IDC_DEBUG_DATA_FILE,szFilename,ARRAYSIZEOF(szFilename));
// decode address fields // decode address fields
if (!GetAddr(hDlg,IDC_DEBUG_DATA_STARTADDR,&dwStartAddr)) if ( !GetAddr(hDlg,IDC_DEBUG_DATA_STARTADDR,&dwStartAddr,0xFFFFF,FALSE)
|| dwStartAddr == -1)
return FALSE; return FALSE;
if (!GetAddr(hDlg,IDC_DEBUG_DATA_ENDADDR,&dwEndAddr)) if ( !GetAddr(hDlg,IDC_DEBUG_DATA_ENDADDR,&dwEndAddr,0xFFFFF,FALSE)
|| dwEndAddr == -1)
return FALSE; return FALSE;
_ASSERT(dwStartAddr <= 0xFFFFF);
_ASSERT(dwEndAddr <= 0xFFFFF);
// save memory dump file // save memory dump file
if (!SaveMemData(szFilename,dwStartAddr,dwEndAddr)) if (!SaveMemData(szFilename,dwStartAddr,dwEndAddr))
return FALSE; return FALSE;

View file

@ -13,9 +13,7 @@
#include "kml.h" #include "kml.h"
#include "debugger.h" #include "debugger.h"
#define VERSION "1.55" #define VERSION "1.56"
// #define MONOCHROME // CF_BITMAP clipboard format
#ifdef _DEBUG #ifdef _DEBUG
LPCTSTR szNoTitle = _T("Emu48 ")_T(VERSION)_T(" Debug"); LPCTSTR szNoTitle = _T("Emu48 ")_T(VERSION)_T(" Debug");
@ -222,7 +220,7 @@ static VOID SetSoundDeviceList(HWND hWnd,UINT uDeviceID)
// preset selector // preset selector
uSelectDevice = (UINT) SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM) _T("Standard Audio")); uSelectDevice = (UINT) SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM) _T("Standard Audio"));
SendMessage(hWnd,CB_SETITEMDATA,uSelectDevice,(UINT) -1); SendMessage(hWnd,CB_SETITEMDATA,uSelectDevice,WAVE_MAPPER);
uDevNo = waveOutGetNumDevs(); uDevNo = waveOutGetNumDevs();
for (uDevID = 0; uDevID < uDevNo; ++uDevID) for (uDevID = 0; uDevID < uDevNo; ++uDevID)
@ -374,7 +372,7 @@ static BOOL CALLBACK SettingsGeneralProc(HWND hDlg, UINT uMsg, DWORD wParam, LON
SetWindowPos(hWnd,hWndInsertAfter,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE); SetWindowPos(hWnd,hWndInsertAfter,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE);
if (hDlgDebug != NULL) if (hDlgDebug != NULL)
{ {
SetWindowPos(hDlgDebug,hWndInsertAfter,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE); SetWindowPos(GetLastActivePopup(hDlgDebug),hWndInsertAfter,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE);
} }
return TRUE; return TRUE;
} }
@ -620,10 +618,17 @@ static BOOL CALLBACK SettingsPeripheralProc(HWND hDlg, UINT uMsg, DWORD wParam,
uDevId = (UINT) SendDlgItemMessage(hDlg,IDC_SOUND_DEVICE,CB_GETITEMDATA,i,0); uDevId = (UINT) SendDlgItemMessage(hDlg,IDC_SOUND_DEVICE,CB_GETITEMDATA,i,0);
if (uWaveDevId != uDevId) // sound device id changed if (uWaveDevId != uDevId) // sound device id changed
{ {
UINT nActState;
uWaveDevId = uDevId; // set new sound device id uWaveDevId = uDevId; // set new sound device id
nActState = SwitchToState(SM_SLEEP);
// restart sound engine with new device id // restart sound engine with new device id
SwitchToState(SwitchToState(SM_SLEEP)); SoundClose(); // close waveform-audio output device
SoundOpen(uWaveDevId); // open waveform-audio output device
SwitchToState(nActState);
} }
// UDP infrared printer settings // UDP infrared printer settings
GetDlgItemText(hDlg,IDC_IR_ADDR,szUdpServer,ARRAYSIZEOF(szUdpServer)); GetDlgItemText(hDlg,IDC_IR_ADDR,szUdpServer,ARRAYSIZEOF(szUdpServer));
@ -913,13 +918,11 @@ cancel:
// //
static LRESULT OnFileNew(VOID) static LRESULT OnFileNew(VOID)
{ {
if (pbyRom) if (bDocumentAvail)
{ {
SwitchToState(SM_INVALID); SwitchToState(SM_INVALID);
if (IDCANCEL == SaveChanges(bAutoSave)) if (IDCANCEL == SaveChanges(bAutoSave))
goto cancel; goto cancel;
SaveBackup();
} }
if (NewDocument()) SetWindowTitle(_T("Untitled")); if (NewDocument()) SetWindowTitle(_T("Untitled"));
cancel: cancel:
@ -932,7 +935,7 @@ cancel:
// //
static LRESULT OnFileOpen(VOID) static LRESULT OnFileOpen(VOID)
{ {
if (pbyRom) if (bDocumentAvail)
{ {
SwitchToState(SM_INVALID); SwitchToState(SM_INVALID);
if (IDCANCEL == SaveChanges(bAutoSave)) if (IDCANCEL == SaveChanges(bAutoSave))
@ -941,7 +944,7 @@ static LRESULT OnFileOpen(VOID)
if (GetOpenFilename()) if (GetOpenFilename())
{ {
if (OpenDocument(szBufferFilename)) if (OpenDocument(szBufferFilename))
MruAdd(szCurrentFilename); MruAdd(szBufferFilename);
} }
cancel: cancel:
if (pbyRom) SwitchToState(SM_RUN); if (pbyRom) SwitchToState(SM_RUN);
@ -959,7 +962,7 @@ static LRESULT OnFileMruOpen(UINT wID)
lpszFilename = MruFilename(wID); // full filename from MRU list lpszFilename = MruFilename(wID); // full filename from MRU list
if (lpszFilename == NULL) return 0; // MRU slot not filled if (lpszFilename == NULL) return 0; // MRU slot not filled
if (pbyRom) if (bDocumentAvail)
{ {
SwitchToState(SM_INVALID); SwitchToState(SM_INVALID);
if (IDCANCEL == SaveChanges(bAutoSave)) if (IDCANCEL == SaveChanges(bAutoSave))
@ -983,10 +986,12 @@ cancel:
// //
static LRESULT OnFileSave(VOID) static LRESULT OnFileSave(VOID)
{ {
if (pbyRom == NULL) return 0; if (bDocumentAvail)
{
SwitchToState(SM_INVALID); SwitchToState(SM_INVALID);
SaveChanges(TRUE); SaveChanges(TRUE);
SwitchToState(SM_RUN); SwitchToState(SM_RUN);
}
return 0; return 0;
} }
@ -995,16 +1000,16 @@ static LRESULT OnFileSave(VOID)
// //
static LRESULT OnFileSaveAs(VOID) static LRESULT OnFileSaveAs(VOID)
{ {
if (pbyRom == NULL) return 0; if (bDocumentAvail)
{
SwitchToState(SM_INVALID); SwitchToState(SM_INVALID);
if (GetSaveAsFilename()) if (GetSaveAsFilename())
{ {
if (SaveDocumentAs(szBufferFilename)) if (SaveDocumentAs(szBufferFilename))
MruAdd(szCurrentFilename); MruAdd(szCurrentFilename);
} }
SwitchToState(SM_RUN); SwitchToState(SM_RUN);
}
return 0; return 0;
} }
@ -1013,7 +1018,8 @@ static LRESULT OnFileSaveAs(VOID)
// //
static LRESULT OnFileClose(VOID) static LRESULT OnFileClose(VOID)
{ {
if (pbyRom == NULL) return 0; if (bDocumentAvail)
{
SwitchToState(SM_INVALID); SwitchToState(SM_INVALID);
if (SaveChanges(bAutoSave) != IDCANCEL) if (SaveChanges(bAutoSave) != IDCANCEL)
{ {
@ -1024,6 +1030,7 @@ static LRESULT OnFileClose(VOID)
{ {
SwitchToState(SM_RUN); SwitchToState(SM_RUN);
} }
}
return 0; return 0;
} }
@ -1053,7 +1060,6 @@ static LRESULT OnViewCopy(VOID)
{ {
if (EmptyClipboard()) if (EmptyClipboard())
{ {
#if !defined MONOCHROME
// DIB bitmap // DIB bitmap
#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4) #define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)
#define PALVERSION 0x300 #define PALVERSION 0x300
@ -1137,20 +1143,6 @@ static LRESULT OnViewCopy(VOID)
DeleteObject(hBmp); DeleteObject(hBmp);
#undef WIDTHBYTES #undef WIDTHBYTES
#undef PALVERSION #undef PALVERSION
#else
HBITMAP hOldBmp, hBmp;
HDC hBmpDC;
// don't work with background index <> 0
_ASSERT(nLcdZoom == 1 || nLcdZoom == 2 || nLcdZoom == 4);
hBmp = CreateBitmap(131*nLcdZoom,64*nLcdZoom,1,1,NULL);
hBmpDC = CreateCompatibleDC(NULL);
hOldBmp = (HBITMAP)SelectObject(hBmpDC,hBmp);
BitBlt(hBmpDC,0,0,131*nLcdZoom,64*nLcdZoom,hLcdDC,Chipset.boffset*nLcdZoom,0,SRCCOPY);
SetClipboardData(CF_BITMAP,hBmp);
SelectObject(hBmpDC,hOldBmp);
DeleteDC(hBmpDC);
#endif
} }
CloseClipboard(); CloseClipboard();
} }
@ -2021,6 +2013,8 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC
hszTopic = DdeCreateStringHandle(idDdeInst,szTopic,0); hszTopic = DdeCreateStringHandle(idDdeInst,szTopic,0);
DdeNameService(idDdeInst,hszService,NULL,DNS_REGISTER); DdeNameService(idDdeInst,hszService,NULL,DNS_REGISTER);
SoundOpen(uWaveDevId); // open waveform-audio output device
_ASSERT(hWnd != NULL); _ASSERT(hWnd != NULL);
_ASSERT(hWindowDC != NULL); _ASSERT(hWindowDC != NULL);
@ -2075,6 +2069,8 @@ start:
DdeFreeStringHandle(idDdeInst, hszTopic); DdeFreeStringHandle(idDdeInst, hszTopic);
DdeUninitialize(idDdeInst); DdeUninitialize(idDdeInst);
SoundClose(); // close waveform-audio output device
// get full path name of szCurrentFilename // get full path name of szCurrentFilename
if (GetFullPathName(szCurrentFilename,ARRAYSIZEOF(szBufferFilename),szBufferFilename,&lpFilePart) == 0) if (GetFullPathName(szCurrentFilename,ARRAYSIZEOF(szBufferFilename),szBufferFilename,&lpFilePart) == 0)
szBufferFilename[0] = 0; // no last document name szBufferFilename[0] = 0; // no last document name

View file

@ -296,7 +296,7 @@ FONT 8, "MS Sans Serif"
BEGIN BEGIN
ICON IDI_EMU48,IDC_STATIC,7,6,20,20,SS_REALSIZEIMAGE ICON IDI_EMU48,IDC_STATIC,7,6,20,20,SS_REALSIZEIMAGE
LTEXT "",IDC_VERSION,29,6,151,8,NOT WS_GROUP LTEXT "",IDC_VERSION,29,6,151,8,NOT WS_GROUP
LTEXT "Copyright © 2013 Christoph Gießelink && Sébastien Carlier", LTEXT "Copyright © 2014 Christoph Gießelink && Sébastien Carlier",
IDC_STATIC,29,18,181,8 IDC_STATIC,29,18,181,8
DEFPUSHBUTTON "OK",IDOK,215,12,39,14 DEFPUSHBUTTON "OK",IDOK,215,12,39,14
EDITTEXT IDC_LICENSE,7,33,247,112,ES_MULTILINE | ES_AUTOHSCROLL | EDITTEXT IDC_LICENSE,7,33,247,112,ES_MULTILINE | ES_AUTOHSCROLL |
@ -687,8 +687,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,5,5,0 FILEVERSION 1,5,6,0
PRODUCTVERSION 1,5,5,0 PRODUCTVERSION 1,5,6,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -705,12 +705,12 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Christoph Gießelink & Sebastien Carlier\0" VALUE "CompanyName", "Christoph Gießelink & Sebastien Carlier\0"
VALUE "FileDescription", "HP38/39/40/48/49 Emulator\0" VALUE "FileDescription", "HP38/39/40/48/49 Emulator\0"
VALUE "FileVersion", "1, 5, 5, 0\0" VALUE "FileVersion", "1, 5, 6, 0\0"
VALUE "InternalName", "Emu48\0" VALUE "InternalName", "Emu48\0"
VALUE "LegalCopyright", "Copyright © 2013\0" VALUE "LegalCopyright", "Copyright © 2014\0"
VALUE "OriginalFilename", "Emu48.exe\0" VALUE "OriginalFilename", "Emu48.exe\0"
VALUE "ProductName", "Emu48\0" VALUE "ProductName", "Emu48\0"
VALUE "ProductVersion", "1, 5, 5, 0\0" VALUE "ProductVersion", "1, 5, 6, 0\0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -51,7 +51,7 @@ DWORD dwDbgRstkp; // stack recursion level of step over end
DWORD dwDbgRstk; // possible return address DWORD dwDbgRstk; // possible return address
DWORD *pdwInstrArray = NULL; // last instruction array DWORD *pdwInstrArray = NULL; // last instruction array
WORD wInstrSize; // size of last instruction array WORD wInstrSize = 256; // size of last instruction array
WORD wInstrWp; // write pointer of instruction array WORD wInstrWp; // write pointer of instruction array
WORD wInstrRp; // read pointer of instruction array WORD wInstrRp; // read pointer of instruction array
@ -566,7 +566,6 @@ loop:
SetHP48Time(); // update HP48 time & date SetHP48Time(); // update HP48 time & date
// start display counter/update engine // start display counter/update engine
StartDisplay((BYTE)(((Chipset.IORam[LINECOUNT+1]<<4)|Chipset.IORam[LINECOUNT])&0x3F)); StartDisplay((BYTE)(((Chipset.IORam[LINECOUNT+1]<<4)|Chipset.IORam[LINECOUNT])&0x3F));
SoundOpen(uWaveDevId); // open waveform-audio output device
StartBatMeasure(); // start battery measurement StartBatMeasure(); // start battery measurement
StartTimers(); StartTimers();
} }
@ -628,7 +627,6 @@ loop:
StopDisplay(); // stop display counter/update StopDisplay(); // stop display counter/update
StopBatMeasure(); // stop battery measurement StopBatMeasure(); // stop battery measurement
StopTimers(); StopTimers();
SoundClose(); // close waveform-audio output device
while (nNextState == SM_SLEEP) // go into sleep state while (nNextState == SM_SLEEP) // go into sleep state
{ {

View file

@ -11,14 +11,14 @@
#define F 0xFF // F = function #define F 0xFF // F = function
typedef struct typedef const struct
{ {
const VOID *pLnk; const VOID *pLnk;
const DWORD dwTyp; const DWORD dwTyp;
} JMPTAB, *PJMPTAB; } JMPTAB, *PJMPTAB;
// jump tables // jump tables
static const JMPTAB oF_[] = static JMPTAB oF_[] =
{ {
oF0, F, oF0, F,
oF1, F, oF1, F,
@ -38,7 +38,7 @@ static const JMPTAB oF_[] =
oFF, F oFF, F
}; };
static const JMPTAB oE_[] = static JMPTAB oE_[] =
{ {
oE0, F, oE0, F,
oE1, F, oE1, F,
@ -58,7 +58,7 @@ static const JMPTAB oE_[] =
oEF, F oEF, F
}; };
static const JMPTAB oD_[] = static JMPTAB oD_[] =
{ {
oD0, F, oD0, F,
oD1, F, oD1, F,
@ -78,7 +78,7 @@ static const JMPTAB oD_[] =
oDF, F oDF, F
}; };
static const JMPTAB oC_[] = static JMPTAB oC_[] =
{ {
oC0, F, oC0, F,
oC1, F, oC1, F,
@ -98,7 +98,7 @@ static const JMPTAB oC_[] =
oCF, F oCF, F
}; };
static const JMPTAB oBb_[] = static JMPTAB oBb_[] =
{ {
oBb0, F, oBb0, F,
oBb1, F, oBb1, F,
@ -118,7 +118,7 @@ static const JMPTAB oBb_[] =
oBbF, F oBbF, F
}; };
static const JMPTAB oBa_[] = static JMPTAB oBa_[] =
{ {
oBa0, F, oBa0, F,
oBa1, F, oBa1, F,
@ -138,7 +138,7 @@ static const JMPTAB oBa_[] =
oBaF, F oBaF, F
}; };
static const JMPTAB oB_[] = static JMPTAB oB_[] =
{ {
oBa_, 2, oBa_, 2,
oBa_, 2, oBa_, 2,
@ -158,7 +158,7 @@ static const JMPTAB oB_[] =
oBb_, 2 oBb_, 2
}; };
static const JMPTAB oAb_[] = static JMPTAB oAb_[] =
{ {
oAb0, F, oAb0, F,
oAb1, F, oAb1, F,
@ -178,7 +178,7 @@ static const JMPTAB oAb_[] =
oAbF, F oAbF, F
}; };
static const JMPTAB oAa_[] = static JMPTAB oAa_[] =
{ {
oAa0, F, oAa0, F,
oAa1, F, oAa1, F,
@ -198,7 +198,7 @@ static const JMPTAB oAa_[] =
oAaF, F oAaF, F
}; };
static const JMPTAB oA_[] = static JMPTAB oA_[] =
{ {
oAa_, 2, oAa_, 2,
oAa_, 2, oAa_, 2,
@ -218,7 +218,7 @@ static const JMPTAB oA_[] =
oAb_, 2 oAb_, 2
}; };
static const JMPTAB o9b_[] = static JMPTAB o9b_[] =
{ {
o9b0, F, o9b0, F,
o9b1, F, o9b1, F,
@ -238,7 +238,7 @@ static const JMPTAB o9b_[] =
o9bF, F o9bF, F
}; };
static const JMPTAB o9a_[] = static JMPTAB o9a_[] =
{ {
o9a0, F, o9a0, F,
o9a1, F, o9a1, F,
@ -258,7 +258,7 @@ static const JMPTAB o9a_[] =
o9aF, F o9aF, F
}; };
static const JMPTAB o9_[] = static JMPTAB o9_[] =
{ {
o9a_, 2, o9a_, 2,
o9a_, 2, o9a_, 2,
@ -278,7 +278,7 @@ static const JMPTAB o9_[] =
o9b_, 2 o9b_, 2
}; };
static const JMPTAB o8B_[] = static JMPTAB o8B_[] =
{ {
o8B0, F, o8B0, F,
o8B1, F, o8B1, F,
@ -298,7 +298,7 @@ static const JMPTAB o8B_[] =
o8BF, F o8BF, F
}; };
static const JMPTAB o8A_[] = static JMPTAB o8A_[] =
{ {
o8A0, F, o8A0, F,
o8A1, F, o8A1, F,
@ -318,7 +318,7 @@ static const JMPTAB o8A_[] =
o8AF, F o8AF, F
}; };
static const JMPTAB o81B_[] = static JMPTAB o81B_[] =
{ {
o_invalid4, F, o_invalid4, F,
o81B1, F, // normally o_invalid4, beep patch o81B1, F, // normally o_invalid4, beep patch
@ -338,7 +338,7 @@ static const JMPTAB o81B_[] =
o_invalid4, F o_invalid4, F
}; };
static const JMPTAB o81Af2_[] = static JMPTAB o81Af2_[] =
{ {
o81Af20, F, o81Af20, F,
o81Af21, F, o81Af21, F,
@ -358,7 +358,7 @@ static const JMPTAB o81Af2_[] =
o_invalid6, F o_invalid6, F
}; };
static const JMPTAB o81Af1_[] = static JMPTAB o81Af1_[] =
{ {
o81Af10, F, o81Af10, F,
o81Af11, F, o81Af11, F,
@ -378,7 +378,7 @@ static const JMPTAB o81Af1_[] =
o_invalid6, F o_invalid6, F
}; };
static const JMPTAB o81Af0_[] = static JMPTAB o81Af0_[] =
{ {
o81Af00, F, o81Af00, F,
o81Af01, F, o81Af01, F,
@ -398,7 +398,7 @@ static const JMPTAB o81Af0_[] =
o_invalid6, F o_invalid6, F
}; };
static const JMPTAB o81A_[] = static JMPTAB o81A_[] =
{ {
o81Af0_, 5, o81Af0_, 5,
o81Af1_, 5, o81Af1_, 5,
@ -418,7 +418,7 @@ static const JMPTAB o81A_[] =
o_invalid6, F o_invalid6, F
}; };
static const JMPTAB o819_[] = static JMPTAB o819_[] =
{ {
o819f0, F, o819f0, F,
o819f1, F, o819f1, F,
@ -438,7 +438,7 @@ static const JMPTAB o819_[] =
o_invalid5, F o_invalid5, F
}; };
static const JMPTAB o818_[] = static JMPTAB o818_[] =
{ {
o818f0x, F, o818f0x, F,
o818f1x, F, o818f1x, F,
@ -458,7 +458,7 @@ static const JMPTAB o818_[] =
o_invalid6, F o_invalid6, F
}; };
static const JMPTAB o81_[] = static JMPTAB o81_[] =
{ {
o810, F, o810, F,
o811, F, o811, F,
@ -478,7 +478,7 @@ static const JMPTAB o81_[] =
o81F, F o81F, F
}; };
static const JMPTAB o8081_[] = static JMPTAB o8081_[] =
{ {
o80810, F, o80810, F,
o_invalid5, F, o_invalid5, F,
@ -498,7 +498,7 @@ static const JMPTAB o8081_[] =
o_invalid5, F o_invalid5, F
}; };
static const JMPTAB o808_[] = static JMPTAB o808_[] =
{ {
o8080, F, o8080, F,
o8081_, 4, o8081_, 4,
@ -518,7 +518,7 @@ static const JMPTAB o808_[] =
o808F, F o808F, F
}; };
static const JMPTAB o80_[] = static JMPTAB o80_[] =
{ {
o800, F, o800, F,
o801, F, o801, F,
@ -538,7 +538,7 @@ static const JMPTAB o80_[] =
o80Fn, F o80Fn, F
}; };
static const JMPTAB o8_[] = static JMPTAB o8_[] =
{ {
o80_, 2, o80_, 2,
o81_, 2, o81_, 2,
@ -558,7 +558,7 @@ static const JMPTAB o8_[] =
o8Fd5, F o8Fd5, F
}; };
static const JMPTAB o15_[] = static JMPTAB o15_[] =
{ {
o150a, F, o150a, F,
o151a, F, o151a, F,
@ -578,7 +578,7 @@ static const JMPTAB o15_[] =
o15Fx, F o15Fx, F
}; };
static const JMPTAB o14_[] = static JMPTAB o14_[] =
{ {
o140, F, o140, F,
o141, F, o141, F,
@ -598,7 +598,7 @@ static const JMPTAB o14_[] =
o14F, F o14F, F
}; };
static const JMPTAB o13_[] = static JMPTAB o13_[] =
{ {
o130, F, o130, F,
o131, F, o131, F,
@ -618,7 +618,7 @@ static const JMPTAB o13_[] =
o13F, F o13F, F
}; };
static const JMPTAB o12_[] = static JMPTAB o12_[] =
{ {
o120, F, o120, F,
o121, F, o121, F,
@ -638,7 +638,7 @@ static const JMPTAB o12_[] =
o_invalid3, F o_invalid3, F
}; };
static const JMPTAB o11_[] = static JMPTAB o11_[] =
{ {
o110, F, o110, F,
o111, F, o111, F,
@ -658,7 +658,7 @@ static const JMPTAB o11_[] =
o_invalid3, F o_invalid3, F
}; };
static const JMPTAB o10_[] = static JMPTAB o10_[] =
{ {
o100, F, o100, F,
o101, F, o101, F,
@ -678,7 +678,7 @@ static const JMPTAB o10_[] =
o_invalid3, F o_invalid3, F
}; };
static const JMPTAB o1_[] = static JMPTAB o1_[] =
{ {
o10_, 2, o10_, 2,
o11_, 2, o11_, 2,
@ -698,7 +698,7 @@ static const JMPTAB o1_[] =
o1Fd5, F o1Fd5, F
}; };
static const JMPTAB o0E_[] = static JMPTAB o0E_[] =
{ {
o0Ef0, F, o0Ef0, F,
o0Ef1, F, o0Ef1, F,
@ -718,7 +718,7 @@ static const JMPTAB o0E_[] =
o0EfF, F o0EfF, F
}; };
static const JMPTAB o0_[] = static JMPTAB o0_[] =
{ {
o00, F, o00, F,
o01, F, o01, F,
@ -738,7 +738,7 @@ static const JMPTAB o0_[] =
o0F, F o0F, F
}; };
static const JMPTAB o_[] = static JMPTAB o_[] =
{ {
o0_, 1, o0_, 1,
o1_, 1, o1_, 1,
@ -761,15 +761,15 @@ static const JMPTAB o_[] =
// opcode dispatcher // opcode dispatcher
VOID EvalOpcode(LPBYTE I) VOID EvalOpcode(LPBYTE I)
{ {
DWORD dwTemp,dwIndex = 0; DWORD dwIndex = 0;
JMPTAB const *pJmpTab = o_; PJMPTAB pJmpTab = o_;
do do
{ {
dwTemp = I[dwIndex]; // table entry _ASSERT(I[dwIndex] <= 0xf); // found packed data
_ASSERT(dwTemp <= 0xf); // found packed data pJmpTab = &pJmpTab[I[dwIndex]]; // table entry by opcode
dwIndex = pJmpTab[dwTemp].dwTyp; // next pointer type dwIndex = pJmpTab->dwTyp; // next pointer type
pJmpTab = (PJMPTAB) pJmpTab[dwTemp].pLnk; // next pointer to table/function pJmpTab = (PJMPTAB) pJmpTab->pLnk; // next pointer to table/function
} }
while (dwIndex != F); // reference to table? -> again while (dwIndex != F); // reference to table? -> again

View file

@ -312,6 +312,7 @@ BOOL PatchRom(LPCTSTR szFilename)
PSZ lpStop,lpBuf = NULL; PSZ lpStop,lpBuf = NULL;
DWORD dwAddress = 0; DWORD dwAddress = 0;
UINT nPos = 0; UINT nPos = 0;
BOOL bSucc = TRUE;
if (pbyRom == NULL) return FALSE; if (pbyRom == NULL) return FALSE;
SetCurrentDirectory(szEmuDirectory); SetCurrentDirectory(szEmuDirectory);
@ -319,13 +320,8 @@ BOOL PatchRom(LPCTSTR szFilename)
SetCurrentDirectory(szCurrentDirectory); SetCurrentDirectory(szCurrentDirectory);
if (hFile == INVALID_HANDLE_VALUE) return FALSE; if (hFile == INVALID_HANDLE_VALUE) return FALSE;
dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh); dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
if (dwFileSizeLow <= 5) if (dwFileSizeHigh != 0 || dwFileSizeLow == 0)
{ // file is too small. { // file is too large or empty
CloseHandle(hFile);
return FALSE;
}
if (dwFileSizeHigh != 0)
{ // file is too large.
CloseHandle(hFile); CloseHandle(hFile);
return FALSE; return FALSE;
} }
@ -341,20 +337,15 @@ BOOL PatchRom(LPCTSTR szFilename)
nPos = 0; nPos = 0;
while (lpBuf[nPos]) while (lpBuf[nPos])
{ {
do // remove blank space // skip whitespace characters
{ nPos += (UINT) strspn(&lpBuf[nPos]," \t\n\r");
if ( (lpBuf[nPos]!=' ')
&&(lpBuf[nPos]!='\n') if (lpBuf[nPos] == ';') // comment?
&&(lpBuf[nPos]!='\r')
&&(lpBuf[nPos]!='\t')) break;
nPos++;
} while (lpBuf[nPos]);
if (lpBuf[nPos]==';') // comment ?
{ {
do do
{ {
nPos++; nPos++;
if (lpBuf[nPos]=='\n') if (lpBuf[nPos] == '\n')
{ {
nPos++; nPos++;
break; break;
@ -363,10 +354,22 @@ BOOL PatchRom(LPCTSTR szFilename)
continue; continue;
} }
dwAddress = strtoul(&lpBuf[nPos], &lpStop, 16); dwAddress = strtoul(&lpBuf[nPos], &lpStop, 16);
nPos += (UINT) (lpStop - &lpBuf[nPos]) + 1; nPos = (UINT) (lpStop - lpBuf); // position of lpStop
if (*lpStop != ':' || *lpStop == 0)
if (*lpStop != 0) // data behind address
{
if (*lpStop != ':') // invalid syntax
{
// skip to end of line
while (lpBuf[nPos] != '\n' && lpBuf[nPos] != 0)
{
++nPos;
}
bSucc = FALSE;
continue; continue;
while (lpBuf[nPos]) }
while (lpBuf[++nPos])
{ {
if (isxdigit(lpBuf[nPos]) == FALSE) break; if (isxdigit(lpBuf[nPos]) == FALSE) break;
if (dwAddress < dwRomSize) // patch ROM if (dwAddress < dwRomSize) // patch ROM
@ -375,11 +378,12 @@ BOOL PatchRom(LPCTSTR szFilename)
PatchNibble(dwAddress, Asc2Nib(lpBuf[nPos])); PatchNibble(dwAddress, Asc2Nib(lpBuf[nPos]));
} }
++dwAddress; ++dwAddress;
++nPos;
} }
} }
}
_ASSERT(nPos <= dwFileSizeLow); // buffer overflow?
free(lpBuf); free(lpBuf);
return TRUE; return bSucc;
} }

View file

@ -37,12 +37,19 @@
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
#endif #endif
#if !defined GWLP_USERDATA
#define GWLP_USERDATA GWL_USERDATA
#endif
#if !defined IDC_HAND // Win2k specific definition #if !defined IDC_HAND // Win2k specific definition
#define IDC_HAND MAKEINTRESOURCE(32649) #define IDC_HAND MAKEINTRESOURCE(32649)
#endif #endif
#if _MSC_VER <= 1200 // missing type definition in the MSVC6.0 SDK and earlier #if _MSC_VER <= 1200 // missing type definition in the MSVC6.0 SDK and earlier
#define SetWindowLongPtr SetWindowLong
#define GetWindowLongPtr GetWindowLong
typedef SIZE_T DWORD_PTR, *PDWORD_PTR; typedef SIZE_T DWORD_PTR, *PDWORD_PTR;
typedef LONG LONG_PTR, *PLONG_PTR;
#endif #endif
#if _MSC_VER >= 1400 // valid for VS2005 and later #if _MSC_VER >= 1400 // valid for VS2005 and later

View file

@ -175,6 +175,8 @@ VOID ReadSettings(VOID)
ReadString(_T("Port2"),_T("Filename"),_T("SHARED.BIN"),szPort2Filename,ARRAYSIZEOF(szPort2Filename)); ReadString(_T("Port2"),_T("Filename"),_T("SHARED.BIN"),szPort2Filename,ARRAYSIZEOF(szPort2Filename));
// KML // KML
bAlwaysDisplayLog = ReadInt(_T("KML"),_T("AlwaysDisplayLog"),bAlwaysDisplayLog); bAlwaysDisplayLog = ReadInt(_T("KML"),_T("AlwaysDisplayLog"),bAlwaysDisplayLog);
// Debugger
wInstrSize = ReadInt(_T("Debugger"),_T("LastInstrBufSize"),wInstrSize);
// Disassembler // Disassembler
disassembler_mode = ReadInt(_T("Disassembler"),_T("Mnemonics"),disassembler_mode); disassembler_mode = ReadInt(_T("Disassembler"),_T("Mnemonics"),disassembler_mode);
disassembler_symb = ReadInt(_T("Disassembler"),_T("Symbolic"),disassembler_symb); disassembler_symb = ReadInt(_T("Disassembler"),_T("Symbolic"),disassembler_symb);
@ -223,6 +225,8 @@ VOID WriteSettings(VOID)
WriteString(_T("Port2"),_T("Filename"),szPort2Filename); WriteString(_T("Port2"),_T("Filename"),szPort2Filename);
// KML // KML
WriteInt(_T("KML"),_T("AlwaysDisplayLog"),bAlwaysDisplayLog); WriteInt(_T("KML"),_T("AlwaysDisplayLog"),bAlwaysDisplayLog);
// Debugger
WriteInt(_T("Debugger"),_T("LastInstrBufSize"),wInstrSize);
// Disassembler // Disassembler
WriteInt(_T("Disassembler"),_T("Mnemonics"),disassembler_mode); WriteInt(_T("Disassembler"),_T("Mnemonics"),disassembler_mode);
WriteInt(_T("Disassembler"),_T("Symbolic"),disassembler_symb); WriteInt(_T("Disassembler"),_T("Symbolic"),disassembler_symb);

View file

@ -96,7 +96,8 @@ static VOID DestroyWaveThread(VOID)
if (hThreadWave != NULL) // sound message handler thread running if (hThreadWave != NULL) // sound message handler thread running
{ {
// shut down thread // shut down thread
PostThreadMessage(dwThreadWaveId,WM_QUIT,0,0); while (!PostThreadMessage(dwThreadWaveId,WM_QUIT,0,0))
Sleep(0);
WaitForSingleObject(hThreadWave,INFINITE); WaitForSingleObject(hThreadWave,INFINITE);
CloseHandle(hThreadWave); CloseHandle(hThreadWave);
hThreadWave = NULL; hThreadWave = NULL;
@ -345,7 +346,8 @@ BOOL SoundGetDeviceID(UINT *puDeviceID)
// //
BOOL SoundOpen(UINT uDeviceID) BOOL SoundOpen(UINT uDeviceID)
{ {
if (hWaveDevice == NULL) // no sound device // check if sound device is already open
if (hWaveDevice == NULL && SoundAvailable(uDeviceID))
{ {
WAVEFORMATEX wf; WAVEFORMATEX wf;
BOOL bSucc; BOOL bSucc;

View file

@ -27,6 +27,31 @@ BOOL bDetectClpObject = TRUE; // try to detect clipboard object
//# //#
//################ //################
static LPTSTR Trim(LPCTSTR cp)
{
LPCTSTR pcWs = _T(" \t\n\r"); // valid whitespace characters
LPTSTR pc;
DWORD dwFirst,dwLast;
dwLast = lstrlen(cp); // last position in string (EOS)
// trim leading and tailing whitespace characters
dwFirst = (DWORD) _tcsspn(cp,pcWs); // position of 1st non whitespace character
// search for position behind last non whitespace character
while (dwLast > dwFirst && _tcschr(pcWs,cp[dwLast-1]) != NULL)
--dwLast;
dwLast = 1 + dwLast - dwFirst; // calculate buffer length
if ((pc = (LPTSTR) malloc(dwLast * sizeof(*pc))) != NULL)
{
lstrcpyn(pc,&cp[dwFirst],dwLast); // copy relevant data + EOS
}
return pc;
}
static INT RPL_GetZInt(BYTE CONST *pbyNum,INT nIntLen,LPTSTR cp,INT nSize) static INT RPL_GetZInt(BYTE CONST *pbyNum,INT nIntLen,LPTSTR cp,INT nSize)
{ {
INT i = 0; // character counter INT i = 0; // character counter
@ -60,7 +85,7 @@ static INT RPL_GetZInt(BYTE CONST *pbyNum,INT nIntLen,LPTSTR cp,INT nSize)
return i; return i;
} }
static INT RPL_SetZInt(LPCTSTR cp,LPBYTE pbyNum,INT nSize) static __inline INT SetZInt(LPCTSTR cp,LPBYTE pbyNum,INT nSize)
{ {
BYTE bySign; BYTE bySign;
INT nStrLen,nNumSize; INT nStrLen,nNumSize;
@ -109,6 +134,20 @@ static INT RPL_SetZInt(LPCTSTR cp,LPBYTE pbyNum,INT nSize)
return nNumSize; return nNumSize;
} }
static INT RPL_SetZInt(LPCTSTR cp,LPBYTE pbyNum,INT nSize)
{
LPTSTR pszData;
INT s = 0;
if ((pszData = Trim(cp)) != NULL) // create a trimmed working copy of the string
{
s = SetZInt(pszData,pbyNum,nSize);
free(pszData);
}
return s;
}
static INT RPL_GetBcd(BYTE CONST *pbyNum,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPTSTR cp,INT nSize) static INT RPL_GetBcd(BYTE CONST *pbyNum,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPTSTR cp,INT nSize)
{ {
BYTE byNib; BYTE byNib;
@ -198,7 +237,7 @@ static INT RPL_GetBcd(BYTE CONST *pbyNum,INT nMantLen,INT nExpLen,CONST TCHAR cD
return i; return i;
} }
static INT RPL_SetBcd(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPBYTE pbyNum,INT nSize) static __inline INT SetBcd(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPBYTE pbyNum,INT nSize)
{ {
TCHAR cVc[] = _T(".0123456789eE+-"); TCHAR cVc[] = _T(".0123456789eE+-");
@ -318,6 +357,20 @@ static INT RPL_SetBcd(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPBYT
return nMantLen + nExpLen + 1; return nMantLen + nExpLen + 1;
} }
static INT RPL_SetBcd(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPBYTE pbyNum,INT nSize)
{
LPTSTR pszData;
INT s = 0;
if ((pszData = Trim(cp)) != NULL) // create a trimmed working copy of the string
{
s = SetBcd(pszData,nMantLen,nExpLen,cDec,pbyNum,nSize);
free(pszData);
}
return s;
}
static INT RPL_GetComplex(BYTE CONST *pbyNum,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPTSTR cp,INT nSize) static INT RPL_GetComplex(BYTE CONST *pbyNum,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPTSTR cp,INT nSize)
{ {
INT nLen,nPos; INT nLen,nPos;
@ -371,8 +424,7 @@ static INT RPL_SetComplex(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,L
? _T(',') // radix mark '.' -> ',' separator ? _T(',') // radix mark '.' -> ',' separator
: _T(';'); // radix mark ',' -> ';' separator : _T(';'); // radix mark ',' -> ';' separator
// create a working copy of the string if ((pszData = Trim(cp)) != NULL) // create a trimmed working copy of the string
if ((pszData = DuplicateString(cp)) != NULL)
{ {
INT nStrLength = lstrlen(pszData); // string length INT nStrLength = lstrlen(pszData); // string length
@ -415,35 +467,6 @@ static INT RPL_SetComplex(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,L
//# //#
//################ //################
#if 0
static INT IsRealNumber(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPBYTE pbyNum,INT nSize)
{
LPTSTR lpszNumber;
INT nLength = 0;
if ((lpszNumber = DuplicateString(cp)) != NULL)
{
LPTSTR p = lpszNumber;
INT i;
// cut heading whitespaces
for (; *p == _T(' ') || *p == _T('\t'); ++p) { }
// cut tailing whitespaces
for (i = lstrlen(p); --i >= 0;)
{
if (p[i] != _T(' ') && p[i] != _T('\t'))
break;
}
p[++i] = 0; // new EOS
nLength = RPL_SetBcd(p,nMantLen,nExpLen,cDec,pbyNum,nSize);
HeapFree(hHeap,0,lpszNumber);
}
return nLength;
}
#endif
static TCHAR GetRadix(VOID) static TCHAR GetRadix(VOID)
{ {
// get locale decimal point // get locale decimal point

View file

@ -13,9 +13,6 @@
#define AUTO_OFF 10 // Time in minutes for 'auto off' #define AUTO_OFF 10 // Time in minutes for 'auto off'
// Ticks for 01.01.1970 00:00:00
#define UNIX_0_TIME ((ULONGLONG) 0x0001cf2e8f800000)
// Ticks for 'auto off' // Ticks for 'auto off'
#define OFF_TIME ((ULONGLONG) (AUTO_OFF * 60) << 13) #define OFF_TIME ((ULONGLONG) (AUTO_OFF * 60) << 13)

View file

@ -37,7 +37,7 @@ BOOL SendByteUdp(BYTE byData)
#if defined _UNICODE #if defined _UNICODE
DWORD dwLength = lstrlen(szUdpServer) + 1; DWORD dwLength = lstrlen(szUdpServer) + 1;
if ((lpszIpAddr = (LPSTR) malloc(dwLength)) == NULL) if ((lpszIpAddr = (LPSTR) _alloca(dwLength)) == NULL)
return TRUE; // server not found return TRUE; // server not found
WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK,
@ -55,17 +55,11 @@ BOOL SendByteUdp(BYTE byData)
PHOSTENT host = gethostbyname(lpszIpAddr); PHOSTENT host = gethostbyname(lpszIpAddr);
if (host == NULL) if (host == NULL)
{ {
#if defined _UNICODE
free(lpszIpAddr);
#endif
return TRUE; // server not found return TRUE; // server not found
} }
ip_addr.s_addr = ((PIN_ADDR) host->h_addr_list[0])->s_addr; ip_addr.s_addr = ((PIN_ADDR) host->h_addr_list[0])->s_addr;
} }
#if defined _UNICODE
free(lpszIpAddr);
#endif
} }
// create UDP socket // create UDP socket

View file

@ -296,7 +296,7 @@ FONT 8, "MS Sans Serif"
BEGIN BEGIN
ICON IDI_EMU48,IDC_STATIC,7,6,20,20,SS_REALSIZEIMAGE ICON IDI_EMU48,IDC_STATIC,7,6,20,20,SS_REALSIZEIMAGE
LTEXT "",IDC_VERSION,29,6,151,8,NOT WS_GROUP LTEXT "",IDC_VERSION,29,6,151,8,NOT WS_GROUP
LTEXT "Copyright © 2013 Christoph Gießelink && Sébastien Carlier", LTEXT "Copyright © 2014 Christoph Gießelink && Sébastien Carlier",
IDC_STATIC,29,18,181,8 IDC_STATIC,29,18,181,8
DEFPUSHBUTTON "OK",IDOK,215,12,39,14 DEFPUSHBUTTON "OK",IDOK,215,12,39,14
EDITTEXT IDC_LICENSE,7,33,247,112,ES_MULTILINE | ES_AUTOHSCROLL | EDITTEXT IDC_LICENSE,7,33,247,112,ES_MULTILINE | ES_AUTOHSCROLL |
@ -715,8 +715,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,5,5,0 FILEVERSION 1,5,6,0
PRODUCTVERSION 1,5,5,0 PRODUCTVERSION 1,5,6,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -733,12 +733,12 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Christoph Gießelink & Sebastien Carlier\0" VALUE "CompanyName", "Christoph Gießelink & Sebastien Carlier\0"
VALUE "FileDescription", "HP38/39/40/48/49 Emulator\0" VALUE "FileDescription", "HP38/39/40/48/49 Emulator\0"
VALUE "FileVersion", "1, 5, 5, 0\0" VALUE "FileVersion", "1, 5, 6, 0\0"
VALUE "InternalName", "Emu48\0" VALUE "InternalName", "Emu48\0"
VALUE "LegalCopyright", "Copyright © 2013\0" VALUE "LegalCopyright", "Copyright © 2014\0"
VALUE "OriginalFilename", "Emu48.exe\0" VALUE "OriginalFilename", "Emu48.exe\0"
VALUE "ProductName", "Emu48\0" VALUE "ProductName", "Emu48\0"
VALUE "ProductVersion", "1, 5, 5, 0\0" VALUE "ProductVersion", "1, 5, 6, 0\0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -45,10 +45,10 @@ cleanobj:
cleanexe: cleanexe:
-rm -f $(TARGET) -rm -f $(TARGET)
cursor.o: cursor.c pch.h emu48.h cursor.o: cursor.c pch.h emu48.h types.h
$(CC) $(CFLAGS) $(DEFINES) -c -o cursor.o cursor.c $(CC) $(CFLAGS) $(DEFINES) -c -o cursor.o cursor.c
ddeserv.o: ddeserv.c pch.h emu48.h types.h ddeserv.o: ddeserv.c pch.h emu48.h types.h io.h
$(CC) $(CFLAGS) $(DEFINES) -c -o ddeserv.o ddeserv.c $(CC) $(CFLAGS) $(DEFINES) -c -o ddeserv.o ddeserv.c
debugger.o: debugger.c pch.h resource.h emu48.h \ debugger.o: debugger.c pch.h resource.h emu48.h \
@ -65,7 +65,7 @@ display.o: display.c pch.h resource.h emu48.h \
types.h io.h kml.h types.h io.h kml.h
$(CC) $(CFLAGS) $(DEFINES) -c -o display.o display.c $(CC) $(CFLAGS) $(DEFINES) -c -o display.o display.c
disrpl.o: disrpl.c pch.h Emu48.h disrpl.h disrpl.o: disrpl.c pch.h Emu48.h types.h disrpl.h
$(CC) $(CFLAGS) $(DEFINES) -c -o disrpl.o disrpl.c $(CC) $(CFLAGS) $(DEFINES) -c -o disrpl.o disrpl.c
emu48.o: emu48.c pch.h resource.h emu48.h types.h \ emu48.o: emu48.c pch.h resource.h emu48.h types.h \
@ -73,16 +73,16 @@ emu48.o: emu48.c pch.h resource.h emu48.h types.h \
$(CC) $(CFLAGS) $(DEFINES) -c -o emu48.o emu48.c $(CC) $(CFLAGS) $(DEFINES) -c -o emu48.o emu48.c
engine.o: engine.c pch.h emu48.h types.h opcodes.h \ engine.o: engine.c pch.h emu48.h types.h opcodes.h \
io.h debugger.h ops.h io.h debugger.h
$(CC) $(CFLAGS) $(DEFINES) -c -o engine.o engine.c $(CC) $(CFLAGS) $(DEFINES) -c -o engine.o engine.c
external.o: external.c pch.h emu48.h types.h external.o: external.c pch.h emu48.h types.h ops.h
$(CC) $(CFLAGS) $(DEFINES) -c -o external.o external.c $(CC) $(CFLAGS) $(DEFINES) -c -o external.o external.c
fetch.o: fetch.c pch.h opcodes.h fetch.o: fetch.c pch.h opcodes.h
$(CC) $(CFLAGS) $(DEFINES) -c -o fetch.o fetch.c $(CC) $(CFLAGS) $(DEFINES) -c -o fetch.o fetch.c
files.o: files.c pch.h emu48.h types.h io.h \ files.o: files.c pch.h emu48.h types.h ops.h io.h \
kml.h i28f160.h debugger.h kml.h i28f160.h debugger.h
$(CC) $(CFLAGS) $(DEFINES) -c -o files.o files.c $(CC) $(CFLAGS) $(DEFINES) -c -o files.o files.c
@ -92,33 +92,33 @@ i28f160.o: i28f160.c pch.h emu48.h types.h i28f160.h
keyboard.o: keyboard.c pch.h emu48.h types.h io.h keyboard.o: keyboard.c pch.h emu48.h types.h io.h
$(CC) $(CFLAGS) $(DEFINES) -c -o keyboard.o keyboard.c $(CC) $(CFLAGS) $(DEFINES) -c -o keyboard.o keyboard.c
keymacro.o: keymacro.c pch.h resource.h Emu48.h kml.h keymacro.o: keymacro.c pch.h resource.h Emu48.h types.h kml.h
$(CC) $(CFLAGS) $(DEFINES) -c -o keymacro.o keymacro.c $(CC) $(CFLAGS) $(DEFINES) -c -o keymacro.o keymacro.c
kml.o: kml.c pch.h resource.h emu48.h types.h kml.h kml.o: kml.c pch.h resource.h emu48.h types.h kml.h
$(CC) $(CFLAGS) $(DEFINES) -c -o kml.o kml.c $(CC) $(CFLAGS) $(DEFINES) -c -o kml.o kml.c
lowbat.o: lowbat.c pch.h Emu48.h io.h lowbat.o: lowbat.c pch.h emu48.h types.h io.h
$(CC) $(CFLAGS) $(DEFINES) -c -o lowbat.o lowbat.c $(CC) $(CFLAGS) $(DEFINES) -c -o lowbat.o lowbat.c
mops.o: mops.c pch.h emu48.h types.h opcodes.h io.h \ mops.o: mops.c pch.h emu48.h types.h ops.h opcodes.h io.h \
i28f160.h i28f160.h
$(CC) $(CFLAGS) $(DEFINES) -c -o mops.o mops.c $(CC) $(CFLAGS) $(DEFINES) -c -o mops.o mops.c
mru.o: mru.c pch.h resource.h emu48.h mru.o: mru.c pch.h resource.h emu48.h types.h
$(CC) $(CFLAGS) $(DEFINES) -c -o mru.o mru.c $(CC) $(CFLAGS) $(DEFINES) -c -o mru.o mru.c
opcodes.o: opcodes.c pch.h emu48.h types.h opcodes.h \ opcodes.o: opcodes.c pch.h emu48.h types.h opcodes.h \
io.h ops.h io.h
$(CC) $(CFLAGS) $(DEFINES) -c -o opcodes.o opcodes.c $(CC) $(CFLAGS) $(DEFINES) -c -o opcodes.o opcodes.c
# pch.o: pch.c pch.h # pch.o: pch.c pch.h
# $(CC) $(CFLAGS) $(DEFINES) -c -o pch.o pch.c # $(CC) $(CFLAGS) $(DEFINES) -c -o pch.o pch.c
redeye.o: redeye.c pch.h emu48.h io.h redeye.o: redeye.c pch.h emu48.h types.h io.h
$(CC) $(CFLAGS) $(DEFINES) -c -o redeye.o redeye.c $(CC) $(CFLAGS) $(DEFINES) -c -o redeye.o redeye.c
rpl.o: rpl.c pch.h emu48.h types.h io.h rpl.o: rpl.c pch.h emu48.h types.h ops.h io.h
$(CC) $(CFLAGS) $(DEFINES) -c -o rpl.o rpl.c $(CC) $(CFLAGS) $(DEFINES) -c -o rpl.o rpl.c
serial.o: serial.c pch.h emu48.h types.h io.h serial.o: serial.c pch.h emu48.h types.h io.h
@ -127,19 +127,19 @@ serial.o: serial.c pch.h emu48.h types.h io.h
settings.o: settings.c pch.h emu48.h types.h i28f160.h settings.o: settings.c pch.h emu48.h types.h i28f160.h
$(CC) $(CFLAGS) $(DEFINES) -c -o settings.o settings.c $(CC) $(CFLAGS) $(DEFINES) -c -o settings.o settings.c
sound.o: sound.c pch.h emu48.h sound.o: sound.c pch.h emu48.h types.h
$(CC) $(CFLAGS) $(DEFINES) -c -o sound.o sound.c $(CC) $(CFLAGS) $(DEFINES) -c -o sound.o sound.c
stack.o: stack.c pch.h resource.h emu48.h io.h stack.o: stack.c pch.h resource.h emu48.h types.h io.h
$(CC) $(CFLAGS) $(DEFINES) -c -o stack.o stack.c $(CC) $(CFLAGS) $(DEFINES) -c -o stack.o stack.c
symbfile.o: symbfile.c pch.h emu48.h symbfile.o: symbfile.c pch.h emu48.h types.h
$(CC) $(CFLAGS) $(DEFINES) -c -o symbfile.o symbfile.c $(CC) $(CFLAGS) $(DEFINES) -c -o symbfile.o symbfile.c
timer.o: timer.c pch.h emu48.h types.h io.h timer.o: timer.c pch.h emu48.h types.h ops.h io.h
$(CC) $(CFLAGS) $(DEFINES) -c -o timer.o timer.c $(CC) $(CFLAGS) $(DEFINES) -c -o timer.o timer.c
udp.o: udp.c pch.h io.h udp.o: udp.c pch.h emu48.h types.h
$(CC) $(CFLAGS) $(DEFINES) -c -o udp.o udp.c $(CC) $(CFLAGS) $(DEFINES) -c -o udp.o udp.c
$(RSRCOBJ): $(RSRC) resource.h emu48.ico dbgtool.bmp checkbox.bmp Emu48.xml $(RSRCOBJ): $(RSRC) resource.h emu48.ico dbgtool.bmp checkbox.bmp Emu48.xml

View file

@ -18,14 +18,6 @@
#include <conio.h> #include <conio.h>
// #include <crtdbg.h> // missing file // #include <crtdbg.h> // missing file
#if !defined VERIFY
#if defined _DEBUG
#define VERIFY(f) _ASSERT(f)
#else // _DEBUG
#define VERIFY(f) ((VOID)(f))
#endif // _DEBUG
#endif // _VERIFY
#if !defined IDC_HAND // Win2k specific definition #if !defined IDC_HAND // Win2k specific definition
#define IDC_HAND MAKEINTRESOURCE(32649) #define IDC_HAND MAKEINTRESOURCE(32649)
#endif #endif
@ -38,12 +30,16 @@
#define __min(a,b) (((a) < (b)) ? (a) : (b)) #define __min(a,b) (((a) < (b)) ? (a) : (b))
#endif #endif
// normally defined in CONIO.H
int __cdecl _inp(unsigned short);
int __cdecl _outp(unsigned short, int);
// normally defined in missing CRTDBG.H // normally defined in missing CRTDBG.H
#if !defined _ASSERT #if !defined _ASSERT
#define _ASSERT(a) #define _ASSERT(a)
#endif #endif
#define _CrtDumpMemoryLeaks() ((int)0) #define _CrtDumpMemoryLeaks()
#if !defined VERIFY
#if defined _DEBUG
#define VERIFY(f) _ASSERT(f)
#else // _DEBUG
#define VERIFY(f) ((VOID)(f))
#endif // _DEBUG
#endif // VERIFY

View file

@ -48,4 +48,4 @@ Many thanks to Pedro A. Arranda Guti
compatible. compatible.
04/30/13 (c) by Christoph Gießelink 04/29/14 (c) by Christoph Gießelink

Binary file not shown.