/**************************************************************************************\
*                                                                                      *
*               The Lisa Emulator Project  V1.0 BETA REL 2007.03.01                    *
*                             http://lisaem.sunder.net                                 *
*                                                                                      *
*                  Copyright (C) 1998, 2007 Ray A. Arachelian                          *
*                                All Rights Reserved                                   *
*                                                                                      *
*           This program is free software; you can redistribute it and/or              *
*           modify it under the terms of the GNU General Public License                *
*           as published by the Free Software Foundation; either version 2             *
*           of the License, or (at your option) any later version.                     *
*                                                                                      *
*           This program is distributed in the hope that it will be useful,            *
*           but WITHOUT ANY WARRANTY; without even the implied warranty of             *
*           MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
*           GNU General Public License for more details.                               *
*                                                                                      *
*           You should have received a copy of the GNU General Public License          *
*           along with this program;  if not, write to the Free Software               *
*           Foundation, Inc., 59 Temple Place #330, Boston, MA 02111-1307, USA.        *
*                                                                                      *
*                   or visit: http://www.gnu.org/licenses/gpl.html                     *
*                                                                                      *
\**************************************************************************************/

#define IWINSIZEX 1020
#define IWINSIZEY 720

#define IWINSIZE IWINSIZEX,IWINSIZEY

#define ISKINSIZEX 1485
#define ISKINSIZEY 1031
#define ISKINSIZE  ISKINSIZEX,ISKINSIZEY

#define FLOPPY_LEFT 1099
#define FLOPPY_TOP 481

#define POWER_LEFT 0
#define POWER_TOP 738

#define WINXDIFF 20
#define WINYDIFF 60


#include <wx/wx.h>
#include <wx/defs.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/dcbuffer.h>
#include <wx/wxhtml.h>
#include <wx/fs_zip.h>
#include <wx/wfstream.h>
#include <wx/log.h>
#include <wx/filedlg.h>
#include <wx/fileconf.h>
#include <wx/statusbr.h>
#include <wx/scrolwin.h>
#include <wx/sound.h>
#include <wx/config.h>
#include <wx/clipbrd.h>
#include <wx/datetime.h>
#include <wx/stopwatch.h>
#include <wx/display.h>
#include <wx/gdicmn.h>
#include <wx/pen.h>
#include <wx/scrolwin.h>
#include <wx/notebook.h>
#include <wx/aboutdlg.h>
#include <wx/stdpaths.h>
#include <wx/choicdlg.h>


#include "machine.h"
#include "keyscan.h"

#include "LisaConfig.h"
#include "LisaConfigFrame.h"

// sounds, images, etc.
#include "lisaem_static_resources.h"

#ifdef __WXOSX__
#include <CoreFoundation/CoreFoundation.h>
#include <sys/param.h>
#endif

//If enabled, it will compile in the emulator, otherwise it's just a standalone UI for testing the UI.
#define ENABLE_EMULATOR 1

#ifdef ENABLE_EMULATOR

#define MAXLOOPS 100


#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif

extern "C"
{
 #include "vars.h"
 int32 reg68k_external_execute(void);
 void unvars(void);

}
#endif

// fwd references.
extern "C" int ImageWriter_LisaEm_Init(int iwnum);
extern "C" void iw_formfeed(int iw);
extern "C" void ImageWriterLoop(int iw,uint8 c);
extern "C" void iw_shutdown(void);

extern "C" void emulate (void);
extern "C" void XLLisaRedrawPixels(void);          // proto to supress warning below
extern "C" void LisaRedrawPixels(void);

extern "C" void resume_run(void);
extern "C" void pause_run(void);

extern "C" void force_refresh(void);


void turn_skins_on(void);
void turn_skins_off(void);

void powerbutton(void);
void setvideomode(int mode);

int asciikeyboard=1;

/*************************************************************************************************************************************/

// these need to be removed and autodected based on ROM
#ifdef ENABLE_EMULATOR

 extern "C" int cpu68k_init(void);
 extern "C" void cpu68k_reset(void);
 extern "C" uint8 evenparity(uint8 data);

#else
 int contrast=0x80;
 int lisa_vid_size_x=720;
 int lisa_vid_size_y=364;
 unsigned char volume=0x02;         // from
 unsigned char t2=0x60;             //0x60/20 // Lisa

#endif

void disable_display_menu(void);
void enable_display_menu(void);

int effective_lisa_vid_size_y=500; //364;
int effective_lisa_vid_size_x=720;

wxPaintEvent nada;
void black(void);


// these need to be inside some sort of skin.rc file.
int screen_origin_x=140;
int screen_origin_y=130;



wxConfigBase      *myConfig;             // default configuration (just a pointer to the active config)
wxString           myconfigfile;         // config filename
wxFileStream      *pConfigIS;            // config file


LisaConfigFrame  *my_LisaConfigFrame=NULL;

void updateThrottleMenus(float throttle);
/*************************************************************************************************************************************/


// Declare the application class
class LisaEmApp : public wxApp
{
public:
    // Called on application startup
    virtual bool OnInit();
    #ifndef __WXOSX__
    void OnQuit(wxCommandEvent& event);
    #endif
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*
 * The repainting logic needs a rototill, but for now add some constants
 * and some notes so I can try to understand what's going on exactly. This
 * sorely needs a revisit before the public source code release.
 *
 * The current repaint logic is based on the idea that the skin faces are
 * blitted onto a skin DC, then if any of the emulated floppy, power, or
 * video state change, bitmaps representing those changes are blitted onto
 * the appropriate location in the skin; then the entire skin is redrawn in
 * our window.
 *
 * Not only does this have an extra layer of indirection, it also means that
 * comparitively small changes trigger a huge 1.5 megapixel blit. Many times
 * a second! A common case will be LOS redrawing a 16x16 pixel mouse cursor
 * that just moved! ++ungood. Moving 4.5MB around just for that seems a bit
 * much. At the very least, in that case we should constrain the skin->window
 * blits to the video rectangle alone.
 *
 * There's also duplication or at least some overlap between, for ex,
 * REPAINT_POWER_TO_SKIN and POWER_NEEDS_REDRAW.
 *
 * The better way to do this is to use the skin as a backing store for
 * lisaface*.png and nothing more. If the GUI tells us that some of the
 * window is invalid (because, say, another window on top of it was moved)
 * and needs the skin to be redrawn on it, *then* we blit the skin (and
 * video, power, floppy on top of it).
 *
 * When we want to refresh a region of the window because the emu state has
 * changed (eg screen update, floppy insert), we'll mark it as an invalid
 * rectangle (using the GUI's APIs). When the GUI asks us to refresh the
 * window, then we do.
 *
 * This has the nice side effect of not redrawing the window more often
 * than the host hardware can handle.
 */

#define FLOPPY_NEEDS_REDRAW    0x80
#define FLOPPY_ANIMATING       0x40
#define FLOPPY_INSERT_0        0x00
#define FLOPPY_INSERT_1        0x01
#define FLOPPY_INSERT_2        0x02
#define FLOPPY_PRESENT         0x03
#define FLOPPY_EMPTY           0x04
#define FLOPPY_ANIM_MASK       0x07

#define POWER_NEEDS_REDRAW     0x80
#define POWER_UNKNOWN          0x40
#define POWER_ON_MASK          0x01
#define POWER_ON               0x01
#define POWER_OFF              0x00

#define REPAINT_INVALID_WINDOW 0x80
#define REPAINT_POWER_TO_SKIN  0x04
#define REPAINT_FLOPPY_TO_SKIN 0x02
#define REPAINT_VIDEO_TO_SKIN  0x01
#define REPAINT_NOTHING        0x00

class LisaWin : public wxScrolledWindow
{
public:
    // constructors
    LisaWin(wxWindow *parent);
    ~LisaWin();

    int doubley;          // this doubles the number of vertical lines

    int dirtyscreen;      // indicated dirty lisa vidram

    uint8 brightness;

    void SetStatusBarText(wxString &msg);

    int refresh_bytemap;  // flag indicating contrast change
    int floppystate;      // animation state of floppy
    int powerstate;       // animation state of power button

     void RePaint_AAGray(void);
     void RePaint_AntiAliased(void);
     void RePaint_DoubleY(void);
     void RePaint_SingleY(void);
     //void RePaint_Scaled(void);
     void RePaint_3A(void);
     void RePaint_2X3Y(void);

     void (LisaWin::*RePainter)(void);   // pointer method to one of the above


     void SetVideoMode(int mode);

     void OnPaint(wxPaintEvent &event);
     void OnErase(wxEraseEvent &event);

     void OnMouseMove(wxMouseEvent &event);
     void OnKeyDown(wxKeyEvent& event);
     void OnKeyUp(wxKeyEvent& event);
     void OnChar(wxKeyEvent& event);

     void LogKeyEvent(const wxChar *name, wxKeyEvent& event,int keydir);
     void ContrastChange(void);

     int lastkeystroke;


     int repaintall;

private:
     int lastkeyevent;
     wxScrolledWindow *myparent;
     wxCursor *m_dot_cursor;

     static inline wxChar GetChar(bool on, wxChar c) { return on ? c : _T('-'); }

     DECLARE_EVENT_TABLE()
};



BEGIN_EVENT_TABLE(LisaWin, wxScrolledWindow)

    EVT_KEY_DOWN(LisaWin::OnKeyDown)
    EVT_KEY_UP(LisaWin::OnKeyUp)
    EVT_CHAR(LisaWin::OnChar)

    EVT_ERASE_BACKGROUND(LisaWin::OnErase)
    EVT_PAINT(LisaWin::OnPaint)

    EVT_MOTION(LisaWin::OnMouseMove)
    EVT_LEFT_DOWN(LisaWin::OnMouseMove)
    EVT_LEFT_UP(LisaWin::OnMouseMove)
    EVT_RIGHT_DOWN(LisaWin::OnMouseMove)
    EVT_RIGHT_UP(LisaWin::OnMouseMove)

END_EVENT_TABLE()




// Event table for LisaEmFrame
enum
{
    ID_SCREENSHOT=10001,           // anti-aliased screenshot
    ID_SCREENSHOT2,                // screenshot with screen
    ID_SCREENSHOT3,                // raw screenshot - no aliasing

    ID_DEBUGGER,
    ID_POWERKEY,
    ID_KEY_APL_DOT,
    ID_KEY_APL_S,
    ID_KEY_APL_ENTER,
    ID_KEY_APL_RENTER,
    ID_KEY_APL_1,
    ID_KEY_APL_2,
    ID_KEY_APL_3,
    ID_KEY_NMI,

    ID_PROFILEPWR,
    ID_FLOPPY,
    ID_NewFLOPPY,

    ID_RUN,
    ID_PAUSE,

    ID_KEY_OPT_0,
    ID_KEY_OPT_4,
    ID_KEY_OPT_7,
    ID_KEYBOARD,
    ID_ASCIIKB,
    ID_RAWKB,

    ID_THROTTLE5,
    ID_THROTTLE8,
    ID_THROTTLE10,
    ID_THROTTLE12,
    ID_THROTTLE16,
    ID_THROTTLE32,


    ID_LISAWEB,
    ID_LISAFAQ,

    ID_VID_AA,
    ID_VID_AAG,
    //ID_VID_SCALED,
    ID_VID_DY,
    ID_VID_SY,
    ID_VID_2X3Y,


    ID_VID_SKINS_ON,
    ID_VID_SKINS_OFF
};

// Declare our main frame class




class LisaEmFrame : public wxFrame
{
public:

    int running;          // is the Lisa running?  0=off, 1=running, 10=paused/locked.

    // Constructor
    LisaEmFrame(const wxString& title);

    void LoadImages(void);
    void UnloadImages(void);

    // Event handlers
    #ifdef __WXOSX__
    void OnQuit(wxCommandEvent& event);
    #endif
    void OnAbout(wxCommandEvent& event);

    void OnLisaWeb(wxCommandEvent& event);
    void OnLisaFaq(wxCommandEvent& event);



    void OnConfig(wxCommandEvent& event);
    void OnOpen(wxCommandEvent& event);
    void OnSaveAs(wxCommandEvent& event);


    void OnRun(wxCommandEvent& event);
    void OnPause(wxCommandEvent& event);

    void OnScreenshot(wxCommandEvent& event);
    void OnDebugger(wxCommandEvent& event);
    void OnPOWERKEY(wxCommandEvent& event);
    void OnKEY_APL_DOT(wxCommandEvent& event);
    void OnKEY_APL_S(wxCommandEvent& event);
    void OnKEY_APL_ENTER(wxCommandEvent& event);
    void OnKEY_APL_RENTER(wxCommandEvent& event);
    void OnKEY_APL_1(wxCommandEvent& event);
    void OnKEY_APL_2(wxCommandEvent& event);
    void OnKEY_APL_3(wxCommandEvent& event);
    void OnKEY_NMI(wxCommandEvent& event);
    void OnProFilePower(wxCommandEvent& event);
    void OnFLOPPY(wxCommandEvent& event);
    void OnNewFLOPPY(wxCommandEvent& event);
    void OnKEY_OPT_0(wxCommandEvent& event);
    void OnKEY_OPT_4(wxCommandEvent& event);
    void OnKEY_OPT_7(wxCommandEvent& event);
    void OnKEYBOARD(wxCommandEvent& event);

    void OnASCIIKB(wxCommandEvent& event);
    void OnRAWKB(wxCommandEvent& event);

    void OnTHROTTLE5(wxCommandEvent& event);
    void OnTHROTTLE8(wxCommandEvent& event);
    void OnThrottle10(wxCommandEvent& event);
    void OnThrottle12(wxCommandEvent& event);
    void OnThrottle16(wxCommandEvent& event);
    void OnThrottle32(wxCommandEvent& event);



    void SetStatusBarText(wxString &msg);

    void OnPasteToKeyboard(wxCommandEvent&event);


    void OnIdleEvent(wxIdleEvent& event);

    // menu commands that switch video mode
    void OnVideoAntiAliased(wxCommandEvent& event);
    void OnVideoAAGray(wxCommandEvent& event);
    //void OnVideoScaled(wxCommandEvent& event);
    void OnVideoDoubleY(wxCommandEvent& event);
    void OnVideoSingleY(wxCommandEvent& event);
    void OnVideo2X3Y(wxCommandEvent& event);

    void OnSkinsOn(wxCommandEvent& event);
    void OnSkinsOff(wxCommandEvent& event);

    // Grrr! this has been privateized as of 2.7!
    void mySetMinSize(wxSize size)    { SetMinSize(size);  }

    class LisaWin *win;

    int depth;

    wxStopWatch runtime;               // idle loop stopwatch
    wxStopWatch soundsw;
    int soundplaying;

    uint16 lastt2;
    XTIMER lastclk;



    wxString     wspaste_to_keyboard;
    volatile char *paste_to_keyboard;
    uint32         idx_paste_to_kb;
    int            loops;
    float          throttle;
    float          clockfactor;
    float          mhzactual;

    wxString floppy_to_insert;


    DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(LisaEmFrame, wxFrame)


    EVT_MENU(wxID_ABOUT,        LisaEmFrame::OnAbout)


    EVT_MENU(ID_LISAWEB,        LisaEmFrame::OnLisaWeb)
    EVT_MENU(ID_LISAFAQ,        LisaEmFrame::OnLisaFaq)


    EVT_MENU(wxID_PREFERENCES,  LisaEmFrame::OnConfig)
    EVT_MENU(wxID_OPEN,         LisaEmFrame::OnOpen)
    EVT_MENU(wxID_SAVEAS,       LisaEmFrame::OnSaveAs)

    EVT_MENU(ID_SCREENSHOT,     LisaEmFrame::OnScreenshot)
    EVT_MENU(ID_SCREENSHOT2,    LisaEmFrame::OnScreenshot)
    EVT_MENU(ID_SCREENSHOT3,    LisaEmFrame::OnScreenshot)


    EVT_MENU(ID_DEBUGGER,       LisaEmFrame::OnDebugger)
    EVT_MENU(ID_POWERKEY,       LisaEmFrame::OnPOWERKEY)
    EVT_MENU(ID_KEY_APL_DOT,    LisaEmFrame::OnKEY_APL_DOT)
    EVT_MENU(ID_KEY_APL_S,      LisaEmFrame::OnKEY_APL_S)
    EVT_MENU(ID_KEY_APL_ENTER,  LisaEmFrame::OnKEY_APL_ENTER)
    EVT_MENU(ID_KEY_APL_RENTER, LisaEmFrame::OnKEY_APL_RENTER)
    EVT_MENU(ID_KEY_APL_1,      LisaEmFrame::OnKEY_APL_1)
    EVT_MENU(ID_KEY_APL_2,      LisaEmFrame::OnKEY_APL_2)
    EVT_MENU(ID_KEY_APL_3,      LisaEmFrame::OnKEY_APL_3)
    EVT_MENU(ID_KEY_NMI,        LisaEmFrame::OnKEY_NMI)
    EVT_MENU(ID_PROFILEPWR,     LisaEmFrame::OnProFilePower)
    EVT_MENU(ID_FLOPPY,         LisaEmFrame::OnFLOPPY)
    EVT_MENU(ID_NewFLOPPY,      LisaEmFrame::OnNewFLOPPY)

    EVT_MENU(ID_KEY_OPT_0,      LisaEmFrame::OnKEY_OPT_0)
    EVT_MENU(ID_KEY_OPT_4,      LisaEmFrame::OnKEY_OPT_4)
    EVT_MENU(ID_KEY_OPT_7,      LisaEmFrame::OnKEY_OPT_7)

    EVT_MENU(ID_KEYBOARD,       LisaEmFrame::OnKEYBOARD)
    EVT_MENU(ID_ASCIIKB,        LisaEmFrame::OnASCIIKB)
    EVT_MENU(ID_RAWKB,          LisaEmFrame::OnRAWKB)

    EVT_MENU(ID_THROTTLE5,      LisaEmFrame::OnTHROTTLE5)
    EVT_MENU(ID_THROTTLE8,      LisaEmFrame::OnTHROTTLE8)
    EVT_MENU(ID_THROTTLE10,     LisaEmFrame::OnThrottle10)
    EVT_MENU(ID_THROTTLE12,     LisaEmFrame::OnThrottle12)
    EVT_MENU(ID_THROTTLE16,     LisaEmFrame::OnThrottle16)
    EVT_MENU(ID_THROTTLE32,     LisaEmFrame::OnThrottle32)


    EVT_MENU(wxID_PASTE,        LisaEmFrame::OnPasteToKeyboard)


    EVT_MENU(ID_VID_AA,         LisaEmFrame::OnVideoAntiAliased)
    EVT_MENU(ID_VID_AAG,        LisaEmFrame::OnVideoAAGray)

    //EVT_MENU(ID_VID_SCALED,     LisaEmFrame::OnVideoScaled)



    EVT_MENU(ID_VID_DY,         LisaEmFrame::OnVideoDoubleY)
    EVT_MENU(ID_VID_SY,         LisaEmFrame::OnVideoSingleY)
    EVT_MENU(ID_VID_2X3Y,       LisaEmFrame::OnVideo2X3Y)

    EVT_MENU(ID_VID_SKINS_ON,   LisaEmFrame::OnSkinsOn)
    EVT_MENU(ID_VID_SKINS_OFF,  LisaEmFrame::OnSkinsOff)


    EVT_MENU(ID_RUN,            LisaEmFrame::OnRun)
    EVT_MENU(ID_PAUSE,          LisaEmFrame::OnPause)


    EVT_IDLE(LisaEmFrame::OnIdleEvent)
    #ifdef __WXOSX__
    EVT_MENU(wxID_EXIT,         LisaEmFrame::OnQuit)
    #else
    EVT_MENU(wxID_EXIT,         LisaEmApp::OnQuit)
    #endif




END_EVENT_TABLE()


// want to have these as globals so that they can be accessed by other fn's
// and passed around like a cheap 40oz bottle at a frat.

wxFileConfig     *pConfig =NULL;


wxMenu *fileMenu     = NULL;
wxMenu *editMenu     = NULL;
wxMenu *keyMenu      = NULL;
wxMenu *DisplayMenu  = NULL;
wxMenu *throttleMenu = NULL;
wxMenu *helpMenu     = NULL;


LisaConfig       *my_lisaconfig=NULL;

LisaWin          *my_lisawin=NULL;
LisaEmFrame      *my_lisaframe=NULL;


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef __WXOSX__
/*
 * Returns the absolute path of the Resources directory for this application's
 * bundle. Used for loading images etc on Mac OS X.
 * NB Be sure to free the path returned by this once you're done with it!
 */
char *getResourcesDir(void)
{
    CFURLRef pathCFURLRef;
    CFStringRef pathCFString;
    char *path;
    int pathLen;

    path = (char *)malloc(MAXPATHLEN);
    pathCFURLRef = CFBundleCopyBundleURL(CFBundleGetMainBundle()); // Get path to application bundle.
    pathCFString = CFURLCopyFileSystemPath(pathCFURLRef, kCFURLPOSIXPathStyle);
    CFStringGetCString(pathCFString, path, MAXPATHLEN, CFStringGetSystemEncoding());
    CFRelease(pathCFString);
    CFRelease(pathCFURLRef);

    pathLen = strlen(path);
    path[pathLen++] = '/';

    pathCFURLRef = CFBundleCopyResourcesDirectoryURL (CFBundleGetMainBundle()); // Get relative path to resources directory.
    pathCFString = CFURLCopyFileSystemPath(pathCFURLRef, kCFURLPOSIXPathStyle);
    CFStringGetCString(pathCFString, path + pathLen, MAXPATHLEN, CFStringGetSystemEncoding());
    CFRelease(pathCFString);
    CFRelease(pathCFURLRef);

    return path;
}

#else

char *getResourcesDir(void)
{
    static char ret[1024];

    wxStandardPathsBase& stdp = wxStandardPaths::Get();
    strncpy(ret,stdp.GetResourcesDir().c_str(),1024);

    return ret;
}

#endif


char *getDocumentsDir(void)
{
    static char ret[1024];

    wxStandardPathsBase& stdp = wxStandardPaths::Get();
    strncpy(ret,stdp.GetDocumentsDir().c_str(),1024);

    return ret;
}

// does not exist in wx2.7.x //
/*char *getExecutablePath(void)
{
    static char ret[1024];

    wxStandardPathsBase& stdp = wxStandardPaths::Get();
    strncpy(ret,stdp.GetExecutablePath().c_str(),1024);

    return ret;
}
*/

wxMenuBar        *menuBar = NULL;

wxBitmap         *my_lisabitmap=NULL;
wxMemoryDC       *my_memDC=NULL;

wxSound          *my_lisa_sound=NULL;       // sounds generated by Lisa Speaker

wxSound          *my_floppy_eject=NULL;
wxSound          *my_floppy_insert=NULL;
wxSound          *my_floppy_motor1=NULL;
wxSound          *my_floppy_motor2=NULL;
wxSound          *my_lisa_power_switch01=NULL;
wxSound          *my_lisa_power_switch02=NULL;
wxSound          *my_poweroffclk=NULL;




// byte bitmap used to accelerate writes to videoram
wxBitmap  *my_bytemap=NULL;    wxMemoryDC *my_bytemapDC =NULL;
wxBitmap  *my_alias  =NULL;    wxMemoryDC *my_aliasDC   =NULL;

wxBitmap  *my_skin   =NULL;    wxMemoryDC *my_skinDC    =NULL; // bug with wxX11 turns background black, this attempts
wxBitmap  *my_skin0  =NULL;    wxMemoryDC *my_skin0DC   =NULL; // to split the img in two hoping that it will help.
wxBitmap  *my_skin1  =NULL;    wxMemoryDC *my_skin1DC   =NULL;
wxBitmap  *my_skin2  =NULL;    wxMemoryDC *my_skin2DC   =NULL;
wxBitmap  *my_skin3  =NULL;    wxMemoryDC *my_skin3DC   =NULL;

wxBitmap  *my_floppy0=NULL;    wxMemoryDC *my_floppy0DC =NULL;
wxBitmap  *my_floppy1=NULL;    wxMemoryDC *my_floppy1DC =NULL;
wxBitmap  *my_floppy2=NULL;    wxMemoryDC *my_floppy2DC =NULL;
wxBitmap  *my_floppy3=NULL;    wxMemoryDC *my_floppy3DC =NULL;
wxBitmap  *my_floppy4=NULL;    wxMemoryDC *my_floppy4DC=NULL;

wxBitmap  *my_poweron  =NULL;  wxMemoryDC *my_poweronDC =NULL;
wxBitmap  *my_poweroff =NULL;  wxMemoryDC *my_poweroffDC=NULL;


// display lens
static wxCoord screen_y_map[364];
static wxCoord screen_to_mouse[502];
static int     screen_focus[502];       // how close to the ideal pixel we are
static int     yoffset[364];

void disable_display_menu(void)   {}//{if (!menuBar) return; DisplayMenu->Enable(i,false);}
void enable_display_menu(void)    {}//{if (!menuBar) return; DisplayMenu->Enable(i,true );}

void buildscreenymap(void)
{
 float f,yf, ratio=364.0/500.0;
 int i,yy;
 wxCoord y;


 for (y=0; y<364; y++) yoffset[y]=y*90;


 for (f=0.0,i=0; i<502; i++,f+=1.0)
     {
       yf=(f*ratio);
       yy=(int)(yf); y=(wxCoord)yy;

       screen_focus[i]=((int)(yf*10)) % 10;

       y=(y<499) ? y:499;
       screen_to_mouse[i]=y;
          screen_y_map[y]=i;
     }

}

void buildscreenymap_raw(void)
{
 wxCoord y;


 for (y=0; y<364; y++)
     {
       yoffset[y]=y*90;

       screen_focus[y]=0;

       screen_to_mouse[y]=y;
          screen_y_map[y]=y;
     }

}

void buildscreenymap_3A(void)
{
 wxCoord y;


 for (y=0; y<431; y++)
     {
       yoffset[y]=y*76;

       screen_focus[y]=0;

       screen_to_mouse[y]=y;
          screen_y_map[y]=y;
     }

}


void buildscreenymap_2Y(void)
{
 wxCoord y;

 for (y=0; y<364; y++)   yoffset[y]=y*90;


 for (y=0; y<364*2; y++)
     {
       screen_focus[y]=0;

       screen_to_mouse[y]=y/2;
          screen_y_map[y*2]=y;
          screen_y_map[y*2+1]=y;
     }

}


void buildscreenymap_3Y(void)
{
 wxCoord y;

 for (y=0; y<364; y++)   yoffset[y]=y*90;


 for (y=0; y<364*3; y++)
     {
       screen_focus[y]=0;

       screen_to_mouse[y]  =y/3;
          screen_y_map[y+0]=y/3;
          screen_y_map[y+1]=y/3;
          screen_y_map[y+2]=y/3;
     }

}


void LisaEmFrame::OnPasteToKeyboard(wxCommandEvent& WXUNUSED(event))
{
    wxTextDataObject data;
    if (wxTheClipboard->Open()) {
        if (wxTheClipboard->IsSupported(wxDF_TEXT)) {
            wxTheClipboard->GetData(data);
        }
        wxTheClipboard->Close();

        wxTheClipboard->UsePrimarySelection();

        if (wxTheClipboard->IsSupported(wxDF_TEXT)) {
            wspaste_to_keyboard = data.GetText();
            paste_to_keyboard = (char *)(wspaste_to_keyboard.c_str());
            idx_paste_to_kb = 0;
        }
    }
}

LisaWin::LisaWin(wxWindow *parent)
        : wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
                   wxRAISED_BORDER|wxVSCROLL|wxHSCROLL|wxWS_EX_PROCESS_IDLE
#ifdef wxALWAYS_SHOW_SB
|wxALWAYS_SHOW_SB
#endif
#ifdef wxMAXIMIZE
|wxMAXIMIZE
#endif
                                   )
{
       floppystate = FLOPPY_NEEDS_REDRAW | FLOPPY_EMPTY; // set floppy state and force refresh
       powerstate = POWER_NEEDS_REDRAW;                  // set power state and force refresh
       repaintall = REPAINT_NOTHING;

       SetExtraStyle(wxWS_EX_PROCESS_IDLE );

       // deprecated // SetInitialBestSize(wxSize(IWINSIZE));

       if (skins_on)
       {
         SetMinSize(wxSize(IWINSIZE));
         SetSize(wxSize(IWINSIZE));
         SetMaxSize(wxSize(ISKINSIZE));
         SetScrollbars(ISKINSIZEX/100, ISKINSIZEY/100,  100,100,  0,0,  true);
         EnableScrolling(true,true);

         screen_origin_x=140;
         screen_origin_y=130;
       }
       else
       {
         switch (lisa_ui_video_mode)
         {
                case 0:   effective_lisa_vid_size_x=720;   effective_lisa_vid_size_y=500;      break;
                case 1:   effective_lisa_vid_size_x=720;   effective_lisa_vid_size_y=364*2;    break;
                case 2:   effective_lisa_vid_size_x=720;   effective_lisa_vid_size_y=364;      break;
                case 3:   effective_lisa_vid_size_x=720*2; effective_lisa_vid_size_y=364*3;    break;
                case 4:   effective_lisa_vid_size_x=720;   effective_lisa_vid_size_y=500;      break;
                case 0x3a:effective_lisa_vid_size_x=608;   effective_lisa_vid_size_y=431;      break;
         }

         screen_origin_x=0;
         screen_origin_y=0;

         int x,y;
         SetClientSize(wxSize(effective_lisa_vid_size_x,effective_lisa_vid_size_y));
/*
         GetClientSize(&x,&y);
         if (x!=effective_lisa_vid_size_x || y!=effective_lisa_vid_size_y)
         {

            ALERT_LOG(0,"Client size I got back:%d,%d differs from %d,%d",x,y,
                           effective_lisa_vid_size_x,effective_lisa_vid_size_y);
         }
*/
         GetSize(&x,&y);
// X11, Windows return too small a value for getsize
#ifndef  __WXOSX__
         x+=WINXDIFF; y+=WINYDIFF;
#endif
         SetMaxSize(wxSize(x,y));
         SetMinSize(wxSize(x,y));
         EnableScrolling(false,false);

       }


       RePainter=&LisaWin::RePaint_AntiAliased;  //RePaint_AAGray;

       /* Draw the VNC-style dot cursor we use later */
#if defined(__WXX11__) || defined(__WXGTK__) || defined(__WXOSX__)

#ifdef __WXOSX__
       /* LSB (0x01) is leftmost. First byte is top row. 1 is black/visible*/
       char cursor_bits[] = {0x00, 0x0e, 0x0e, 0x0e, 0x00};
       char mask_bits[]   = {0x1f, 0x1f, 0x1f, 0x1f, 0x1f};
#else
       char cursor_bits[] = {0xe0, 0xee, 0xee, 0xee, 0xe0};
       char mask_bits[]   = {0xe0, 0xe0, 0xe0, 0xe0, 0xe0};
#endif

       wxBitmap bmp = wxBitmap(cursor_bits, 8, 5);
       bmp.SetMask(new wxMask(wxBitmap(mask_bits, 8, 5)));

       wxImage img = bmp.ConvertToImage();
       img.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, "2");
       img.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, "2");

       m_dot_cursor = new wxCursor(img);
#endif
}

LisaWin::~LisaWin(void)
{
#if defined(__WXX11__) || defined(__WXGTK__) || defined(__WXOSX__)
    delete m_dot_cursor;
#endif
}






void LisaEmFrame::OnVideoAntiAliased(wxCommandEvent& WXUNUSED(event))
{
    wxScreenDC theScreen;
    int x,y;
    theScreen.GetSize(&x,&y);
    if (x<IWINSIZEX || y<IWINSIZEY)
    {
        wxString msg;
        msg.Printf("Your display is only (%d,%d).  This mode needs at least (%d,%d), will shut off skins.",
                   x,y,  IWINSIZEX,IWINSIZEY);
        wxMessageBox(wxT(msg),wxT("The display is too small"));
        turn_skins_off();
        if (y<IWINSIZEY) {my_lisawin->SetVideoMode(2); return;}  // even still too small, go raw bits mode.
    }

    my_lisawin->SetVideoMode(0);
}
void LisaEmFrame::OnVideoAAGray(wxCommandEvent& WXUNUSED(event))
{
    wxScreenDC theScreen;
    int x,y;
    theScreen.GetSize(&x,&y);
    if (x<IWINSIZEX || y<IWINSIZEY)
    {
        wxString msg;
        msg.Printf("Your display is only (%d,%d).  This mode needs at least (%d,%d), will shut off skins.",
                   x,y,  IWINSIZEX,IWINSIZEY);
        wxMessageBox(wxT(msg),wxT("The display is too small"));
        turn_skins_off();
        if (y<IWINSIZEY) {my_lisawin->SetVideoMode(2); return;}
    }

    my_lisawin->SetVideoMode(4);
}
//void LisaEmFrame::OnVideoScaled(wxCommandEvent& WXUNUSED(event))          {my_lisawin->SetVideoMode(5);}
void LisaEmFrame::OnVideoSingleY(wxCommandEvent& WXUNUSED(event))         {my_lisawin->SetVideoMode(2);}


void LisaEmFrame::OnVideoDoubleY(wxCommandEvent& WXUNUSED(event))
{
    wxScreenDC theScreen;
    int x,y;
    theScreen.GetSize(&x,&y);
    if (x<720+40 || y<364*2+50)
    {
     wxString msg;
     msg.Printf("Your display is only (%d,%d).  This mode needs at least (%d,%d).",
                x,y,  720+40,364*2+50 );
        wxMessageBox(wxT(msg),wxT("The display is too small"));

     // oops!  The display size changed on us since the last time. i.e. notebook with small screen
     // previously connected to large monitor, now on native LCD
     if (lisa_ui_video_mode==1 || lisa_ui_video_mode==3)
        {
          my_lisawin->SetVideoMode(2); turn_skins_off();
        }

     return;

    } /// display too small ///////////////////////////////////////////////////////////////

    if (skins_on)
    {
     if (yesnomessagebox(wxT("This mode doesn't work with the Lisa Skin.  Shut off the skin?"),
            wxT("Remove Skin?"))==0) return;
    }
    my_lisawin->SetVideoMode(1);
    turn_skins_off();
}


void LisaEmFrame::OnSkinsOn(wxCommandEvent& WXUNUSED(event))
{

  if (lisa_ui_video_mode==3 || lisa_ui_video_mode==2)
  {
   if (yesnomessagebox(wxT("The current display mode doesn't work with the Lisa Skin. Change modes?"),
            wxT("Change Display Mode?"))==0) return;

    setvideomode(0);
  }

  skins_on_next_run=1;
  skins_on=1;
  turn_skins_on();
}


void LisaEmFrame::OnSkinsOff(wxCommandEvent& WXUNUSED(event))
{
 skins_on_next_run=0;
 skins_on=0;
 turn_skins_off();
}


void LisaEmFrame::OnVideo2X3Y(wxCommandEvent& WXUNUSED(event))
{
    wxScreenDC theScreen;
    int x,y;
    theScreen.GetSize(&x,&y);
    if (x<720*2+40 || y<364*3+100)
    {
     wxString msg;
     msg.Printf("Your display is only (%d,%d).  This mode needs at least (%d,%d).",
                x,y,  720*2+40,364*3+100 );
        wxMessageBox(wxT(msg),wxT("The display is too small"));

     // oops!  The display size changed on us since the last time. i.e. notebook with small screen
     // previously connected to large monitor, now on native LCD
     if (lisa_ui_video_mode==1 || lisa_ui_video_mode==3)
        {
          my_lisawin->SetVideoMode(2);
          turn_skins_off();
        }


     return;
    }

    if (skins_on)
    {
     if (yesnomessagebox(wxT("This mode doesn't work with the Lisa Skin.  Shut off the skin?"),
            wxT("Remove Skin?"))==0) return;
    }

    my_lisawin->SetVideoMode(3);
    turn_skins_off();
}



void LisaWin::SetVideoMode(int mode)
{
  //int i;
  black();

  if ( has_lisa_xl_screenmod)   mode= 0x3a;               // sanity fixes
  if (!has_lisa_xl_screenmod && mode==0x3a) mode=0;
  if (mode!=0x3a) lisa_ui_video_mode=mode;


  DisplayMenu->Check(ID_VID_AA    ,mode==0);  DisplayMenu->Enable(ID_VID_AA    ,mode!=0x3a);
  DisplayMenu->Check(ID_VID_AAG   ,mode==4);  DisplayMenu->Enable(ID_VID_AAG   ,mode!=0x3a);
  //DisplayMenu->Check(ID_VID_SCALED,mode==5);  DisplayMenu->Enable(ID_VID_SCALED,mode!=0x3a);
  DisplayMenu->Check(ID_VID_DY    ,mode==1);  DisplayMenu->Enable(ID_VID_DY    ,mode!=0x3a);
  DisplayMenu->Check(ID_VID_SY    ,mode==2);  DisplayMenu->Enable(ID_VID_SY    ,mode!=0x3a);
  DisplayMenu->Check(ID_VID_2X3Y  ,mode==3);  DisplayMenu->Enable(ID_VID_2X3Y  ,mode!=0x3a);

  my_memDC->SetUserScale( 1.0, 1.0);
  my_memDC->ComputeScaleAndOrigin();


  switch (mode)
  {

   case 0:   buildscreenymap()    ;  screen_origin_x=140; screen_origin_y=130; effective_lisa_vid_size_x=720; effective_lisa_vid_size_y=500;
             RePainter=&LisaWin::RePaint_AntiAliased;                                                                    break;

   case 1:   buildscreenymap_2Y() ;  screen_origin_x=  0; screen_origin_y=  0; effective_lisa_vid_size_x=720; effective_lisa_vid_size_y=364*2;
             RePainter=&LisaWin::RePaint_DoubleY;                                                                        break;

   case 2:   buildscreenymap_raw();  screen_origin_x=140; screen_origin_y=198; effective_lisa_vid_size_x=720; effective_lisa_vid_size_y=364;
             RePainter=&LisaWin::RePaint_SingleY;                                                                        break;

   case 3:   buildscreenymap_3Y() ;  screen_origin_x=  0; screen_origin_y=  0; effective_lisa_vid_size_x=720*2; effective_lisa_vid_size_y=364*3;
             RePainter=&LisaWin::RePaint_2X3Y;                                                                           break;

   case 4:   buildscreenymap()    ;  screen_origin_x=140; screen_origin_y=130; effective_lisa_vid_size_x=720; effective_lisa_vid_size_y=500;
             RePainter=&LisaWin::RePaint_AAGray;                                                                         break;

   //case 5:   buildscreenymap()    ;  screen_origin_x=140; screen_origin_y=130; effective_lisa_vid_size_x=720; effective_lisa_vid_size_y=500;
   //          RePainter=&LisaWin::RePaint_Scaled;                                                                         break;


   case 0x3a:buildscreenymap_3A();

             lisa_vid_size_x=608;
             lisa_vid_size_y=431;
             effective_lisa_vid_size_x=608;
             effective_lisa_vid_size_y=431;

             screen_origin_x=140+56;
             screen_origin_y=130+34;

             lisa_vid_size_x=608;
             lisa_vid_size_y=431;

             lisa_vid_size_xbytes=76;
             has_lisa_xl_screenmod=1;

             RePainter=&LisaWin::RePaint_3A;
             break;
  }

   delete my_lisabitmap;
   delete my_memDC;

   my_memDC     =new class wxMemoryDC;
   my_lisabitmap=new class wxBitmap(effective_lisa_vid_size_x, effective_lisa_vid_size_y);
   my_memDC->SelectObject(*my_lisabitmap);

   videoramdirty|=9;

   Refresh();
   Update();

   if (!skins_on) {screen_origin_x=0; screen_origin_y=0;}

   //RePaint(1, nada);
}



void LisaEmFrame::OnIdleEvent(wxIdleEvent& event)
{
  static int     screenrefresh;
  static long    delay;
  static long    last_runtime_sample;
  static  XTIMER last_runtime_cpu68k_clx;
#ifdef ENABLE_EMULATOR


   if (my_lisaframe->running==1)
   {
    XTIMER now;


     if (!last_runtime_sample )           // initialize slice timer
     {
        last_runtime_sample=my_lisaframe->runtime.Time();
        last_runtime_cpu68k_clx=cpu68k_clocks;
     }




    // deffered floppy insert.
    if (cpu68k_clocks<10 && floppy_to_insert.Len())
       {
           const char *s = floppy_to_insert.fn_str();
           floppy_insert((char *)s);
           floppy_to_insert="";
       }


    clktest = cpu68k_clocks;
    // ultimately I should hand unroll and make it into a big switch/case without breaks, so it'll fall through.

    long int elapsed;

    if (loops<2)        loops=2;          // limits
    if (loops>MAXLOOPS) loops=MAXLOOPS;

    for (int i=0; i<loops; i++)
    {

     long start=my_lisaframe->runtime.Time();
     XTIMER clx=cpu68k_clocks;

     reg68k_external_execute();

     if (loops & 1) {
                     seek_mouse_event();
                     if (floppy_6504_wait && floppy_6504_wait<128)
                                               floppy_6504_wait--;  // this is dumb! it should be a timer!
                    }

     get_next_timer_event();  //20070110//

    // refresh every X frames for efficiency.
    if ( (cpu68k_clocks-lastrefresh)>(REFRESHRATE)*3 && vertical)  //was *5
         {
            check_running_lisa_os();
            my_lisawin->dirtyscreen=1;
            screenrefresh++;
            if (videoramdirty) {Refresh(false,NULL); Update();}//my_lisawin->RePaint(0,nada);
            lastrefresh=cpu68k_clocks; videoramdirty=0;

         }

     // correct timing
     long end=my_lisaframe->runtime.Time();

     if (end-start>20)  // make sure we have a large enough sample.
        {
          delay=(long)( (double)(cpu68k_clocks-clx)*clockfactor )-(end-start);
          delay=(delay>300) ? 300:delay;
          delay=(delay<0)   ? 0  :delay;
        }

     if (delay) wxMilliSleep(delay);

    } // for loops ////////////////////////////////////////////////////////////////////////////////////////////////////////////



    // adjust timing.
    //elapsed=st.Time();                                      // in milliseconds  10^-3
    //if (elapsed>100)                                        // ensure that we have enough sample time to matter
    {

     elapsed=my_lisaframe->runtime.Time();                    // total milliseconds since startup
     now=(long)((double)(cpu68k_clocks)*clockfactor);         // turn 1/5MHz to milliseconds, use FPU to avoid int division slowdown

     if (now<elapsed && loops<MAXLOOPS)  loops++;
     if (now>elapsed && loops>1       )  loops--;

     if (now>elapsed                  )
        {
           wxMilliSleep( ((now-elapsed)<300)?(now-elapsed):300);  // don't sleep more than 300ms if we can help it
        }


     if (screenrefresh>4)
        {
             // sample MHz actual every X seconds
             // elapsed=my_lisaframe->runtime.Time();         don't erase "has powered off" if we just shut down.
             if ( (elapsed - last_runtime_sample > 1000)  && my_lisaframe->running)
             {
                wxString text;
                mhzactual= 0.001 *
                          ( (float)(cpu68k_clocks-last_runtime_cpu68k_clx))  /
                          ( (float)(elapsed-last_runtime_sample) );

                text.Printf("%1.2f MHz",mhzactual);
                my_lisaframe->SetStatusBarText(text);

                last_runtime_sample=elapsed;
                last_runtime_cpu68k_clx=cpu68k_clocks;

                // further slowdown
                if (mhzactual-throttle>0.5) wxMilliSleep((long)((mhzactual-throttle)*15));
             }
             screenrefresh=0;

        }
    }

    // anything to paste to the keyboard?
    if (wspaste_to_keyboard.Len()>idx_paste_to_kb && (copsqueuelen>=0 && copsqueuelen<MAXCOPSQUEUE-8) )
        keystroke_cops( paste_to_keyboard[idx_paste_to_kb++] );


   }
   else {
         last_runtime_sample=0;
        }
#endif

  if ((my_lisawin->floppystate & FLOPPY_ANIM_MASK) != FLOPPY_PRESENT && (my_lisawin->floppystate & FLOPPY_ANIM_MASK) != FLOPPY_EMPTY )
   {

    if (my_lisawin->floppystate & FLOPPY_ANIMATING)                           // go to next frame
       {
         if ((my_lisawin->floppystate & FLOPPY_ANIM_MASK)!= FLOPPY_PRESENT) {
             my_lisawin->floppystate++;
             my_lisawin->floppystate |= FLOPPY_ANIMATING|FLOPPY_NEEDS_REDRAW;
         } else {
               my_lisawin->floppystate= FLOPPY_NEEDS_REDRAW | FLOPPY_PRESENT; // refresh, stop counting.
               if (my_floppy_insert!=NULL && sound_effects_on) my_floppy_insert->Play(wxSOUND_ASYNC);
              }
       }

    else
       {

             if (my_floppy_eject!=NULL && sound_effects_on && (my_lisawin->floppystate & FLOPPY_ANIM_MASK)== FLOPPY_INSERT_2)
                 my_floppy_eject->Play(wxSOUND_ASYNC);

             if ((my_lisawin->floppystate & FLOPPY_ANIM_MASK)==FLOPPY_INSERT_0) {
                 my_lisawin->floppystate=FLOPPY_NEEDS_REDRAW | FLOPPY_EMPTY;
             } else {
                 my_lisawin->floppystate--;  // go to previous frame
                 my_lisawin->floppystate |= FLOPPY_NEEDS_REDRAW;
             }

       }

    wxMilliSleep(200);
   }


   DEBUG_LOG(0,"Yielding");
   if (!running) {
       wxMilliSleep(50);
       wxTheApp->Yield(false);
   }

   DEBUG_LOG(0,"Asking for more idle time");
   //event.SetMode(wxIDLE_PROCESS_SPECIFIED); //  <- without this it crashes on exit, but with this, it doesn't get more time!
   event.RequestMore();                       //  and yes, I did specify this on the window, but it needs to be on the Frame.
   //if (!running) {my_lisawin->Refresh(); my_lisawin->Update();}
        //my_lisawin->RePaint(0,nada); //  so should I move this into the window - that's why it was crashing before.

}


void save_global_prefs(void)
{
  if (!myConfig)    return;        // not initialized yet
  if (!DisplayMenu) return;
  if (!my_lisaframe) return;
  if (!fileMenu)     return;
  myConfig->Write(_T("/soundeffects"),sound_effects_on);
  myConfig->Write(_T("/displayskins"),skins_on_next_run);
  myConfig->Write(_T("/displaymode"), (long)lisa_ui_video_mode);
  myConfig->Write(_T("/asciikeyboard"), (long)asciikeyboard);
  myConfig->Write(_T("/lisaconfigfile"),myconfigfile);
  myConfig->Write(_T("/throttle"),(long)my_lisaframe->throttle);
  myConfig->Flush();


  DisplayMenu->Check(ID_VID_AA    ,lisa_ui_video_mode==0);  DisplayMenu->Enable(ID_VID_AA    ,lisa_ui_video_mode!=0x3a);
  DisplayMenu->Check(ID_VID_AAG   ,lisa_ui_video_mode==4);  DisplayMenu->Enable(ID_VID_AAG   ,lisa_ui_video_mode!=0x3a);
//DisplayMenu->Check(ID_VID_SCALED,lisa_ui_video_mode==5);  DisplayMenu->Enable(ID_VID_SCALED,lisa_ui_video_mode!=0x3a);
  DisplayMenu->Check(ID_VID_DY    ,lisa_ui_video_mode==1);  DisplayMenu->Enable(ID_VID_DY    ,lisa_ui_video_mode!=0x3a);
  DisplayMenu->Check(ID_VID_SY    ,lisa_ui_video_mode==2);  DisplayMenu->Enable(ID_VID_SY    ,lisa_ui_video_mode!=0x3a);
  DisplayMenu->Check(ID_VID_2X3Y  ,lisa_ui_video_mode==3);  DisplayMenu->Enable(ID_VID_2X3Y  ,lisa_ui_video_mode!=0x3a);

  DisplayMenu->Check(ID_VID_SKINS_ON, !!skins_on);
  DisplayMenu->Check(ID_VID_SKINS_OFF, !skins_on);
  fileMenu->Check(ID_PROFILEPWR, !!powerfile_power);  // not saved, is temporary.
  updateThrottleMenus(my_lisaframe->throttle);
}


void LisaEmFrame::SetStatusBarText(wxString &msg) {SetStatusText(msg,0);}

DECLARE_APP(LisaEmApp)           // Implements LisaEmApp& GetApp()
IMPLEMENT_APP(LisaEmApp)         // Give wxWidgets the means to create a LisaEmApp object


// Initialize the application
bool LisaEmApp::OnInit()
{
    wxString argv1;

    wxString defaultconfig = wxGetHomeDir();
    #ifdef __UNIX__
    if (defaultconfig.Last() != wxT('/') )  defaultconfig  <<wxT("/lisaem.conf");
    #else
    if (defaultconfig.Last() != wxT('\\') )  defaultconfig  <<wxT("\\lisaem.conf");
    #endif

    myConfig = wxConfig::Get();         // this one is the global configuration for the app.
                                        // get the path to the last opened Lisaconfig and load that.

    myconfigfile=myConfig->Read(_T("/lisaconfigfile"),defaultconfig);

    // override config file on startup if passed a parameter.
    if (argc>1)
    {
      char *ext;

      ext=strstr(argv[1],".lisaem");

      if (ext!=NULL && strlen(ext)==7)  // ensure the proper extension is contained.
         {
            argv1.Printf("%s",argv[1]);
            if (wxFile::Exists(argv1) ) myconfigfile=argv1;
         }
    }


    pConfig=new wxFileConfig(_T("LisaEm"),
                             _T("sunder.NET"),
                             _T(myconfigfile),     //local
                             _T(myconfigfile),     //global
                             wxCONFIG_USE_LOCAL_FILE,
                             wxConvAuto() );   // or wxConvUTF8

    // Create the main application window
    my_lisaframe=new LisaEmFrame(wxT("LisaEm"));


    // this is a global setting
    skins_on          =(int)myConfig->Read(_T("/displayskins"),(long)1);
    skins_on_next_run=skins_on;
    sound_effects_on  =(int)myConfig->Read(_T("/soundeffects"),(long)1);
    lisa_ui_video_mode=(int)myConfig->Read(_T("/displaymode"), (long)0);
    asciikeyboard     =(int)myConfig->Read(_T("/asciikeyboard"),(long)1);
    my_lisaframe->throttle=(float)(myConfig->Read(_T("/throttle"),(long)5));
    my_lisaframe->clockfactor=1.0/(my_lisaframe->throttle*1000000.0);

    save_global_prefs();

    DEBUG_LOG(0,"CreatingLisaconfig()");
    my_lisaconfig = new LisaConfig();
    DEBUG_LOG(0,"Created Lisaconfig()");

    pConfig->SetRecordDefaults();

    DEBUG_LOG(0,"Loading Config");
    my_lisaconfig->Load(pConfig, floppy_ram);// load it in
    DEBUG_LOG(0,"Saving Config");
    my_lisaconfig->Save(pConfig, floppy_ram);// save it so defaults are created



    my_lisaframe->running=0;                 // CPU isn't yet turned on
    my_lisaframe->loops=MAXLOOPS;            // # of loops to execute from OnIdle

    //my_lisaframe->clockfactor=0.0002;        // 5MHz
    //my_lisaframe->throttle=5.0;              // 5MHz




    SetVendorName("sunder.NET");
    SetAppName("LisaEm");

    my_lisaframe->Show(true);                // Light it up

//#if defined(__WXOSX__) ||
    char *rscDir = getResourcesDir();
    char *sndFile = (char *)malloc(MAXPATHLEN);

#ifndef __WXMSW__
    sprintf(sndFile, "%s/floppy_eject.wav", rscDir);
    my_floppy_eject = new wxSound(wxString(sndFile));
    sprintf(sndFile, "%s/floppy_insert_sound.wav", rscDir);
    my_floppy_insert = new wxSound(wxString(sndFile));
    sprintf(sndFile, "%s/floppy_motor1.wav", rscDir);
    my_floppy_motor1 = new wxSound(wxString(sndFile));
    sprintf(sndFile, "%s/floppy_motor2.wav", rscDir);
    my_floppy_motor2 = new wxSound(wxString(sndFile));

    sprintf(sndFile, "%s/lisa_power_switch01.wav", rscDir);
    my_lisa_power_switch01 = new wxSound(wxString(sndFile));
    sprintf(sndFile, "%s/lisa_power_switch02.wav", rscDir);
    my_lisa_power_switch02 = new wxSound(wxString(sndFile));

    sprintf(sndFile, "%s/poweroffclk.wav", rscDir);
    my_poweroffclk = new wxSound(wxString(sndFile));
#else
    sprintf(sndFile, "%s\\floppy_eject.wav", rscDir);
    my_floppy_eject = new wxSound(wxString(sndFile));
    sprintf(sndFile, "%s\\floppy_insert_sound.wav", rscDir);
    my_floppy_insert = new wxSound(wxString(sndFile));
    sprintf(sndFile, "%s\\floppy_motor1.wav", rscDir);
    my_floppy_motor1 = new wxSound(wxString(sndFile));
    sprintf(sndFile, "%s\\floppy_motor2.wav", rscDir);
    my_floppy_motor2 = new wxSound(wxString(sndFile));

    sprintf(sndFile, "%s\\lisa_power_switch01.wav", rscDir);
    my_lisa_power_switch01 = new wxSound(wxString(sndFile));
    sprintf(sndFile, "%s\\lisa_power_switch02.wav", rscDir);
    my_lisa_power_switch02 = new wxSound(wxString(sndFile));

    sprintf(sndFile, "%s\\poweroffclk.wav", rscDir);
    my_poweroffclk = new wxSound(wxString(sndFile));
#endif

//#else
//    my_floppy_eject       =new wxSound(floppy_eject_size, floppy_eject);
//    my_floppy_insert      =new wxSound(floppy_insert_sound_size, floppy_insert_sound);
//    my_floppy_motor1      =new wxSound(floppy_motor1_size, floppy_motor1);
//    my_floppy_motor2      =new wxSound(floppy_motor2_size, floppy_motor2);
//
//    my_lisa_power_switch01=new wxSound(lisa_power_switch01_size, lisa_power_switch01);
//    my_lisa_power_switch02=new wxSound(lisa_power_switch02_size, lisa_power_switch02);
//
//    my_poweroffclk        =new wxSound(poweroffclk_size, poweroffclk);
//#endif
    return true;
}





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Keyboard Scancodes - these are valid for all keyboards, except that 42 is not on the US keyboard
//                      and 48 is not on the EU keyboard.
//
//                               0    1    2    3    4    5    6    7    8    9   10   11  12   13  14
uint8 kbcodes[5][15]=      { {0x68,0x74,0x71,0x72,0x73,0x64,0x61,0x62,0x63,0x50,0x51,0x40,0x41,0x45, 0 },
                             {0x78,0x75,0x77,0x60,0x65,0x66,0x67,0x52,0x53,0x5f,0x44,0x56,0x57,0x42, 0 },
                             {0x7d,0x70,0x76,0x7b,0x69,0x6a,0x6b,0x54,0x55,0x59,0x5a,0x5b,0x42,0x48, 0 },
                             {0x7e,0x43,0x79,0x7a,0x6d,0x6c,0x6e,0x6f,0x58,0x5d,0x5e,0x4c,0x7e,   0, 0 },
                             {0x7c,0x7f,0x5c,0x46,0x4e,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0 } };
//
//                              0    1    2    3   4
uint8 kbcodesn[ 5][5]=     { {0x20,0x21,0x22,0x23, 0},
                             {0x24,0x25,0x26,0x27, 0},
                             {0x28,0x29,0x2a,0x2b, 0},
                             {0x4d,0x2d,0x2e,0x2f, 0},
                             {0x49,0x2c,   0,   0, 0}                                                    };
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Keyboard Legends - need to build these for the other languages /////////////////////////////////////////////////
//                  - also need to build 3x more (shifted, option, option+shift)
//
//                             0        1   2   3   4   5   6   7   8   9   10 11  12  13          14
char *kb_us_r1_txt[5][15]= { {"`",     "1","2","3","4","5","6","7","8","9","0","-","=","BACKSPACE","" },
                             {"TAB",   "q","w","e","r","t","y","u","i","o","p","[","]","\\"       ,"" },
                             {"CAPS",  "a","s","d","f","g","h","j","k","l",";","'","" ,"RETURN"   ,"" },
                             {"SHIFT", "", "z","x","c","v","b","n","m",",",".","/","SHIFT",""     ,"" },
                             {"OPT","CMD","SPACE","ENTER","OPT","","","","","","","","",""        ,"" }  };
//
char *kb_us_n1_txt[ 5][5]= { {"CLR","-","+","*"    ,""  },
                             {  "7","8","9","/"    ,""  },
                             {  "4","5","6",","    ,""  },
                             {  "1","2","3","ENTER",""  },
                             {  "0",".","" ,""     ,""  }                                                };
//
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//borrowed from wxWidgets example code:  keyboard.cpp by  Vadim Zeitlin
void LisaWin::LogKeyEvent(const wxChar* WXUNUSED(name), wxKeyEvent& event, int keydir)
{
    wxString key;
    long keycode = event.GetKeyCode();
    int lisakey=0;

    //ALERT_LOG(0,"Received %08x keycode on event %d",keycode,keydir);
    switch ( keycode )
    {
#ifdef ENABLE_EMULATOR
//
// need to implement variations of these for the other keyboard languages too.
// also should get rid of this and replace it with some sort of table/array instead.
//
        case 'a' :                   lisakey=KEYCODE_A;               break;
        case 'b' :                   lisakey=KEYCODE_B;               break;
        case 'c' :                   lisakey=KEYCODE_C;               break;
        case 'd' :                   lisakey=KEYCODE_D;               break;
        case 'e' :                   lisakey=KEYCODE_E;               break;
        case 'f' :                   lisakey=KEYCODE_F;               break;
        case 'g' :                   lisakey=KEYCODE_G;               break;
        case 'h' :                   lisakey=KEYCODE_H;               break;
        case 'i' :                   lisakey=KEYCODE_I;               break;
        case 'j' :                   lisakey=KEYCODE_J;               break;
        case 'k' :                   lisakey=KEYCODE_K;               break;
        case 'l' :                   lisakey=KEYCODE_L;               break;
        case 'm' :                   lisakey=KEYCODE_M;               break;
        case 'n' :                   lisakey=KEYCODE_N;               break;
        case 'o' :                   lisakey=KEYCODE_O;               break;
        case 'p' :                   lisakey=KEYCODE_P;               break;
        case 'q' :                   lisakey=KEYCODE_Q;               break;
        case 'r' :                   lisakey=KEYCODE_R;               break;
        case 's' :                   lisakey=KEYCODE_S;               break;
        case 't' :                   lisakey=KEYCODE_T;               break;
        case 'u' :                   lisakey=KEYCODE_U;               break;
        case 'v' :                   lisakey=KEYCODE_V;               break;
        case 'w' :                   lisakey=KEYCODE_W;               break;
        case 'x' :                   lisakey=KEYCODE_X;               break;
        case 'y' :                   lisakey=KEYCODE_Y;               break;
        case 'z' :                   lisakey=KEYCODE_Z;               break;

        case 'A' :                   lisakey=KEYCODE_A;               break;
        case 'B' :                   lisakey=KEYCODE_B;               break;
        case 'C' :                   lisakey=KEYCODE_C;               break;
        case 'D' :                   lisakey=KEYCODE_D;               break;
        case 'E' :                   lisakey=KEYCODE_E;               break;
        case 'F' :                   lisakey=KEYCODE_F;               break;
        case 'G' :                   lisakey=KEYCODE_G;               break;
        case 'H' :                   lisakey=KEYCODE_H;               break;
        case 'I' :                   lisakey=KEYCODE_I;               break;
        case 'J' :                   lisakey=KEYCODE_J;               break;
        case 'K' :                   lisakey=KEYCODE_K;               break;
        case 'L' :                   lisakey=KEYCODE_L;               break;
        case 'M' :                   lisakey=KEYCODE_M;               break;
        case 'N' :                   lisakey=KEYCODE_N;               break;
        case 'O' :                   lisakey=KEYCODE_O;               break;
        case 'P' :                   lisakey=KEYCODE_P;               break;
        case 'Q' :                   lisakey=KEYCODE_Q;               break;
        case 'R' :                   lisakey=KEYCODE_R;               break;
        case 'S' :                   lisakey=KEYCODE_S;               break;
        case 'T' :                   lisakey=KEYCODE_T;               break;
        case 'U' :                   lisakey=KEYCODE_U;               break;
        case 'V' :                   lisakey=KEYCODE_V;               break;
        case 'W' :                   lisakey=KEYCODE_W;               break;
        case 'X' :                   lisakey=KEYCODE_X;               break;
        case 'Y' :                   lisakey=KEYCODE_Y;               break;
        case 'Z' :                   lisakey=KEYCODE_Z;               break;

        case '0' :                   lisakey=KEYCODE_0;               break;
        case '1' :                   lisakey=KEYCODE_1;               break;
        case '2' :                   lisakey=KEYCODE_2;               break;
        case '3' :                   lisakey=KEYCODE_3;               break;
        case '4' :                   lisakey=KEYCODE_4;               break;
        case '5' :                   lisakey=KEYCODE_5;               break;
        case '6' :                   lisakey=KEYCODE_6;               break;
        case '7' :                   lisakey=KEYCODE_7;               break;
        case '8' :                   lisakey=KEYCODE_8;               break;
        case '9' :                   lisakey=KEYCODE_9;               break;

        case WXK_NUMPAD0:            lisakey=KEYCODE_0NUM;            break;
        case WXK_NUMPAD1:            lisakey=KEYCODE_1NUM;            break;
        case WXK_NUMPAD2:            lisakey=KEYCODE_2NUM;            break;
        case WXK_NUMPAD3:            lisakey=KEYCODE_3NUM;            break;
        case WXK_NUMPAD4:            lisakey=KEYCODE_4NUM;            break;
        case WXK_NUMPAD5:            lisakey=KEYCODE_5NUM;            break;
        case WXK_NUMPAD6:            lisakey=KEYCODE_6NUM;            break;
        case WXK_NUMPAD7:            lisakey=KEYCODE_7NUM;            break;
        case WXK_NUMPAD8:            lisakey=KEYCODE_8NUM;            break;
        case WXK_NUMPAD9:            lisakey=KEYCODE_9NUM;            break;

        case WXK_BACK:               lisakey=KEYCODE_BACKSPACE;       break;
        case WXK_TAB:                lisakey=KEYCODE_TAB;             break;
        case WXK_RETURN:             lisakey=KEYCODE_RETURN;          break;
        case WXK_ESCAPE:             lisakey=KEYCODE_CLEAR;           break;
        case WXK_SPACE:              lisakey=KEYCODE_SPACE;           break;
        case WXK_DELETE:             lisakey=KEYCODE_BACKSPACE;       break;

        case        '~':
        case        '`':             lisakey=KEYCODE_TILDE;           break;

        case        '_':
        case        '-':             lisakey=KEYCODE_MINUS;           break;

        case        '+':
        case        '=':             lisakey=KEYCODE_PLUS;            break;

        case        '{':
        case        '[':             lisakey=KEYCODE_OBRAK;           break;

        case        '}':
        case        ']':             lisakey=KEYCODE_CBRAK;           break;


        case        '|':
        case        '\\':            lisakey=KEYCODE_BSLASH;          break;

        case        ':':
        case        ';':             lisakey=KEYCODE_COLON;           break;

        case        '"':
        case        '\'':            lisakey=KEYCODE_QUOTE;           break;

        case        '<':
        case        ',':             lisakey=KEYCODE_COMMA;           break;

        case        '>':
        case        '.':             lisakey=KEYCODE_DOT;             break;

        case        '?':
        case        '/':             lisakey=KEYCODE_FSLASHQ;         break;

        case WXK_NUMPAD_LEFT:        lisakey=KEYCODE_CURSORL;         break;
        case WXK_NUMPAD_UP:          lisakey=KEYCODE_CURSORU;         break;
        case WXK_NUMPAD_RIGHT:       lisakey=KEYCODE_CURSORR;         break;
        case WXK_NUMPAD_DOWN :       lisakey=KEYCODE_CURSORD;         break;


        case WXK_NUMPAD_INSERT:      lisakey=KEYCODE_LENTER;          break;
        case WXK_NUMPAD_DELETE:      lisakey=KEYCODE_BACKSPACE;       break;

      //case WXK_NUMPAD_EQUAL:       lisakey=KEYCODE_EQUAL;           break;
        case WXK_NUMPAD_MULTIPLY:    lisakey=KEYCODE_STAR_NUM;        break;
        case WXK_NUMPAD_ADD:         lisakey=KEYCODE_PLUS_NUM;        break;
        case WXK_NUMPAD_SEPARATOR:
        case WXK_NUMPAD_SUBTRACT:    lisakey=KEYCODE_MINUS_NUM;       break;
        case WXK_NUMPAD_DECIMAL:     lisakey=KEYCODE_DOTNUM;          break;
        case WXK_NUMPAD_DIVIDE:      lisakey=KEYCODE_FSLASH;          break;

        case WXK_NUMPAD_ENTER:       lisakey=KEYCODE_ENTERNUM;        break;
        case WXK_NUMLOCK:            lisakey=KEYCODE_CLEAR;           break;

#endif
        /*
        default:

        {
            if ( keycode < 256 )
            {
                if ( keycode == 0 )
                    key.Printf(_T("NUL"));
                else if ( keycode < 27 )
                    key.Printf(_T("Ctrl-%c"),
                                (unsigned char)(_T('A') + keycode - 1));
                else
                    key.Printf(_T("'%c'"), (unsigned char)keycode);
            }
            else
            {
                key.Printf(_T("unknown (%ld)"), keycode);
            }
        } */
    }

#ifdef  ENABLE_EMULATOR

        if (asciikeyboard)
        {
                 if (keydir==2 && lastkeystroke==-1)  {//ALERT_LOG(0,"Sending keystroke:%02x",keycode);
                                                        keystroke_cops(keycode); }
                 lastkeystroke=-1;
        }
        else
        {
                 if (keydir==-1)                lisakey |=KEY_DOWN;
                 if (lisakey & 0x7f)            {//ALERT_LOG(0,"Sending rawkeycode %02x",lisakey);
                                                 send_cops_keycode(lisakey);}
                 lastkeystroke=-1;
        }



#endif

/*
    wxString msg;
    msg.Printf(_T("%s event: %s (flags = %c%c%c%c)"),
               name,
               key.c_str(),
               GetChar(event.ControlDown(), _T('C')),
               GetChar(event.AltDown(), _T('A')),
               GetChar(event.ShiftDown(), _T('S')),
               GetChar(event.MetaDown(), _T('M')));

        msg += wxString::Format(_T(" (raw key code/flags: %lu and 0x%lx)"),
                                (unsigned long)event.GetRawKeyCode(),
                                (unsigned long)event.GetRawKeyFlags());

    my_lisaframe->SetStatusBarText(wxT(msg));
*/
}


void LisaWin::OnKeyDown(wxKeyEvent& event)
{
    #ifdef ENABLE_EMULATOR
    if (asciikeyboard) event.Skip(); else LogKeyEvent(_T("Key down"), event,-1);
    #endif
}

void LisaWin::OnKeyUp(wxKeyEvent& event)
{
    #ifdef ENABLE_EMULATOR
    if (asciikeyboard) event.Skip(); else LogKeyEvent(_T("Key up"), event,1);
    #endif
}

void LisaWin::OnChar(wxKeyEvent& event)
     {
        if (event.GetKeyCode()==WXK_SHIFT ||                    // avoid inserting extra ascii 1,2,4 on shift,alt,control down.
            event.GetKeyCode()==WXK_ALT   ||
            event.GetKeyCode()==WXK_CONTROL  ) return;

        wxString x; x.Printf(_T("keychar %c"),event.GetKeyCode()); my_lisaframe->SetStatusBarText(x);
        if (asciikeyboard) LogKeyEvent(_T("Char"), event, 2);
        //ALERT_LOG(0,"KeyChar caught");
        if (asciikeyboard) event.Skip(false); //else  LogKeyEvent(_T("Char"), event, 2);
     }


uint8 graymap[4*4*4]={0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,1,0,0,    // two bit gray detection map.
                      0,0,0,0,1,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,1,0,1,0,0,0,0,};




void LisaWin::ContrastChange()
{
    int mc=0x80+((0xff-contrast>>1)  );                   //my contrast

    if (contrast>0xd0) mc=0x70;
    if (contrast>0xe0) mc=0x60;
    if (contrast>0xf0) mc=0x50;

    int mcb=(mc<240) ? mc+16 : mc;                         //my contrast blue
    int c,c1,c2; //,c3,c4;                                 //color level

    int y0,y1,y2,y3;
    int high,medium,low;

    int b3,b2,b1,b0;

#define M(x) (x>255 ? (255): (x))

    // this is correct as brightness is the lowest level of brightness used.
    // that is if you turn up the brightness, black pixels become grayer.

    wxColour black(0,0,0);
    wxColour mid(240,240,255);
    wxColour white(255,255,255);

    wxColour dark(brightness,brightness,brightness);
    wxColour dark0(0,0,0);
    wxColour dark1(0,0,0);
    wxColour gray(0,0,0);

    if (contrast<0xe0)
    {
      c=brightness+120; c1=M(c);    dark0.Set(c1,c1,c1);
      c=brightness+100; c2=M(c);    dark1.Set(c2,c2,c2);
      gray.Set(mc,mc,mcb);
    }


    if (my_alias==NULL)
      {
        DEBUG_LOG(0,"Depth:%d",my_lisaframe->depth);
        my_alias =new class wxBitmap(4*16,16*16);
        my_aliasDC=new class wxMemoryDC;
        my_aliasDC->SelectObject(*my_alias);


        my_bytemap =new class wxBitmap(4,19);
        my_bytemapDC=new class wxMemoryDC;
        my_bytemapDC->SelectObject(*my_bytemap);
      }

    wxPen pen[8];
    pen[0] = wxPen(gray,  1, wxSOLID);
    pen[1] = wxPen(dark0, 1, wxSOLID);
    pen[2] = wxPen(dark1, 1, wxSOLID);
    pen[3] = wxPen(dark1, 1, wxSOLID);
    pen[4] = wxPen(dark,  1, wxSOLID);
    pen[5] = wxPen(dark,  1, wxSOLID);
    pen[6] = wxPen(dark,  1, wxSOLID);
    pen[7] = wxPen(dark,  1, wxSOLID);

//            X YH YL   X*4       (YH*16)|(YL)
#define AALIS(a, b,c) ((a)<<2),( ((b)<<4)|(c) )

    for (y0=0; y0<16; y0++)
    {
      my_bytemapDC->SetPen(pen[ (y0 & 8)!=0 ? 7:0]); my_bytemapDC->DrawPoint(0,y0);
      my_bytemapDC->SetPen(pen[ (y0 & 4)!=0 ? 7:0]); my_bytemapDC->DrawPoint(1,y0);
      my_bytemapDC->SetPen(pen[ (y0 & 2)!=0 ? 7:0]); my_bytemapDC->DrawPoint(2,y0);
      my_bytemapDC->SetPen(pen[ (y0 & 1)!=0 ? 7:0]); my_bytemapDC->DrawPoint(3,y0);
    }


    my_bytemapDC->SetPen(white);
	my_bytemapDC->DrawPoint(0,16);
    my_bytemapDC->DrawPoint(1,16);
    my_bytemapDC->DrawPoint(2,16);
    my_bytemapDC->DrawPoint(3,16);

	my_bytemapDC->SetPen(mid);
	my_bytemapDC->DrawPoint(0,17);
    my_bytemapDC->DrawPoint(1,17);
    my_bytemapDC->DrawPoint(2,17);
    my_bytemapDC->DrawPoint(3,17);

	my_bytemapDC->SetPen(black);
	my_bytemapDC->DrawPoint(0,18);
    my_bytemapDC->DrawPoint(1,18);
    my_bytemapDC->DrawPoint(2,18);
    my_bytemapDC->DrawPoint(3,18);


    for (y0=0; y0<(16*16*16); y0++)
     {

      y1=( (y0>>8) & 15);
      y2=( (y0>>4) & 15);
      y3=( (y0   ) & 15);

      high   =  y1    & y2    & y3    ;      // above, middle, and below are black
      medium = (y1&y2)|(y2&y3)|(y3&y1);      // two out of 3 are black
      low    =  y1    ^ y2    ^ y3    ;      // lowest - single pixel is black


      b3=((  1 & high) ? 4:0) | ((  1 & medium) ? 2:0) | ((  1 & low) ? 1:0);
      b2=((  2 & high) ? 4:0) | ((  2 & medium) ? 2:0) | ((  2 & low) ? 1:0);
      b1=((  4 & high) ? 4:0) | ((  4 & medium) ? 2:0) | ((  4 & low) ? 1:0);
      b0=((  8 & high) ? 4:0) | ((  8 & medium) ? 2:0) | ((  8 & low) ? 1:0);


      // hack to defuzz 50% gray desktop
      if ( (y1==0x5 && y2==0x0a) || (y1==0xa && y2==0x05) ||
           (y3==0x5 && y2==0x0a) || (y3==0xa && y2==0x05) ||

           (y1==0xf && y2==0x0a) || (y1==0xf && y2==0x05) ||     // 20061216
           (y3==0xf && y2==0x0a) || (y3==0xf && y2==0x05) ||     // 20061216
           (y1==0x0 && y2==0x0a) || (y1==0x0 && y2==0x05) ||     // 20061216
           (y3==0x0 && y2==0x0a) || (y3==0x0 && y2==0x05)        // 20061216
         ) {b3=2; b2=2; b1=2; b0=2;}

      my_aliasDC->SetPen(pen[b0]); my_aliasDC->DrawPoint(0+AALIS(y1,y2,y3) );
      my_aliasDC->SetPen(pen[b1]); my_aliasDC->DrawPoint(1+AALIS(y1,y2,y3) );
      my_aliasDC->SetPen(pen[b2]); my_aliasDC->DrawPoint(2+AALIS(y1,y2,y3) );
      my_aliasDC->SetPen(pen[b3]); my_aliasDC->DrawPoint(3+AALIS(y1,y2,y3) );
     }

    //ALERT_LOG(0,"aliasMap Depth:%d",my_alias->GetDepth() );
#undef M

    refresh_bytemap=0;        // we just refreshed the contrast table
    dirtyscreen=1;            // however, the screen bitmap is now dirty

    force_refresh();          // win32 still has contrast-trails. :-( 20070216
}



void LisaWin::RePaint_2X3Y()
{

int i=0, updated=0;
uint8 val,val1,val2;

uint8 doublex[16]={0x00,0x03,0x0c,0x0f,0x30,0x33,0x3c,0x3f,0xc0,0xc3,0xcc,0xcf,0xf0,0xf3,0xfc,0xff};

int maxy=lisa_vid_size_y*3;
int maxx=lisa_vid_size_x*2;
int x,y;

for (y=0; y<maxy ; y+=3)
  for (x=0; x<maxx ; x+=16, i++)
  {
       #ifdef ENABLE_EMULATOR
       val=lisaram[videolatchaddress+i];
       #else
       val=screenshot[i];
       #endif

     if (videoramdirty>1 || dirtyvidram[i]!=val)
     {
      updated=1;

      val1=doublex[val>>4];   val2=doublex[val & 15];

      my_memDC->Blit(x    ,y,    4, 1, my_bytemapDC, 0,(val1>>4)&15, wxCOPY, false);
      my_memDC->Blit(x    ,y+1,  4, 1, my_bytemapDC, 0,(val1>>4)&15, wxCOPY, false);
      my_memDC->Blit(x    ,y+2,  4, 1, my_bytemapDC, 0,(val1>>4)&15, wxCOPY, false);

      my_memDC->Blit(x+4  ,y  ,  4, 1, my_bytemapDC, 0,(val1   )&15, wxCOPY, false);
      my_memDC->Blit(x+4  ,y+1,  4, 1, my_bytemapDC, 0,(val1   )&15, wxCOPY, false);
      my_memDC->Blit(x+4  ,y+2,  4, 1, my_bytemapDC, 0,(val1   )&15, wxCOPY, false);

      my_memDC->Blit(x+8  ,y,    4, 1, my_bytemapDC, 0,(val2>>4)&15, wxCOPY, false);
      my_memDC->Blit(x+8  ,y+1,  4, 1, my_bytemapDC, 0,(val2>>4)&15, wxCOPY, false);
      my_memDC->Blit(x+8  ,y+2,  4, 1, my_bytemapDC, 0,(val2>>4)&15, wxCOPY, false);

      my_memDC->Blit(x+12 ,y  ,  4, 1, my_bytemapDC, 0,(val2   )&15, wxCOPY, false);
      my_memDC->Blit(x+12 ,y+1,  4, 1, my_bytemapDC, 0,(val2   )&15, wxCOPY, false);
      my_memDC->Blit(x+12 ,y+2,  4, 1, my_bytemapDC, 0,(val2   )&15, wxCOPY, false);

     }
  }
  if (updated) {
      memcpy(dirtyvidram,&lisaram[videolatchaddress],32768);
      repaintall |= REPAINT_INVALID_WINDOW | REPAINT_VIDEO_TO_SKIN;
   }

}


void LisaWin::RePaint_DoubleY(void)
{

int i=0,x,y, updated=0; uint8 val;

for (y=0; y<lisa_vid_size_y*2 ; y+=2)
  for (x=0; x<lisa_vid_size_x ; x+=8, i++)
  {
       #ifdef ENABLE_EMULATOR
       val=lisaram[videolatchaddress+i];
       #else
       val=screenshot[i];
       #endif

     if (videoramdirty>1 || dirtyvidram[i]!=val)
     {
       updated=1;

      my_memDC->Blit(x  ,y,    4, 1, my_bytemapDC, 0,(val>>4)&15, wxCOPY, false);
      my_memDC->Blit(x  ,y+1,  4, 1, my_bytemapDC, 0,(val>>4)&15, wxCOPY, false);
      my_memDC->Blit(x+4,y  ,  4, 1, my_bytemapDC, 0,(val   )&15, wxCOPY, false);
      my_memDC->Blit(x+4,y+1,  4, 1, my_bytemapDC, 0,(val   )&15, wxCOPY, false);
     }
  }
  if (updated) {
      memcpy(dirtyvidram,&lisaram[videolatchaddress],32768);
      repaintall |= REPAINT_VIDEO_TO_SKIN;
  }


}

/*-----------------1/12/2007 9:54AM-----------------
 * junk do not use
 * --------------------------------------------------
void LisaWin::RePaint_Scaled(void)
{
int i,x,y, updated=0; uint8 val;

//my_memDC->SetMapMode(wxMM_TEXT);
my_memDC->SetUserScale( 1.0, (float)(500.0/364.0));
my_memDC->ComputeScaleAndOrigin();


for (i=0, y=0; y<lisa_vid_size_y ; y++)
  for (x=0; x<lisa_vid_size_x ; x+=8, i++)
  {
       #ifdef ENABLE_EMULATOR
       val=lisaram[videolatchaddress+i];
       #else
       val=screenshot[i];
       #endif

     if (videoramdirty>1 || dirtyvidram[i]!=val)
     {
       updated=1;
       my_memDC->Blit(x  ,y,    4, 1, my_bytemapDC, 0,(val>>4)&15, wxCOPY, false);
       my_memDC->Blit(x+4,y  ,  4, 1, my_bytemapDC, 0,(val   )&15, wxCOPY, false);
     }
  }
  if (updated) {memcpy(dirtyvidram,&lisaram[videolatchaddress],32768); repaintall |=REPAINTNEEDS_REDRAW|REPAINT_VIDEO_TO_SKIN;}//DEBUG_LOG(0,"updated vidram");}
}
*/


void LisaWin::RePaint_SingleY(void)
{
int i,x,y, updated=0; uint8 val;

for (i=0, y=0; y<lisa_vid_size_y ; y++)
  for (x=0; x<lisa_vid_size_x ; x+=8, i++)
  {
       #ifdef ENABLE_EMULATOR
       val=lisaram[videolatchaddress+i];
       #else
       val=screenshot[i];
       #endif

     if (videoramdirty>1 || dirtyvidram[i]!=val)
     {
       updated=1;
       my_memDC->Blit(x  ,y,    4, 1, my_bytemapDC, 0,(val>>4)&15, wxCOPY, false);
       my_memDC->Blit(x+4,y  ,  4, 1, my_bytemapDC, 0,(val   )&15, wxCOPY, false);
     }
  }
  if (updated) {
      memcpy(dirtyvidram,&lisaram[videolatchaddress],32768);
      repaintall |= REPAINT_INVALID_WINDOW | REPAINT_VIDEO_TO_SKIN;
  }
}


void LisaWin::RePaint_3A(void)
{
int i,x,y, updated=0; uint8 val;

for (i=0, y=0; y<lisa_vid_size_y ; y++)
  for (x=0; x<lisa_vid_size_x ; x+=8, i++)
  {
       #ifdef ENABLE_EMULATOR
       val=lisaram[videolatchaddress+i];
       #else
       val=screenshot[i];
       #endif

     if (videoramdirty>1 || dirtyvidram[i]!=val)
     {
       updated=1;
       my_memDC->Blit(x  ,y,    4, 1, my_bytemapDC, 0,(val>>4)&15, wxCOPY, false);
       my_memDC->Blit(x+4,y  ,  4, 1, my_bytemapDC, 0,(val   )&15, wxCOPY, false);
     }
  }
  if (updated) {
      memcpy(dirtyvidram,&lisaram[videolatchaddress],32768);
      repaintall |= REPAINT_INVALID_WINDOW | REPAINT_VIDEO_TO_SKIN;
  }
}


void LisaWin::RePaint_AAGray(void)
{


  int x,y,xx;
  int val, vdn, vup; // , va; //iup, idn, vb;
  int vall, vdnl, vupl; // , va; //iup, idn, vb;
  int valh, vdnh, vuph; // , va; //iup, idn, vb;

  int vu3, vu2, vu1, vu0,    va3, va2, va1, va0,   vd3, vd2, vd1, vd0;

  int a1,a2,a3,  ga1,ga2; //,ga3;
  int updated=0;

  for (y=499; y>=0; y--)
   for (x=0, xx=0; x<lisa_vid_size_x ; x+=8, xx++)
    {
      a1=(yoffset[screen_to_mouse[ y>0 ? y-1:0 ]]+xx) &32767;           //  modify for XL 3A ROM here! was [y-1] [y+1] before!
      a2=(yoffset[screen_to_mouse[ y+1]]+xx) &32767;           //
      a3=(yoffset[screen_to_mouse[ y  ]]+xx) &32767;


     #ifdef ENABLE_EMULATOR
        vup=lisaram[videolatchaddress+a1];
        vdn=lisaram[videolatchaddress+a2];
        val=lisaram[videolatchaddress+a3];
     #else
        vup=screenshot[a1];
        vdn=screenshot[a2];
        val=screenshot[a3];
     #endif



      if ( videoramdirty>1 || dirtyvidram[a1]!=vup ||  dirtyvidram[a2]!=vdn || dirtyvidram[a3]!=val )
      {

        updated=1;

        if (contrast>0xf0) {val=0xff; vdn=0xff; vup=0xff; updated=4;}

        vuph=(vup>>4) &15;  vupl=(vup ) & 15;      // nibbles
        valh=(val>>4) &15;  vall=(val ) & 15;
        vdnh=(vdn>>4) &15;  vdnl=(vdn ) & 15;

        my_memDC->Blit(x  ,y, 4, 1, my_bytemapDC, 0,valh, wxCOPY, false);
        my_memDC->Blit(x+4,y, 4, 1, my_bytemapDC, 0,vall, wxCOPY, false);

    /*    // gray Lisa Office System scrollbar - doesn't work right.
        if ( (vuph==0x2 && valh==0x8 && vdnh==0x2) ||  ( vuph==0x8 && valh==0x2 && vdnh==0x8) ||
             (vuph==0x1 && valh==0x4 && vdnh==0x1) ||  ( vuph==0x4 && valh==0x1 && vdnh==0x4) )
             {                          // LisaOS scrollbar pattern
              my_memDC->Blit(x,y,  4,1,  my_aliasDC, AALIS(5,5,5), wxCOPY, false); updated=3;
             }
        if ( (vupl==0x2 && vall==0x8 && vdnl==0x2) ||  (vupl==0x8 && vall==0x2 && vdnl==0x8) ||
             (vupl==0x1 && vall==0x4 && vdnl==0x1) ||  (vupl==0x4 && vall==0x1 && vdnl==0x4) )
             {                          // LisaOS scrollbar pattern
              my_memDC->Blit(x+4,y,  4,1,  my_aliasDC, AALIS(5,5,5), wxCOPY, false); updated=3;
             }

        if (updated!=3)
     */
        {
        ga1=(a3-90) &32767;                   //  modify for XL 3A ROM here! was [y-1] [y+1] before!
        ga2=(a3+90) &32767;                   //  on 2nd thought, no need, we don't use it with the 3A

        vup=lisaram[videolatchaddress+ga1];
        vdn=lisaram[videolatchaddress+ga2];

        vu3=(vup>>6) &3; vu2=(vup>>4) & 3; vu1=(vup>>2)&3; vu0=(vup & 3);   // bitpairs
        va3=(val>>6) &3; va2=(val>>4) & 3; va1=(val>>2)&3; va0=(val & 3);
        vd3=(vdn>>6) &3; vd2=(vdn>>4) & 3; vd1=(vdn>>2)&3; vd0=(vdn & 3);

         if (graymap[(vu0<<4) | (va0<<2) | vd0]) {my_memDC->Blit(x+6,y,  2,1,  my_aliasDC, AALIS(0x0a,0x05,0x0a), wxCOPY, false); updated=2;}
         if (graymap[(vu1<<4) | (va1<<2) | vd1]) {my_memDC->Blit(x+4,y,  2,1,  my_aliasDC, AALIS(0x0a,0x05,0x0a), wxCOPY, false); updated=2;}
         if (graymap[(vu2<<4) | (va2<<2) | vd2]) {my_memDC->Blit(x+2,y,  2,1,  my_aliasDC, AALIS(0x0a,0x05,0x0a), wxCOPY, false); updated=2;}
         if (graymap[(vu3<<4) | (va3<<2) | vd3]) {my_memDC->Blit(x+0,y,  2,1,  my_aliasDC, AALIS(0x0a,0x05,0x0a), wxCOPY, false); updated=2;}
        } // 2 bit degray else
      } // if dirty statement
    }  // for x,y statements

    if (updated) {
        memcpy(dirtyvidram,&lisaram[videolatchaddress],32768);
        repaintall |= REPAINT_INVALID_WINDOW | REPAINT_VIDEO_TO_SKIN;
     }
    //if (updated==4) {DEBUG_LOG(0,"allblack");}
    //else {DEBUG_LOG(0,"updated=%d videoramdirty=%d a1-3: %d,%d,%d contrast:%02x %08x",updated,videoramdirty,a1,a2,a3,contrast,pc24);}





} /////// end of RePaint_AAGray() method ///////////////////////////


void LisaWin::RePaint_AntiAliased(void)
{

    //wxCoord lisa_vid_sizey=lisa_vid_size_y-1;
    int x,y,xx;
    int val, vdn, vup; // , va; //iup, idn, vb;
    int vall, vdnl, vupl; // , va; //iup, idn, vb;
    int valh, vdnh, vuph; // , va; //iup, idn, vb;

    //int vu3, vu2, vu1, vu0,    va3, va2, va1, va0,   vd3, vd2, vd1, vd0;

    int a1,a2,a3; //,  ga1,ga2,ga3;
    int updated=0;

    for (y=499; y>=0; y--)
     for (x=0, xx=0; x<lisa_vid_size_x ; x+=8, xx++)
      {
        a1=(yoffset[screen_to_mouse[ y>0?(y-1):0]]+xx) &32767;           //  modify for XL 3A ROM here! was [y-1] [y+1] before!
        a2=(yoffset[screen_to_mouse[ y+1]]+xx) &32767;           //
        a3=(yoffset[screen_to_mouse[ y  ]]+xx) &32767;


       #ifdef ENABLE_EMULATOR
        vup=lisaram[videolatchaddress+a1];
        vdn=lisaram[videolatchaddress+a2];
        val=lisaram[videolatchaddress+a3];
       #else
          vup=screenshot[a1];
          vdn=screenshot[a2];
          val=screenshot[a3];
       #endif

        if (videoramdirty>1 || dirtyvidram[a1]!=vup ||  dirtyvidram[a2]!=vdn || dirtyvidram[a3]!=val)
        {

         updated=1;

         if (contrast>0xf0) {val=0xff; vdn=0xff; vup=0xff; updated=4;}
          vuph=(vup>>4) &15;  vupl=(vup ) & 15;      // nibbles
          valh=(val>>4) &15;  vall=(val ) & 15;
          vdnh=(vdn>>4) &15;  vdnl=(vdn ) & 15;

          my_memDC->Blit(x  ,y, 4, 1, my_bytemapDC, 0,valh, wxCOPY, false);
          my_memDC->Blit(x+4,y, 4, 1, my_bytemapDC, 0,vall, wxCOPY, false);

        } // if dirty statement
      }  // for x,y statements

      if (updated) {
          memcpy(dirtyvidram,&lisaram[videolatchaddress],32768);
          repaintall |= REPAINT_INVALID_WINDOW | REPAINT_VIDEO_TO_SKIN;
      }

} /////// end of RePaint_AntiAliased() method ///////////////////////////






void LisaWin::OnPaint(wxPaintEvent &event )
{
  if (!my_lisaframe) return; // not built yet!
  if (!my_lisawin)   return;


  repaintall |= REPAINT_INVALID_WINDOW;  // FIXME Every OnPaint?!


   wxPaintDC dc(this);
   DoPrepareDC(dc);


   if (my_alias==NULL || refresh_bytemap) ContrastChange();



   if (my_memDC==NULL)       my_memDC     =new class wxMemoryDC;
   if (my_lisabitmap==NULL) {
       my_lisabitmap=new class wxBitmap(effective_lisa_vid_size_x,effective_lisa_vid_size_y); //,my_lisaframe->depth);
       my_memDC->SelectObject(*my_lisabitmap);
   }


   if ((dirtyscreen || videoramdirty) && (powerstate & POWER_ON_MASK) == POWER_ON)
                                   (my_lisawin->*RePainter)();  // whoever came up with this C++ syntax instead of the old C one was on crack!



   if (my_skin!=NULL && skins_on)
   {
      if ((powerstate & POWER_NEEDS_REDRAW)==POWER_NEEDS_REDRAW && my_skin0  && skins_on)
      {
       int y=0;

       my_skinDC->Blit(0,y, my_skin0->GetWidth(),my_skin0->GetHeight(),my_skin0DC, 0,0 ,wxCOPY, false);   y+=my_skin0->GetHeight();
       my_skinDC->Blit(0,y, my_skin1->GetWidth(),my_skin1->GetHeight(),my_skin1DC, 0,0 ,wxCOPY, false);   y+=my_skin1->GetHeight();
       my_skinDC->Blit(0,y, my_skin2->GetWidth(),my_skin2->GetHeight(),my_skin2DC, 0,0 ,wxCOPY, false);   y+=my_skin2->GetHeight();
       my_skinDC->Blit(0,y, my_skin3->GetWidth(),my_skin3->GetHeight(),my_skin3DC, 0,0 ,wxCOPY, false); //y+=my_skin3->GetHeight();


       delete my_skin0;           my_skin0=NULL;
       delete my_skin1;           my_skin1=NULL;
       delete my_skin2;           my_skin2=NULL;
       delete my_skin3;           my_skin3=NULL;

       delete my_skin0DC;         my_skin0DC=NULL;
       delete my_skin1DC;         my_skin1DC=NULL;
       delete my_skin2DC;         my_skin2DC=NULL;
       delete my_skin3DC;         my_skin3DC=NULL;
      }
       if ((floppystate & FLOPPY_NEEDS_REDRAW) && skins_on)
       {
        // The floppy's going to be redrawn so we to blit the floppy to the
        // skin, and then the window will need to be updated from the skin.
        repaintall |= REPAINT_INVALID_WINDOW | REPAINT_FLOPPY_TO_SKIN;


        switch(floppystate & FLOPPY_ANIM_MASK)
        {

           case 0:  my_skinDC->Blit(FLOPPY_LEFT, FLOPPY_TOP, my_floppy0->GetWidth(),my_floppy0->GetHeight(),my_floppy0DC,    0,0 ,wxCOPY, false); break;
           case 1:  my_skinDC->Blit(FLOPPY_LEFT, FLOPPY_TOP, my_floppy1->GetWidth(),my_floppy1->GetHeight(),my_floppy1DC,    0,0 ,wxCOPY, false); break;
           case 2:  my_skinDC->Blit(FLOPPY_LEFT, FLOPPY_TOP, my_floppy2->GetWidth(),my_floppy2->GetHeight(),my_floppy2DC,    0,0 ,wxCOPY, false); break;
           case 3:  my_skinDC->Blit(FLOPPY_LEFT, FLOPPY_TOP, my_floppy3->GetWidth(),my_floppy3->GetHeight(),my_floppy3DC,    0,0 ,wxCOPY, false); break;
          default:  my_skinDC->Blit(FLOPPY_LEFT, FLOPPY_TOP, my_floppy4->GetWidth(),my_floppy4->GetHeight(),my_floppy4DC,    0,0 ,wxCOPY, false);

        }

        floppystate &= FLOPPY_ANIMATING | FLOPPY_ANIM_MASK; //XXX Is this right?
       }

       if ((powerstate & POWER_NEEDS_REDRAW) && skins_on)
       {
        // The power's going to be redrawn so we to blit the power to the
        // skin, and then the window will need to be updated from the skin.
        repaintall |= REPAINT_INVALID_WINDOW | REPAINT_POWER_TO_SKIN;
        if ((powerstate & POWER_ON_MASK) == POWER_ON)
           {

             my_skinDC->Blit(POWER_LEFT, POWER_TOP,  my_poweron->GetWidth(), my_poweron->GetHeight(), my_poweronDC, 0,0, wxCOPY, false);

             // These coordinates are a small rectangle around the powerlight
             if (powerstate & POWER_UNKNOWN)
                my_skinDC->Blit(1143-2,836-2, (1185-1143)+2  , (876-836)+2, my_poweronDC,  1143,(836-738), wxCOPY, false);
           }
        else
           {
             my_skinDC->Blit(POWER_LEFT, POWER_TOP,  my_poweroff->GetWidth(), my_poweroff->GetHeight(), my_poweroffDC, 0,0, wxCOPY, false);
             if (powerstate & POWER_UNKNOWN)
                 my_skinDC->Blit(1143-2,836-2, (1185-1143)+2  , (876-836)+2, my_poweroffDC,  1143,(836-738), wxCOPY, false);
           }

        powerstate &= POWER_UNKNOWN | POWER_ON; //XX what does this do?
       }



       // Push the Lisavideo bitmap to the skin
       if (repaintall & REPAINT_VIDEO_TO_SKIN)
          {
           if (skins_on)
               my_skinDC->Blit(screen_origin_x,screen_origin_y, effective_lisa_vid_size_x, effective_lisa_vid_size_y,
                             my_memDC, 0,0, wxCOPY, false);

           repaintall &= ~REPAINT_VIDEO_TO_SKIN; // Skin is now up to date with video
           repaintall |= REPAINT_INVALID_WINDOW;
          }



        ///////////////////////////////////////////////////////////////////
        int vbX,vbY;
        int ivbX,ivbY;
        int width,height;

        GetViewStart(&vbX,&vbY);           // convert scrollbar position into skin relative pixels

        ivbX=vbX; ivbY=vbY;
        if (skins_on)
           { vbX=vbX * (my_skin->GetWidth()/100);
             vbY=vbY * (my_skin->GetHeight()/100);
           }
        else
           {
             vbX=vbX * (my_lisabitmap->GetWidth()/100);
             vbY=vbY * (my_lisabitmap->GetHeight()/100);
           }

        ///////////////////////////////////////////////////////////////////

       if ( (repaintall & REPAINT_INVALID_WINDOW) )
       {

        GetClientSize(&width,&height);
        if (skins_on)
        {
            if (width>my_skin->GetWidth())    width=my_skin->GetWidth();
            if (height>my_skin->GetHeight()) height=my_skin->GetHeight();
        }
        dc.Blit(vbX,vbY, width, height, skins_on ? my_skinDC:my_memDC, vbX,vbY, wxCOPY, false);
        repaintall=0;

       }

   }
   else
   {
       static int x; x++;  // don't update too often to avoid slowdowns
       if (!(x & 15) || !my_lisaframe->running)
          {
           int width, height;
           GetSize(&width,&height); //was GetclientSize
           dc.SetBrush(*wxBLACK_BRUSH);  dc.SetPen(*wxBLACK_PEN);
           dc.DrawRectangle(effective_lisa_vid_size_x,0,width,height);
           dc.DrawRectangle(0,effective_lisa_vid_size_y,width,height);
          }

       dc.Blit(0,0, lisa_vid_size_x, effective_lisa_vid_size_y, my_memDC, 0,0, wxCOPY, false);
   }

  dirtyscreen=0;


  event.Skip(false);
}

void LisaWin::OnErase(wxEraseEvent& WXUNUSED(event)) { }  // prevent flashing by eating this event


void black(void)
{
  if (skins_on && my_skin!=NULL)
     {
       my_skinDC->SetBrush(*wxBLACK_BRUSH);
       my_skinDC->SetPen(*wxBLACK_PEN);
       my_skinDC->DrawRectangle(screen_origin_x,screen_origin_y, effective_lisa_vid_size_x,effective_lisa_vid_size_y);
     }
  else
     {
       int x,y;
       my_lisaframe->GetClientSize(&x,&y);
       my_memDC->SetBrush(*wxBLACK_BRUSH);
       my_memDC->SetPen(*wxBLACK_PEN);

       my_memDC->DrawRectangle(0,0,2048,2048);
     }
}


extern "C" void setstatusbar(char *text)
{
 wxString x=text;
 my_lisaframe->SetStatusBarText(x);
}


extern "C" void gen_clock_diff_sleep(long diff)
{
 // 5000000 1      second
 // 500000  1/10   second
 // 50000   1/100  second
 // 5000    1/1000 second
 // 5000*50=5000*50  50ms

 //my_lisaframe->clockfactor;

 // sleep the time difference.
 wxMilliSleep(   (long)(   (double)(diff)*my_lisaframe->clockfactor ) );
}


void lightning(void)
{

 wxPaintEvent nada;
 // CAN YOU FEEL IT BUILDING
 int middle=lisa_vid_size_x/2;
 // CAN YOU FEEL IT BUILDING
 int x,y;
 // DEVASTATION IS ON THE WAY

    contrast=0x80; contrastchange();
     // black screen                              //white->16, mid->17, black->18
     for (y=effective_lisa_vid_size_y; y>=0; y--)
      for (x=0; x<lisa_vid_size_x ; x+=4)
           my_memDC->Blit(x  ,y,    4, 1, my_bytemapDC, 0,18, wxCOPY, false);

     my_lisawin->powerstate = POWER_OFF;     my_lisawin->dirtyscreen=1;     my_lisawin->repaintall|=1;
     my_lisawin->Refresh(); my_lisawin->Update();
     wxMilliSleep(30);

     if (my_poweroffclk!=NULL && sound_effects_on)  my_poweroffclk->Play(wxSOUND_ASYNC);

     for (y=effective_lisa_vid_size_y; y>=0; y--)
     for (x=middle-12; x<middle+12; x+=4)
         {
          if (x==middle) my_memDC->Blit(x  ,y,    4, 1, my_bytemapDC, 0,16, wxCOPY, false);
          else           my_memDC->Blit(x  ,y,    4, 1, my_bytemapDC, 0,17, wxCOPY, false);
         }
     my_lisawin->powerstate = POWER_OFF;     my_lisawin->dirtyscreen=1;     my_lisawin->repaintall|=1;
     my_lisawin->Refresh(true,NULL); my_lisawin->Update();
     wxMilliSleep(30);

     // black out the previous
     for (y=499; y>=0; y--)
     for (x=middle-12; x<middle+12; x+=4)
          my_memDC->Blit(x  ,y,    4, 1, my_bytemapDC, 0,18, wxCOPY,false);

     x=middle;
     for (y=499; y>=0; y--)
         my_memDC->Blit(x  ,y,    4, 1, my_bytemapDC,  0,16, wxCOPY,false);


     my_lisawin->powerstate = POWER_OFF;     my_lisawin->dirtyscreen=1;     my_lisawin->repaintall|=1;
     my_lisawin->Refresh(true,NULL); my_lisawin->Update();
     wxMilliSleep(30);


     for (y=499; y>=0; y--)
         my_memDC->Blit(x  ,y,    4, 1, my_bytemapDC,  0,18, wxCOPY,false);

     my_lisawin->powerstate = POWER_NEEDS_REDRAW;
     my_lisawin->powerstate = POWER_NEEDS_REDRAW;     my_lisawin->dirtyscreen=1;     my_lisawin->repaintall|=1;
     my_lisawin->Refresh(true,NULL); my_lisawin->Update();
     my_lisawin->powerstate = POWER_OFF;
    contrast=0xf;
    contrastchange();
}

int initialize_all_subsystems(void);


void handle_powerbutton(void)
{


   #if ENABLE_EMULATOR


              if ((my_lisawin->powerstate & POWER_ON_MASK) == POWER_ON) // power is already on, send a power-key event instead
              {
               int i;

               presspowerswitch();        // send keyboard event.

               iw_formfeed(scc_a_IW);
               iw_formfeed(scc_b_IW);
               for (i=2; i<9; i++) iw_formfeed(i);
               iw_shutdown();
              }
              else
              {
                  int ret=initialize_all_subsystems();
                  my_lisaconfig->Save(pConfig, floppy_ram);

                  if (!ret)
                  {
                        my_lisawin->powerstate|= POWER_NEEDS_REDRAW | POWER_ON;
                        my_lisaframe->running=1;// was 1
                        my_lisaframe->runtime.Start(0);
                        DEBUG_LOG(0,"Turned on power: running:%d powerstate:%d",my_lisaframe->running,my_lisawin->powerstate);

                  }
                  else wxMessageBox(_T("Power on failed."), _T("Poweron Failed"), wxICON_INFORMATION | wxOK);
                  if (ret>1) EXIT(999);  // out of memory or other fatal error!

                  contrast=0;


                  my_lisawin->dirtyscreen=1;
              }
    #else
               // fake ui flip.
               my_lisawin->powerstate=(powerstate^POWER_ON)|POWER_NEEDS_REDRAW;  // fake for now
    #endif

   if ((my_lisawin->powerstate & POWER_ON_MASK)==POWER_OFF) lightning();
   else my_lisawin->dirtyscreen=1;
}


extern "C" void lisa_powered_off(void)
{

   my_lisaframe->running=0;			     // no longer running
   my_lisawin->powerstate=POWER_OFF; // XXX was '40' -- not 0x40
   lightning();					     // CRT lightning
   unvars();					     // reset global vars
   my_lisaconfig->Save(pConfig, floppy_ram);         // save PRAM, configuration
   fileMenu->Check(ID_RUN,false);		     // emulator no longer running
   my_lisaframe->runtime.Pause();                    // pause the stopwatch
   setstatusbar("The Lisa has powered off");
}

extern "C" void lisa_rebooted(void)
{
   my_lisaconfig->Save(pConfig, floppy_ram);         // save PRAM, configuration
   unvars();                                         // reset global vars and all
   my_lisaframe->running=0;                          // prevent init_all_subs from barking
   my_lisawin->powerstate = POWER_OFF;               // will be turned back on immediately
   handle_powerbutton();                             // back on we go.
}



void LisaWin::OnMouseMove(wxMouseEvent &event)
{

    //PrepareDC(dc);
    wxString str;
    int vbX,vbY;                     // scrollbar location

     // windows bug - crashes because it gets a mouse event before the window is ready.

     wxPoint pos = event.GetPosition();


     if (skins_on && my_skin==NULL) return;
     if (my_lisabitmap==NULL) return;

     GetViewStart(&vbX,&vbY);
    // convert scrollbar position into skin relative pixels
     if (skins_on && my_skin!=NULL)
        {
         vbX=vbX * (my_skin->GetWidth()/100);
         vbY=vbY * (my_skin->GetHeight()/100);
        }
     else
        {
         vbX=vbX * (my_lisabitmap->GetWidth()/100);
         vbY=vbY * (my_lisabitmap->GetHeight()/100);
        }

     pos.x+=vbX;
     pos.y+=vbY;

    long x= pos.x;
    long yy= pos.y;

    long y = doubley ? (yy>>1) : yy;
    long tempy;

    tempy=pos.y-screen_origin_y;
    if (tempy<0) tempy=0;
    if (tempy>500) tempy=499;


    y=screen_to_mouse[tempy];

    x=x-screen_origin_x;


    // power animation + button
    if (pos.x>1143 && pos.x<1185 && pos.y>836 && pos.y<876 && skins_on)
    {
     my_lisawin->powerstate &= POWER_ON;

      // unlike normal UI buttons, where the action happens when you let go of the mouse button,
      // this is as it should be, as the Lisa turns on when the power button is pushed in.
      if (event.LeftDown())    {  if (sound_effects_on) my_lisa_power_switch01->Play(wxSOUND_ASYNC);
                                  my_lisawin->powerstate |= POWER_NEEDS_REDRAW | POWER_UNKNOWN;
                               }

      if (event.LeftUp())      { if (sound_effects_on) my_lisa_power_switch02->Play(wxSOUND_ASYNC);}

      handle_powerbutton();
    }

#ifdef ENABLE_EMULATOR

    if ( (y>0 && y<lisa_vid_size_y) && (x>=0 && x<lisa_vid_size_x) && my_lisaframe->running)
            {
                static long lastup;
                long now;                              // tee hee, the long now.
                int lu=event.LeftUp();
                now=my_lisaframe->runtime.Time();
#if defined(__WXX11__) || defined(__WXGTK__) || defined(__WXOSX__)
                SetCursor(*m_dot_cursor);
#else
                SetCursor(wxCURSOR_CROSS);
#endif
                add_mouse_event(x,y, lu ? -1:  (event.LeftDown() ? 1:0) );

                // double click hack  - fixme BUG BUG BUG - fixme
                if (lu)
                   {
                          if (now-lastup<1500)
                          {
                           add_mouse_event(x,y,  1);
                           add_mouse_event(x,y, -1);
                           add_mouse_event(x,y,  1);
                           add_mouse_event(x,y, -1);
                          }

                          lastup=now;
                   }


            }
    else
            my_lisawin->SetCursor(wxNullCursor);

    if (event.RightDown()) videoramdirty|=9;  // force screen refresh
#else


#endif


    /*
    str.Printf( wxT("Current mouse position: %03d,%03d -> <%03d> <- (%03d) button:%d,%d clock:%04d/%03d %02d:%02d:%02d.%1d loops:%d %0.2f MHz"),
                (int)x, (int)y,( (y>=0 && y<364) ? screen_y_map[y]: 999),(int)yy,
                event.LeftIsDown(),event.RightIsDown() ,

                1980+(lisa_clock.year & 0x0f),
                (((lisa_clock.days_h & 0xf0)>>4)*100+(lisa_clock.days_h & 0x0f)*10+lisa_clock.days_l),
                (lisa_clock.hours_h*10 + lisa_clock.hours_l),
                (lisa_clock.mins_h *10 + lisa_clock.mins_l),
                (lisa_clock.secs_h *10 + lisa_clock.secs_l),
                (lisa_clock.tenths),
                my_lisaframe->loops,
                my_lisaframe->mhzactual/1000.0
              );

    my_lisaframe->SetStatusBarText( str );
    */

}


void LisaEmFrame::OnRun(wxCommandEvent& WXUNUSED(event))
{
    if (my_lisaframe->running==0)  {handle_powerbutton(); return; }
    if (my_lisaframe->running==10) {my_lisaframe->running=1;      }

    fileMenu->Check(ID_RUN,  true);
}


extern "C" void pause_run(void)
{
   if (my_lisaframe->running==0) return;

   if (my_lisaframe->running==1)
   {
     my_lisaframe->runtime.Pause();
     my_lisaframe->running=10;
     return;
   }
}

extern "C" void resume_run(void)
{
   if (my_lisaframe->running==0) return;

   if (my_lisaframe->running==10)
   {
     my_lisaframe->runtime.Resume();
     my_lisaframe->running=1;
     return;
   }
}


void LisaEmFrame::OnPause(wxCommandEvent& WXUNUSED(event))
{
    if (my_lisaframe->running==0)  {                                                return;}
    if (my_lisaframe->running==1)  {pause_run();   fileMenu->Check(ID_PAUSE,true);  return;}
    if (my_lisaframe->running==10) {resume_run();  fileMenu->Check(ID_PAUSE,false); return;}
}


void LisaEmFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
    wxAboutDialogInfo info;
    info.SetName(_T("LisaEm"));
    info.SetVersion(_T("1.0.0 Beta1/20070301"));
    info.SetDescription(_T("The first fully functional Apple Lisa emulator."));
    info.SetCopyright(_T("\xa9 2007 Ray Arachelian"));
    wxAboutBox(info);
}
#ifdef __WXOSX__
void LisaEmFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
#else
void LisaEmApp::OnQuit(wxCommandEvent& WXUNUSED(event))
#endif
{
    if (NULL!=my_lisabitmap          )             {delete my_lisabitmap          ;   my_lisabitmap=NULL;           }
    if (NULL!=my_memDC               )             {delete my_memDC               ;   my_memDC=NULL;                }

    if (NULL!=my_lisa_sound          )             {delete my_lisa_sound          ;   my_lisa_sound=NULL;           }

    if (NULL!=my_floppy_eject        )             {delete my_floppy_eject        ;   my_floppy_eject=NULL;         }
    if (NULL!=my_floppy_insert       )             {delete my_floppy_insert       ;   my_floppy_insert=NULL;        }
    if (NULL!=my_floppy_motor1       )             {delete my_floppy_motor1       ;   my_floppy_motor1=NULL;        }
    if (NULL!=my_floppy_motor2       )             {delete my_floppy_motor2       ;   my_floppy_motor2=NULL;        }
    if (NULL!=my_lisa_power_switch01 )             {delete my_lisa_power_switch01 ;   my_lisa_power_switch01=NULL;  }
    if (NULL!=my_lisa_power_switch02 )             {delete my_lisa_power_switch02 ;   my_lisa_power_switch02=NULL;  }
    if (NULL!=my_poweroffclk         )             {delete my_poweroffclk         ;   my_poweroffclk=NULL;          }
    if (NULL!=my_bytemap)                          {delete my_bytemap;     my_bytemap=NULL; }
    if (NULL!=my_alias  )                          {delete my_alias  ;     my_alias  =NULL; }

    if (NULL!=my_skin   )                          {delete my_skin   ;     my_skin   =NULL; }
    if (NULL!=my_skin0  )                          {delete my_skin0  ;     my_skin   =NULL; }
    if (NULL!=my_skin1  )                          {delete my_skin1  ;     my_skin   =NULL; }
    if (NULL!=my_skin2  )                          {delete my_skin2  ;     my_skin   =NULL; }
    if (NULL!=my_skin3  )                          {delete my_skin3  ;     my_skin   =NULL; }



    if (NULL!=my_floppy0)                          {delete my_floppy0;     my_floppy0=NULL; }
    if (NULL!=my_floppy1)                          {delete my_floppy1;     my_floppy1=NULL; }
    if (NULL!=my_floppy2)                          {delete my_floppy2;     my_floppy2=NULL; }
    if (NULL!=my_floppy3)                          {delete my_floppy3;     my_floppy3=NULL; }
    if (NULL!=my_floppy4)                          {delete my_floppy4;     my_floppy4=NULL; }

    if (NULL!=my_poweron   )                       {delete my_poweron   ;  my_poweron   =NULL; }
    if (NULL!=my_poweroff  )                       {delete my_poweroff  ;  my_poweroff  =NULL; }

    if (NULL!=my_bytemapDC )                       {delete my_bytemapDC ;  my_bytemapDC =NULL; }
    if (NULL!=my_aliasDC   )                       {delete my_aliasDC   ;  my_aliasDC   =NULL; }

    if (NULL!=my_skinDC    )                       {delete my_skinDC    ;  my_skinDC    =NULL; }
    if (NULL!=my_floppy0DC )                       {delete my_floppy0DC ;  my_floppy0DC =NULL; }
    if (NULL!=my_floppy1DC )                       {delete my_floppy1DC ;  my_floppy1DC =NULL; }
    if (NULL!=my_floppy2DC )                       {delete my_floppy2DC ;  my_floppy2DC =NULL; }
    if (NULL!=my_floppy3DC )                       {delete my_floppy3DC ;  my_floppy3DC =NULL; }
    if (NULL!=my_floppy4DC )                       {delete my_floppy4DC ;  my_floppy4DC =NULL; }

    if (NULL!=my_poweronDC )                       {delete my_poweronDC ;  my_poweronDC =NULL; }
    if (NULL!=my_poweroffDC)                       {delete my_poweroffDC;  my_poweroffDC=NULL; }

#ifdef __WXOSX__
     Close();
#else
     wxMilliSleep(500);
     delete my_lisaframe;
     wxExit();
#endif
}

void LisaEmFrame::OnLisaWeb(wxCommandEvent& WXUNUSED(event)) {::wxLaunchDefaultBrowser("http://lisaem.sunder.net");}
void LisaEmFrame::OnLisaFaq(wxCommandEvent& WXUNUSED(event)) {::wxLaunchDefaultBrowser("http://lisafaq.sunder.net");}




void LisaEmFrame::OnConfig(wxCommandEvent& WXUNUSED(event))
{
          if (my_LisaConfigFrame) {delete my_LisaConfigFrame; my_LisaConfigFrame=NULL;}

          if (!my_LisaConfigFrame)    my_LisaConfigFrame=new LisaConfigFrame( wxT("Preferences"), my_lisaconfig);
          #if defined(__WXMOTIF__)
          int x, y;

          GetSize(&x, &y);
#ifndef  __WXOSX__
         x+=WINXDIFF; y+=WINYDIFF;
#endif
          SetSize(wxDefaultCoord, wxDefaultCoord,x,y);
          #endif

          my_LisaConfigFrame->Show();

}

void LisaEmFrame::OnOpen(wxCommandEvent& WXUNUSED(event))
{
       wxString openfile;
       wxFileDialog open(this,                  wxT("Open LisaEm Preferences:"),
                                                wxEmptyString,
                                                wxEmptyString,
                                                wxT("LisaEm Preferences (*.lisaem)|*.lisaem|All (*.*)|*.*"),
                                                (long int)wxFD_OPEN,wxDefaultPosition);

       if (open.ShowModal()==wxID_OK)           openfile=open.GetPath();
       else return;


       if (pConfig) {pConfig->Flush();  delete pConfig; pConfig=NULL;}
       if (my_LisaConfigFrame) {delete my_LisaConfigFrame; my_LisaConfigFrame=NULL;}

       pConfig=new wxFileConfig(_T("LisaEm"),
                              _T("sunder.NET"),
                              _T(openfile),     //local
                              _T(openfile),     //global
                              wxCONFIG_USE_LOCAL_FILE,
                              wxConvAuto() );   // or wxConvUTF8

      pConfig->Get(true);
      if (my_lisaconfig) delete my_lisaconfig;
      my_lisaconfig = new LisaConfig();
      my_lisaconfig->Load(pConfig, floppy_ram);// load it in





      myconfigfile=openfile;  // update global file to point to new config
      save_global_prefs();    // and save them.
}

void LisaEmFrame::OnSaveAs(wxCommandEvent& WXUNUSED(event))
{
       wxString savefile;
       wxFileDialog open(this,                  wxT("Save LisaEm Preferences As:"),
                                                wxEmptyString,
                                                wxT(myconfigfile),  // risky, perhaps.
                                                wxT("LisaEm Preferences (*.lisaem)|*.lisaem|All (*.*)|*.*"),
                                                (long int)wxFD_SAVE,wxDefaultPosition);

       if (open.ShowModal()==wxID_OK)           savefile=open.GetPath();
       else return;

       wxFileOutputStream out(savefile);

       if (pConfig->Save(out,wxConvUTF8) )
           {
            myconfigfile=savefile;
            save_global_prefs();
           }
}


void LisaEmFrame::OnDebugger(wxCommandEvent& WXUNUSED(event))            {}



void LisaEmFrame::OnPOWERKEY(wxCommandEvent& WXUNUSED(event))            {fileMenu->Check(ID_RUN,  true);handle_powerbutton();}

void LisaEmFrame::OnKEY_APL_DOT(wxCommandEvent& WXUNUSED(event))         {apple_dot();}
void LisaEmFrame::OnKEY_APL_S(wxCommandEvent& WXUNUSED(event))           {apple_S();}
void LisaEmFrame::OnKEY_APL_ENTER(wxCommandEvent& WXUNUSED(event))       {apple_enter();}
void LisaEmFrame::OnKEY_APL_RENTER(wxCommandEvent& WXUNUSED(event))      {apple_renter();}
void LisaEmFrame::OnKEY_APL_1(wxCommandEvent& WXUNUSED(event))           {apple_1();}
void LisaEmFrame::OnKEY_APL_2(wxCommandEvent& WXUNUSED(event))           {apple_2();}
void LisaEmFrame::OnKEY_APL_3(wxCommandEvent& WXUNUSED(event))           {apple_3();}

void LisaEmFrame::OnKEY_OPT_0(wxCommandEvent& WXUNUSED(event))           {shift_option_0();}
void LisaEmFrame::OnKEY_OPT_4(wxCommandEvent& WXUNUSED(event))           {shift_option_4();}
void LisaEmFrame::OnKEY_OPT_7(wxCommandEvent& WXUNUSED(event))           {shift_option_7();}
void LisaEmFrame::OnKEY_NMI(wxCommandEvent& WXUNUSED(event))             {send_nmi_key();}
// display a floating keyboard and allow edits.
void LisaEmFrame::OnKEYBOARD(wxCommandEvent& WXUNUSED(event))  {}




void LisaEmFrame::OnASCIIKB(wxCommandEvent& WXUNUSED(event))   {asciikeyboard=1;
                                                                keyMenu->Check(ID_ASCIIKB, true);
                                                                keyMenu->Check(ID_RAWKB,   false);
                                                                myConfig->Write(_T("/asciikeyboard"), (long)asciikeyboard);

                                                               }


void LisaEmFrame::OnRAWKB(wxCommandEvent& WXUNUSED(event))     {asciikeyboard=0;
                                                                keyMenu->Check(ID_ASCIIKB, false);
                                                                keyMenu->Check(ID_RAWKB,   true);
                                                                myConfig->Write(_T("/asciikeyboard"), (long)asciikeyboard);

                                                               }

/* Make sure only one of the Throttle menu items is checked at any time. */
void updateThrottleMenus(float throttle)
{
    if (!my_lisaframe) return;

    my_lisaframe->clockfactor=1.0/(my_lisaframe->throttle*1000000.0);        // 5MHz

    if (!throttleMenu) return;
    throttleMenu->Check(ID_THROTTLE5,   throttle ==  5.0);
    throttleMenu->Check(ID_THROTTLE8,   throttle ==  8.0);
    throttleMenu->Check(ID_THROTTLE10,  throttle == 10.0);
    throttleMenu->Check(ID_THROTTLE12,  throttle == 12.0);
    throttleMenu->Check(ID_THROTTLE16,  throttle == 16.0);
    throttleMenu->Check(ID_THROTTLE32,  throttle == 32.0);
}

void LisaEmFrame::OnTHROTTLE5(wxCommandEvent& WXUNUSED(event))
{
    clockfactor=0.0002;
    throttle = 5;
    save_global_prefs();
}

void LisaEmFrame::OnTHROTTLE8(wxCommandEvent& WXUNUSED(event))
{
    clockfactor=0.000125;
    throttle = 8;
    save_global_prefs();
}

void LisaEmFrame::OnThrottle10(wxCommandEvent& WXUNUSED(event))
{
    clockfactor=0.0001;
    throttle = 10;
    save_global_prefs();
}

void LisaEmFrame::OnThrottle12(wxCommandEvent& WXUNUSED(event))
{
    clockfactor=0.000083333;
    throttle = 12;
    save_global_prefs();
}
void LisaEmFrame::OnThrottle16(wxCommandEvent& WXUNUSED(event))
{
    clockfactor=0.0000625;
    throttle = 16;
    save_global_prefs();
}

void LisaEmFrame::OnThrottle32(wxCommandEvent& WXUNUSED(event))
{
    clockfactor=0.00003125;
    throttle=32;
    save_global_prefs();
}


extern "C" void messagebox(char *s, char *t)  // messagebox string of text, title
           {
             wxString text=s;
             wxString title=t;
             wxMessageBox(_T(text), _T(title), wxICON_INFORMATION | wxOK);
           }

extern "C" int yesnomessagebox(char *s, char *t)  // messagebox string of text, title
           {
             wxString text=s;
             wxString title=t;
             wxMessageDialog w(my_lisawin,_T(text), _T(title), wxICON_QUESTION  | wxYES_NO |wxNO_DEFAULT,wxDefaultPosition );
             if (w.ShowModal()==wxID_YES) return 1;
             return 0;
           }


extern "C" void eject_floppy_animation(void)
{
 if ((my_lisawin->floppystate & FLOPPY_ANIM_MASK)==FLOPPY_PRESENT)          // initiate eject animation sequence
     {my_lisawin->floppystate=FLOPPY_NEEDS_REDRAW|FLOPPY_INSERT_2; return;}

   return;
}


extern "C" void floppy_motor_sounds(int track)
{
// there really are 3-4 speeds, however, my admittedly Bolt Thrower damaged ears can only distinguish two  :)
// close enough for government work, I guess.

#ifndef __WXMSW__
 if (wxSound::IsPlaying()) return;       // doesn't work on windows
#else
 if (my_lisaframe->soundplaying)
    if (my_lisaframe->soundsw.Time()<400) return;
my_lisaframe->soundplaying=1;
my_lisaframe->soundsw.Start(0);
#endif
 wxSound::Stop();

 if (sound_effects_on)
 {
   if (track>35)  {if (my_floppy_motor1->IsOk()) my_floppy_motor1->Play(wxSOUND_ASYNC);}
   else           {if (my_floppy_motor2->IsOk()) my_floppy_motor2->Play(wxSOUND_ASYNC);}
 }

}

void LisaEmFrame::OnProFilePower(wxCommandEvent& WXUNUSED(event))
{
  powerfile_power=!powerfile_power;
  if (fileMenu) fileMenu->Check(ID_PROFILEPWR, !!powerfile_power);
}

void LisaEmFrame::OnFLOPPY(wxCommandEvent& WXUNUSED(event))
{
    if ((my_lisawin->floppystate & FLOPPY_ANIM_MASK)!=FLOPPY_EMPTY) {
        wxMessageBox(_T("A previously inserted diskette is still in the drive. "
            "Please eject the diskette before inserting another."),
            _T("Diskette is already inserted!"), wxICON_INFORMATION | wxOK);
        return;
    }

    pause_run();

    wxString openfile;
    wxFileDialog open(this,                     wxT("Insert a Lisa diskette"),
                                                wxEmptyString,
                                                wxEmptyString,
                                                wxT("Disk Copy (*.dc42)|*.dc42|DART (*.dart)|*.dart|All (*.*)|*.*"),
                                                (long int)wxFD_OPEN,wxDefaultPosition);
    if (open.ShowModal()==wxID_OK)              openfile=open.GetPath();

    resume_run();

#ifdef ENABLE_EMULATOR
    if (!openfile.Len()) return;
    const char *s = openfile.fn_str();

    if (my_lisaframe->running) {
        if (floppy_insert((char *)s)) return;
    } else {
        floppy_to_insert = openfile;
    }
#endif

    if ((my_lisawin->floppystate & FLOPPY_ANIM_MASK)==FLOPPY_EMPTY) {
        // initiate insert animation sequence
        my_lisawin->floppystate=FLOPPY_NEEDS_REDRAW|FLOPPY_ANIMATING|FLOPPY_INSERT_0;
     }
}



void LisaEmFrame::OnNewFLOPPY(wxCommandEvent& WXUNUSED(event))
{
    if (!my_lisaframe->running) {
        wxMessageBox(_T("Please turn the Lisa on before inserting a diskette."),
            _T("The Lisa is Off"), wxICON_INFORMATION | wxOK);
        return;
    }

    if ((my_lisawin->floppystate & FLOPPY_ANIM_MASK)!=FLOPPY_EMPTY) {
        wxMessageBox(_T("A previously inserted diskette is still in the drive. "
            "Please eject the diskette before inserting another."),
            _T("Diskette is already inserted!"), wxICON_INFORMATION | wxOK);
        return;
    }

    pause_run();

    wxString openfile;
    wxFileDialog open(this,                     wxT("Create and insert a blank microdiskette image"),
                                                wxEmptyString,
                                                wxT("blank.dc42"),
                                                wxT("Disk Copy (*.dc42)|*.dc42|All (*.*)|*.*"),
                                                (long int)wxFD_SAVE,wxDefaultPosition);
    if (open.ShowModal()==wxID_OK)              openfile=open.GetPath();

    resume_run();
    if (!openfile.Len()) return;


#ifdef ENABLE_EMULATOR

    const char *s = openfile.fn_str();
    int i = dc42_create((char *)s,"-not a Macintosh disk-", 400*512*2,400*2*12);
    if (i)  {
        wxMessageBox(_T("Could not create the diskette"),
            _T("Sorry"), wxICON_INFORMATION | wxOK);
        return;
    } else {
        floppy_insert((char *)s);
    }
#endif


 if ((my_lisawin->floppystate & FLOPPY_ANIM_MASK)==FLOPPY_EMPTY) {
     // initiate insert animation sequence
     my_lisawin->floppystate=FLOPPY_NEEDS_REDRAW|FLOPPY_ANIMATING|FLOPPY_INSERT_0;
 }
}

//  for future use - for when we figure out the right way to turn of skins on the fly
//  - the real issue is the window size mechanism doesn't work properly.  won't prevent the user
//  from stretching the window, or making it too small.  also in win32/linux with skins off
//  the window is too small - shows scrollbars, but it allows stretching past the lisa screen bitmap
//  which causes garbage in the non-refreshed space.
void LisaEmFrame::UnloadImages(void)
{
   if ( my_skinDC     )  {  delete my_skinDC;     my_skinDC      = NULL;}
   if ( my_skin0DC    )  {  delete my_skin0DC;    my_skin0DC     = NULL;}
   if ( my_skin1DC    )  {  delete my_skin1DC;    my_skin1DC     = NULL;}
   if ( my_skin2DC    )  {  delete my_skin2DC;    my_skin2DC     = NULL;}
   if ( my_skin3DC    )  {  delete my_skin3DC;    my_skin3DC     = NULL;}
   if ( my_floppy0DC  )  {  delete my_floppy0DC;  my_floppy0DC   = NULL;}
   if ( my_floppy1DC  )  {  delete my_floppy1DC;  my_floppy1DC   = NULL;}
   if ( my_floppy2DC  )  {  delete my_floppy2DC;  my_floppy2DC   = NULL;}
   if ( my_floppy3DC  )  {  delete my_floppy3DC;  my_floppy3DC   = NULL;}
   if ( my_floppy4DC  )  {  delete my_floppy4DC;  my_floppy4DC   = NULL;}
   if ( my_poweronDC  )  {  delete my_poweronDC;  my_poweronDC   = NULL;}
   if ( my_poweroffDC )  {  delete my_poweroffDC; my_poweroffDC  = NULL;}

   if ( my_skin       )  {  delete my_skin;       my_skin        = NULL;}
   if ( my_skin0      )  {  delete my_skin0;      my_skin0       = NULL;}
   if ( my_skin1      )  {  delete my_skin1;      my_skin1       = NULL;}
   if ( my_skin2      )  {  delete my_skin2;      my_skin2       = NULL;}
   if ( my_skin3      )  {  delete my_skin3;      my_skin3       = NULL;}
   if ( my_floppy0    )  {  delete my_floppy0;    my_floppy0     = NULL;}
   if ( my_floppy1    )  {  delete my_floppy1;    my_floppy1     = NULL;}
   if ( my_floppy2    )  {  delete my_floppy2;    my_floppy2     = NULL;}
   if ( my_floppy3    )  {  delete my_floppy3;    my_floppy3     = NULL;}
   if ( my_floppy4    )  {  delete my_floppy4;    my_floppy4     = NULL;}
   if ( my_poweron    )  {  delete my_poweron;    my_poweron     = NULL;}
   if ( my_poweroff   )  {  delete my_poweroff;   my_poweroff    = NULL;}

   screen_origin_x=0;
   screen_origin_y=0;
   skins_on=0;

   int x,y;
   my_lisawin->SetClientSize(wxSize(effective_lisa_vid_size_x,effective_lisa_vid_size_y));
   my_lisawin->GetClientSize(&x,&y);
         if (x!=effective_lisa_vid_size_x || y!=effective_lisa_vid_size_y)
         {

            ALERT_LOG(0,"Client size I got back:%d,%d differs from %d,%d",x,y,
                           effective_lisa_vid_size_x,effective_lisa_vid_size_y);
         }

   my_lisawin->GetSize(&x,&y);
// X11, Windows return too small a value for getsize
#ifndef  __WXOSX__
         x+=WINXDIFF; y+=WINYDIFF;
#endif
   SetMaxSize(wxSize(x,y));
   SetMinSize(wxSize(x,y));

   SendSizeEvent();
   Hide();
   Show(true);
   SendSizeEvent();
}

void LisaEmFrame::LoadImages(void)
{

/*
 * On MacOS X, load the images from the Resources/ dir inside the app bundle.
 * Much faster than WX's breathtakingly slow parsing of XPMs, and much
 * smaller too since we can use PNGs.
 * On Linux/Win32 use embedded XPM strings/BMP resources.
 */

if (skins_on)
{
   if (!my_skin) my_skin     = new wxBitmap(ISKINSIZEX,ISKINSIZEY);//, -1);  //20061228//

#ifdef __WXOSX__
   char *rscDir = getResourcesDir();
   char *imgName = (char *)malloc(MAXPATHLEN);

   sprintf(imgName, "%s/lisaface0.png", rscDir);
   if (!my_skin0) my_skin0 = new wxBitmap(imgName, wxBITMAP_TYPE_PNG);
   sprintf(imgName, "%s/lisaface1.png", rscDir);
   if (!my_skin1) my_skin1 = new wxBitmap(imgName, wxBITMAP_TYPE_PNG);
   sprintf(imgName, "%s/lisaface2.png", rscDir);
   if (!my_skin2) my_skin2 = new wxBitmap(imgName, wxBITMAP_TYPE_PNG);
   sprintf(imgName, "%s/lisaface3.png", rscDir);
   if (!my_skin3) my_skin3 = new wxBitmap(imgName, wxBITMAP_TYPE_PNG);

   sprintf(imgName, "%s/floppy0.png", rscDir);
   if (!my_floppy0) my_floppy0 = new wxBitmap(imgName, wxBITMAP_TYPE_PNG);
   sprintf(imgName, "%s/floppy1.png", rscDir);
   if (!my_floppy1) my_floppy1 = new wxBitmap(imgName, wxBITMAP_TYPE_PNG);
   sprintf(imgName, "%s/floppy2.png", rscDir);
   if (!my_floppy2) my_floppy2 = new wxBitmap(imgName, wxBITMAP_TYPE_PNG);
   sprintf(imgName, "%s/floppy3.png", rscDir);
   if (!my_floppy3) my_floppy3 = new wxBitmap(imgName, wxBITMAP_TYPE_PNG);
   sprintf(imgName, "%s/floppyN.png", rscDir);
   if (!my_floppy4) my_floppy4 = new wxBitmap(imgName, wxBITMAP_TYPE_PNG);

   sprintf(imgName, "%s/power_on.png", rscDir);
   if (!my_poweron ) my_poweron = new wxBitmap(imgName, wxBITMAP_TYPE_PNG);
   sprintf(imgName, "%s/power_off.png", rscDir);
   if (!my_poweroff) my_poweroff = new wxBitmap(imgName, wxBITMAP_TYPE_PNG);
   free(rscDir);
   free(imgName);

#else
   if (!my_skin0)     my_skin0     = new wxBITMAP( lisaface0 );                    // otherwise this used to work with wxGTK+
   if (!my_skin1)     my_skin1     = new wxBITMAP( lisaface1 );                    // otherwise this used to work with wxGTK+
   if (!my_skin2)     my_skin2     = new wxBITMAP( lisaface2 );                    // otherwise this used to work with wxGTK+
   if (!my_skin3)     my_skin3     = new wxBITMAP( lisaface3 );                    // otherwise this used to work with wxGTK+

   if (!my_floppy0)   my_floppy0   = new wxBITMAP( floppy0   );
   if (!my_floppy1)   my_floppy1   = new wxBITMAP( floppy1   );
   if (!my_floppy2)   my_floppy2   = new wxBITMAP( floppy2   );
   if (!my_floppy3)   my_floppy3   = new wxBITMAP( floppy3   );
   if (!my_floppy4)   my_floppy4   = new wxBITMAP( floppyN   );

   if (!my_poweron )  my_poweron   = new wxBITMAP( power_on  );
   if (!my_poweroff)  my_poweroff  = new wxBITMAP( power_off );

#endif

   if (!my_skinDC)     my_skinDC    = new wxMemoryDC;
   if (!my_skin0DC)    my_skin0DC   = new wxMemoryDC;
   if (!my_skin1DC)    my_skin1DC   = new wxMemoryDC;
   if (!my_skin2DC)    my_skin2DC   = new wxMemoryDC;
   if (!my_skin3DC)    my_skin3DC   = new wxMemoryDC;

   if (!my_floppy0DC)  my_floppy0DC = new wxMemoryDC;
   if (!my_floppy1DC)  my_floppy1DC = new wxMemoryDC;
   if (!my_floppy2DC)  my_floppy2DC = new wxMemoryDC;
   if (!my_floppy3DC)  my_floppy3DC = new wxMemoryDC;
   if (!my_floppy4DC)  my_floppy4DC = new wxMemoryDC;

   if (!my_poweronDC)  my_poweronDC = new wxMemoryDC;
   if (!my_poweroffDC) my_poweroffDC= new wxMemoryDC;

   my_skinDC->SelectObject(*my_skin);

   my_skin0DC->SelectObject(*my_skin0);
   my_skin1DC->SelectObject(*my_skin1);
   my_skin2DC->SelectObject(*my_skin2);
   my_skin3DC->SelectObject(*my_skin3);

   int y=0;
   my_skinDC->Blit(0,y, my_skin0->GetWidth(),my_skin0->GetHeight(),my_skin0DC, 0,0 ,wxCOPY, false);   y+=my_skin0->GetHeight();
   my_skinDC->Blit(0,y, my_skin1->GetWidth(),my_skin1->GetHeight(),my_skin1DC, 0,0 ,wxCOPY, false);   y+=my_skin1->GetHeight();
   my_skinDC->Blit(0,y, my_skin2->GetWidth(),my_skin2->GetHeight(),my_skin2DC, 0,0 ,wxCOPY, false);   y+=my_skin2->GetHeight();
   my_skinDC->Blit(0,y, my_skin3->GetWidth(),my_skin3->GetHeight(),my_skin3DC, 0,0 ,wxCOPY, false); //y+=my_skin3->GetHeight();

   my_floppy0DC->SelectObject(*my_floppy0);
   my_floppy1DC->SelectObject(*my_floppy1);
   my_floppy2DC->SelectObject(*my_floppy2);
   my_floppy3DC->SelectObject(*my_floppy3);
   my_floppy4DC->SelectObject(*my_floppy4);
   my_poweronDC->SelectObject(*my_poweron);
   my_poweroffDC->SelectObject(*my_poweroff);

   my_lisawin->SetMinSize(wxSize(IWINSIZE));
   my_lisawin->SetClientSize(wxSize(IWINSIZE));
   my_lisawin->SetMaxSize(wxSize(ISKINSIZE));
   my_lisawin->SetScrollbars(ISKINSIZEX/100, ISKINSIZEY/100,  100,100,  0,0,  true);
   my_lisawin->EnableScrolling(true,true);

   // these are evil, but hopefully they fix the sizing issues with non-skins
   SendSizeEvent();
   Hide();
   Show(true);
   SendSizeEvent();



   screen_origin_x=140;
   screen_origin_y=130;
  }

}



LisaEmFrame::LisaEmFrame(const wxString& title)
       : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE)
{
   effective_lisa_vid_size_y=500;
   wxInitAllImageHandlers();


    lastt2 = 0;
    lastclk = 0;



    SetIcon( wxICON( lisa2icon ));

    //SetInitialBestSize(wxSize(IWINSIZE));
    if (skins_on)
    {
       SetMinSize(wxSize(IWINSIZE));
       //SetSize(wxSize(IWINSIZE));
       SetClientSize(wxSize(IWINSIZE));
       SetMaxSize(wxSize(ISKINSIZE));
    }
    else
    {
       int x,y;
       SetClientSize(wxSize(effective_lisa_vid_size_x,effective_lisa_vid_size_y));
       GetClientSize(&x,&y);
       if (x!=effective_lisa_vid_size_x || y!=effective_lisa_vid_size_y)
       {

          ALERT_LOG(0,"Client size I got back:%d,%d differs from %d,%d",x,y,
                         effective_lisa_vid_size_x,effective_lisa_vid_size_y);
       }

       GetSize(&x,&y);
// X11, Windows return too small a value for getsize
#ifndef  __WXOSX__
         x+=WINXDIFF; y+=WINYDIFF;
#endif
       SetMaxSize(wxSize(x,y));
       SetMinSize(wxSize(x,y));
       //EnableScrolling(false,false);
    }

    //SetScrollbars(ISKINSIZEX/100, ISKINSIZEY/100,  100,100,  0,0,  true);
    //EnableScrolling(true,true);


    buildscreenymap();

    // Create a menu bar
    fileMenu     = new wxMenu;
    editMenu     = new wxMenu;
    keyMenu      = new wxMenu;
    DisplayMenu  = new wxMenu;
    throttleMenu = new wxMenu;
    helpMenu     = new wxMenu;



    editMenu->Append(wxID_PASTE, wxT("Paste") , wxT("Paste clipboard text to keyboard.") );

    DisplayMenu->AppendCheckItem(ID_VID_AA  ,    wxT("AntiAliased")           ,  wxT("Aspect Corrected with Anti Aliasing") );
    DisplayMenu->AppendCheckItem(ID_VID_AAG ,    wxT("AntiAliased with Grays"),  wxT("Aspect Corrected with Anti Aliasing and Gray Replacing") );
    //DisplayMenu->AppendCheckItem(ID_VID_SCALED,  wxT("Scaled")                ,  wxT("Scaled by wxWidgets") );

    DisplayMenu->AppendSeparator();
    DisplayMenu->AppendCheckItem(ID_VID_SY  ,    wxT("Raw")                   ,  wxT("Uncorrected Aspect Ratio") );
    DisplayMenu->AppendSeparator();

    DisplayMenu->AppendCheckItem(ID_VID_DY  ,    wxT("Double Y")              ,  wxT("Double Vertical Size") );
    DisplayMenu->AppendCheckItem(ID_VID_2X3Y,    wxT("Double X, Tripple Y")   ,  wxT("Corrected Aspect Ratio, Large Display") );

    DisplayMenu->AppendSeparator();

    DisplayMenu->AppendCheckItem(ID_VID_SKINS_ON,  wxT("Turn Skins On"),         wxT("Turn on skins") );
    DisplayMenu->AppendCheckItem(ID_VID_SKINS_OFF, wxT("Turn Skins Off"),        wxT("Turn off skins") );



    throttleMenu->AppendCheckItem(ID_THROTTLE5,  wxT("5 MHz")                  , wxT("5 MHz - Stock Lisa Speed, recommended.") );
    throttleMenu->AppendCheckItem(ID_THROTTLE8,  wxT("8 MHz")                  , wxT("8 MHz - Macintosh Speed") );
    throttleMenu->AppendCheckItem(ID_THROTTLE10, wxT("10 MHz")                 );
    throttleMenu->AppendCheckItem(ID_THROTTLE12, wxT("12 MHz")                 );
    throttleMenu->AppendCheckItem(ID_THROTTLE16, wxT("16 MHz")                 );
    throttleMenu->AppendCheckItem(ID_THROTTLE32, wxT("32 MHz")                 , wxT("Unattainable on today's machines") );



    // The "About" item should be in the help menu

    //helpMenu->Append(wxID_ABOUT, wxT("&License"),                 wxT("License Information"));
    //helpMenu->AppendSeparator();
    //helpMenu->Append(wxID_ABOUT, wxT("Help \tF1"),                wxT("Emulator Manual"));
    //helpMenu->Append(wxID_ABOUT, wxT("Lisa Help"),                wxT("How to use Lisa software"));

    helpMenu->AppendSeparator();

    helpMenu->Append(ID_LISAWEB, wxT("Lisa Emulator Webpage"),    wxT("http://lisaem.sunder.net"));
    helpMenu->Append(ID_LISAFAQ, wxT("Lisa FAQ webpage"),         wxT("http://lisafaq.sunder.net"));

    helpMenu->AppendSeparator();
    helpMenu->Append(wxID_ABOUT, wxT("&About LisaEm"),                   wxT("About the Lisa Emulator"));


    keyMenu->Append(ID_POWERKEY,         wxT("Power Button"),     wxT("Push the Power Button"));

    keyMenu->AppendSeparator();

    keyMenu->Append(ID_KEY_APL_DOT,      wxT("Apple ."),          wxT("Apple + ."));

    keyMenu->Append(ID_KEY_APL_S,        wxT("Apple S"),          wxT("Apple + S"));
    keyMenu->Append(ID_KEY_APL_ENTER,    wxT("Apple Enter"),      wxT("Apple + Enter"));
    keyMenu->Append(ID_KEY_APL_RENTER,   wxT("Apple Right Enter"),wxT("Apple + Numpad Enter"));

    keyMenu->Append(ID_KEY_APL_1,        wxT("Apple 1"),          wxT("Apple + 1"));
    keyMenu->Append(ID_KEY_APL_2,        wxT("Apple 2"),          wxT("Apple + 2"));
    keyMenu->Append(ID_KEY_APL_3,        wxT("Apple 3"),          wxT("Apple + 3"));


    keyMenu->Append(ID_KEY_OPT_0,        wxT("Option 0"),         wxT("Option 0"));
    keyMenu->Append(ID_KEY_OPT_4,        wxT("Option 4"),         wxT("Option 4"));
    keyMenu->Append(ID_KEY_OPT_7,        wxT("Option 7"),         wxT("Option 7"));

    keyMenu->AppendSeparator();
    keyMenu->Append(ID_KEY_NMI,          wxT("NMI Key"),          wxT("Send Non-Maskable Interrupt key - for LisaBug"));
    keyMenu->AppendSeparator();
    keyMenu->AppendCheckItem(ID_ASCIIKB, wxT("ASCII Keyboard"),    wxT("Translate host keys into ASCII, then to Lisa keys (preferred)"));
    keyMenu->AppendCheckItem(ID_RAWKB,   wxT("Raw Keyboard"),      wxT("Map host keys to Lisa keys directly"));

    //not-yet-used//keyMenu->AppendSeparator();
    //not-yet-used//keyMenu->Append(ID_KEYBOARD,         wxT("Keyboard"),         wxT("Lisa Keyboard"));

    fileMenu->Append(wxID_OPEN,          wxT("Open Preferences"), wxT("Open a LisaEm Preferences file"));
    fileMenu->Append(wxID_SAVEAS,        wxT("Save Preferences As"), wxT("Save current LisaEm Preferences to a new file"));
    fileMenu->Append(wxID_PREFERENCES,   wxT("Preferences"),      wxT("Configure this Lisa"));

    fileMenu->AppendSeparator();

    fileMenu->AppendCheckItem(ID_RUN,    wxT("Run"), wxT("Run Emulation") );
    fileMenu->AppendCheckItem(ID_PAUSE,  wxT("Pause"), wxT("Pause Emulation") );

    fileMenu->AppendSeparator();
    fileMenu->AppendCheckItem(  ID_PROFILEPWR,    wxT("ProFile power"),      wxT("Turn power on/off to the ProFile drives") );
    fileMenu->AppendSeparator();

    fileMenu->Append(  ID_FLOPPY,        wxT("Insert diskette"),    wxT("Insert a disk image"));
    fileMenu->Append(  ID_FLOPPY,        wxT("Insert blank diskette"),wxT("Create, and insert, a blank disk image"));
    fileMenu->AppendSeparator();
    fileMenu->Append(ID_SCREENSHOT,      wxT("Screenshot"),       wxT("Save the current screen as an image"));

    //if (my_skin!=NULL)
    fileMenu->Append(ID_SCREENSHOT2,     wxT("Full Screenshot"),  wxT("Save a screenshot along with the skin"));
    fileMenu->Append(ID_SCREENSHOT3,     wxT("Raw Screenshot"),   wxT("Save a raw screenshot"));


    fileMenu->AppendSeparator();

    fileMenu->Append(wxID_EXIT,          wxT("Exit"),             wxT("Quit the Emulator"));

    // Now append the freshly created menu to the menu bar...

    menuBar = new wxMenuBar();

    menuBar->Append(fileMenu, wxT("File"));
    menuBar->Append(editMenu, wxT("Edit"));
    menuBar->Append(keyMenu,  wxT("Key"));
    menuBar->Append(DisplayMenu, wxT("Display"));
    menuBar->Append(throttleMenu, wxT("Throttle"));
    menuBar->Append(helpMenu, wxT("&Help"));


    SetMenuBar(menuBar);

    CreateStatusBar(1);

    SetStatusText(wxT("Welcome to the Lisa Emulator Project!"));
    soundplaying=0;

    if (!my_lisawin) my_lisawin = new LisaWin(this);

    my_lisawin->doubley = 0;
    my_lisawin->dirtyscreen = 1;
    my_lisawin->brightness = 0;
    my_lisawin->refresh_bytemap = 1;


   if (skins_on)
      {
           int x,y;
           LoadImages();

           x=my_skin->GetWidth();
           y=my_skin->GetHeight();

           my_lisawin->SetMaxSize(wxSize(x,y));
           my_skinDC->SelectObject(*my_skin);

           my_lisawin->SetVirtualSize(x,y);
           my_lisawin->SetScrollbars(x/100, y/100,  100,100,  0,0,  true);
           my_lisawin->EnableScrolling(true,true);
      }
   else
      {
           int x,y;
           //my_lisawin->
           SetClientSize(wxSize(effective_lisa_vid_size_x,effective_lisa_vid_size_y));
           GetClientSize(&x,&y);
           //if (x!=effective_lisa_vid_size_x || y!=effective_lisa_vid_size_y)
           //{
           //   ALERT_LOG(0,"Client size I got back:%d,%d differs from %d,%d",x,y,
           //                effective_lisa_vid_size_x,effective_lisa_vid_size_y);
           //}
           my_lisawin->GetSize(&x,&y);
// X11, Windows return too small a value for getsize
#ifndef  __WXOSX__
         x+=WINXDIFF; y+=WINYDIFF;
#endif
           my_lisawin->SetMaxSize(wxSize(x,y));
           my_lisawin->SetMinSize(wxSize(x,y));
           my_lisawin->EnableScrolling(false,false);
      }
    SendSizeEvent();
    Hide();
    Show(true);
    SendSizeEvent();

    my_lisawin->Show(true);

    keyMenu->Check(ID_ASCIIKB, true);
    keyMenu->Check(ID_RAWKB,   false);
    throttleMenu->Check(ID_THROTTLE5,true);
    fileMenu->Check(ID_PAUSE,false);
    fileMenu->Check(ID_RUN,  false);
    DisplayMenu->Check(ID_VID_AA, true);
}


// copied/based on wxWidgets samples image.cpp (c) 1998-2005 Robert Roebling
void LisaEmFrame::OnScreenshot(wxCommandEvent& event)
{
 wxImage* image=NULL;

    char *description;

    pause_run();

    if ( event.GetId()==ID_SCREENSHOT3)
         {

          description = (char *)("Save RAW Screenshot");

          wxBitmap     *rawlisa=NULL;
          wxMemoryDC   memDC;

          rawlisa=new class wxBitmap(lisa_vid_size_x,lisa_vid_size_y); //,my_lisaframe->depth);
          memDC.SelectObject(*rawlisa);


          int val, valh, vall;
          int i=0;
          for (int y=0; y<lisa_vid_size_y; y++)
           for (int x=0; x<lisa_vid_size_x; x+=8)
           {
            #ifdef ENABLE_EMULATOR
            val=lisaram[videolatchaddress+(i++)];
            #else
            val=screenshot[i]; i++;
            #endif

            valh=(val>>4) & 15;
            vall=(val   ) & 15;

            memDC.Blit(x  ,y,  4, 1, my_bytemapDC, 0,valh, wxCOPY,false);
            memDC.Blit(x+4,y,  4, 1, my_bytemapDC, 0,vall, wxCOPY,false);
           }

          image = new class wxImage(rawlisa->ConvertToImage());
          delete rawlisa;



         }
    else  if ( event.GetId()==ID_SCREENSHOT2 && skins_on)
         {
          description = (char *)("Save Screenshot with Skin");
          image = new class wxImage(my_skin->ConvertToImage());
         }
    else if ( event.GetId()==ID_SCREENSHOT ||( event.GetId()==ID_SCREENSHOT2 && !skins_on) )
         {
          image = new class wxImage(my_lisabitmap->ConvertToImage());
          description = (char *)("Save Screenshot");
         }
    else
         {
          resume_run();
		  return;
		 }


wxString filter;
#ifdef wxUSE_LIBPNG
                                        filter+="PNG files (*.png)|";
                                        //filter+="PNG files (*.png)|*.png|";
#endif
#ifdef wxUSE_LIBJPEG
                                        filter+="JPEG files (*.jpg)|";
#endif
#ifdef wxUSE_LIBTIFF
                                        filter+="TIFF files (*.tif)|";
#endif

//#ifdef wxUSE_LIBGIF
//                                      filter+="GIF files (*.gif)|";
//#endif

#ifdef wxUSE_PCX
                                        filter+="PCX files (*.pcx)|";
#endif
                                        filter+="BMP files (*.bmp)|";

    wxString savefilename = wxFileSelector( wxT(description),
                                            wxEmptyString,
                                            wxEmptyString,
                                            wxEmptyString,
                                            wxT(filter),
#ifdef wxFD_SAVE
                                            wxFD_SAVE);
#else
                                            wxSAVE);
#endif


    if ( savefilename.empty() )  {resume_run();  return; }

    wxString extension;
    wxFileName::SplitPath(savefilename, NULL, NULL, &extension);

    bool saved = false;
    if ( extension == _T("bpp") )
    {
            image->SetOption(wxIMAGE_OPTION_BMP_FORMAT, wxBMP_1BPP_BW);
    }
    else if ( extension == _T("png") )
    {
            image->SetOption(wxIMAGE_OPTION_PNG_FORMAT, wxPNG_TYPE_GREY);
            image->SetOption(wxIMAGE_OPTION_PNG_BITDEPTH, 8);
    }

    if ( !saved )
    {
        // This one guesses image format from filename extension
        // (it may fail if the extension is not recognized):
        image->SaveFile(savefilename);
    }

	/*test */
	wxImage *nibbleimg = new wxImage(my_bytemap->ConvertToImage());
	nibbleimg->SaveFile("lisa_nibbles.png");
	wxImage *aliasimg = new wxImage(my_alias->ConvertToImage());
	aliasimg->SaveFile("lisa_aliasimg.png");
	delete nibbleimg;
	delete aliasimg;

    resume_run();
}

/* Connects a Dual Parallel port card to the specified slot (numbered 0-2). */
void connect_2x_parallel_to_slot(int slot)
{
    int low, high, v;
    switch (slot)
    {
        case 0:
            get_exs0_pending_irq = get_exs0_pending_irq_2xpar;
            low = Ox0000_slot1l; high = Ox2000_slot1h; v = 3;
            break;
        case 1:
            get_exs1_pending_irq = get_exs1_pending_irq_2xpar;
            low = Ox4000_slot2l; high = Ox6000_slot2h; v = 5;
            break;
        case 2:
            get_exs2_pending_irq = get_exs2_pending_irq_2xpar;
            low = Ox8000_slot3l; high = Oxa000_slot3h; v = 7;
            break;
        default:
           //ALERT_LOG(0, "Unknown slot number %d, should be 0-2!", slot);
           return;
    }

    mem68k_memptr    [low] = lisa_mptr_2x_parallel_l;
    mem68k_fetch_byte[low] = lisa_rb_2x_parallel_l;
    mem68k_fetch_word[low] = lisa_rw_2x_parallel_l;
    mem68k_fetch_long[low] = lisa_rl_2x_parallel_l;
    mem68k_store_byte[low] = lisa_wb_2x_parallel_l;
    mem68k_store_word[low] = lisa_ww_2x_parallel_l;
    mem68k_store_long[low] = lisa_wl_2x_parallel_l;

    mem68k_memptr    [high] = lisa_mptr_2x_parallel_h;
    mem68k_fetch_byte[high] = lisa_rb_2x_parallel_h;
    mem68k_fetch_word[high] = lisa_rw_2x_parallel_h;
    mem68k_fetch_long[high] = lisa_rl_2x_parallel_h;
    mem68k_store_byte[high] = lisa_wb_2x_parallel_h;
    mem68k_store_word[high] = lisa_ww_2x_parallel_h;
    mem68k_store_long[high] = lisa_wl_2x_parallel_h;

    reset_via(v);
    reset_via(v+1);
    via[v].active = 1;
    via[v+1].active = 1;
}

/* Connects a printer/profile to the specified VIA */
void connect_device_to_via(int v, wxString device, wxString *file)
{
    if (device.IsSameAs("ADMP", false))
    {
        via[v].ADMP = v;
        ImageWriter_LisaEm_Init(v);                 // &ADMP,pcl,ps,xlib
        //ALERT_LOG(0, "Attached ADMP to VIA#%d", v);
        return;
    }

    if (device.IsSameAs("PROFILE", false))
    {
        if (file->Len()==0)
        {
         static const wxString def[]={  _T(""),_T(""),
                            _T("lisaem-profile.dc42"),
                            _T("lisaem-s1h-profile.dc42"),
                            _T("lisaem-s1l-profile.dc42"),
                            _T("lisaem-s2h-profile.dc42"),
                            _T("lisaem-s2l-profile.dc42"),
                            _T("lisaem-s3h-profile.dc42"),
                            _T("lisaem-s3l-profile.dc42") };

         if (v>1 && v<9) *file =def[v];
         else return;
        }
        DEBUG_LOG(0, "Attempting to attach VIA#%d to profile %s", v, file->c_str());

        via[v].ProFile = (ProFileType *)malloc(sizeof(ProFileType));
        int i = profile_mount((char *)file->c_str(), via[v].ProFile);
        if (i) {
            free(via[v].ProFile);
            via[v].ProFile = NULL;
            DEBUG_LOG(0, "Couldn't get profile because: %d",i);
        } else {
            ProfileReset(via[v].ProFile);
        }
    }
}

/*
 * Connect some (virtual) device to one of the serial ports. 0 is Serial A,
 * 1 is Serial B. A number of the parameters passed in may be modified by
 * this code.
 */
void connect_device_to_serial(int port, FILE **scc_port_F, uint8 *serial,
    wxString *setting, wxString *param, int *scc_telnet_port)
{
    if (port !=0 && port != 1) {
        DEBUG_LOG(0, "Warning Serial port is not A/B: %d", port);
        return;
    }

    if (setting->IsSameAs("LOOPBACK",false)) {
        *scc_port_F = NULL;
        *serial = SCC_LOOPBACKPLUG;
        return;
    }

    if (setting->IsSameAs("NOTHING", false) || setting->IsSameAs("NULL", false) || setting->Len()==0) {
        *scc_port_F = NULL;
        *serial = SCC_NOTHING;
        *setting = "NOTHING";  // fill in for writing into ini file
        return;
    }

    if (setting->IsSameAs("FILE", false))
    {
        *scc_port_F = fopen(*param, "r+b");
        if (!*scc_port_F) {
            wxString err = wxString();
            err.Printf("Could not map Serial port %c to file %s", (port==0?'A':'B'), param->c_str());
            wxMessageBox(err, _T("Serial port configuration"),  wxICON_INFORMATION | wxOK);
            *scc_port_F = NULL;
            *serial = SCC_NOTHING;
        } else {
            *serial = SCC_FILE;
        }
        return;
    }

    if (setting->IsSameAs("PIPE", false)) {
        *scc_port_F = popen(*param, "r+b");
        if (!*scc_port_F) {
            wxString err = wxString();
            err.Printf("Could not map Serial port %c to pipe %s", (port==0?'A':'B'), param->c_str());
            wxMessageBox(err, _T("Serial port configuration"),  wxICON_INFORMATION | wxOK);
            *scc_port_F = NULL;
            *serial = SCC_NOTHING;
        } else {
            *serial = SCC_PIPE;
        }
        return;
    }

#ifndef __MSVCRT__
    if (setting->IsSameAs("TELNETD", false)) {
        unsigned long x;
        *scc_port_F = NULL;
        *serial = SCC_TELNETD;

        *scc_telnet_port = (param->ToULong(&x, 10)==false) ? 9300+port : x;
        init_telnet_serial_port(port);
        return;
    }
#endif

    // Not sure if we'll keep the image writer a separate program or if it
    // will be built in in the future.  This is if it's built in:
    if (setting->IsSameAs("IMAGEWRITER", false)) {
        *scc_port_F = NULL;
        *serial = SCC_IMAGEWRITER_PS;
        ImageWriter_LisaEm_Init(port);
        return;
    }

    /* no longer used
    if (setting->IsSameAs("IMAGEWRITER-PCL", false)) {
        *scc_port_F = NULL;
        *serial = SCC_IMAGEWRITER_PCL;

        if ((*scc_IW=(ImageWriterType *)malloc(sizeof(ImageWriterType)))==NULL) {
            wxString err = wxString();
            err.Printf("Could not attach PCL ImageWriter to Serial port %c", (port==0?'A':'B'));
            wxMessageBox(err, _T("Serial port configuration"), wxICON_INFORMATION | wxOK);
            *serial = SCC_NOTHING;
        } else {
            (*scc_IW)->page = NULL;
            ImageWriter_LisaEm_Init(port, 1, 0, 0);
        }
        return;
    }
    */
    DEBUG_LOG(0, "Warning: unrecognised Serial %c setting '%s'", port==0?'A':'B', setting->c_str());
}

 int initialize_all_subsystems(void)
 {
   /*
   In the beginning there was data.  The data was without form and null,
   and darkness was upon the face of the console; and the Spirit of IBM
   was moving over the face of the market.  And DEC said, "Let there be
   registers"; and there were registers.  And DEC saw that they carried;
   and DEC separated the data from the instructions.  DEC called the data
   Stack, and the instructions they called Code.  And there was evening
   and there was morning, one interrupt.         -- Rico Tudor            */
       if (my_lisaframe->running) {//ALERT_LOG(0,"Already running!");
        return 0;}

       buglog=stderr;

       //debug_on("Initializing");


       //  iw_initialize(IW);
       DEBUG_LOG(0,"initializing all subsystems...");

       // initialize parity array (it's 2x faster to use an array than to call the fn)
       DEBUG_LOG(0,"Parity calculation cache array...");
       {uint16 i;
       for (i=0; i<256; i++) eparity[i]=evenparity((uint8) i);
       }

       segment1=0; segment2=0; start=1;

       // needs to be above read_config_files
       scc_a_port=NULL;
       scc_b_port=NULL;
       scc_a_IW=0;
       scc_b_IW=1;
       serial_a=0;
       serial_b=0;

       init_floppy(my_lisaconfig->iorom);

       bitdepth=8;                             // have to get this from the X Server side...


       DEBUG_LOG(0,"serial number");
       serialnum[0]=0x00;                      // "real" ones will be loaded from the settings file
       serialnum[1]=0x00;
       serialnum[2]=0x00;
       serialnum[3]=0x00;
       serialnum[4]=0x00;
       serialnum[5]=0x00;
       serialnum[6]=0x00;
       serialnum[7]=0x00;
       serialnumshiftcount=0; serialnumshift=0;


       // Is there no explicit way to init the COPS?
       DEBUG_LOG(0,"copsqueue");
       copsqueuelen = 0;
       memset(copsqueue,0,MAXCOPSQUEUE);
       lisa_clock.year = 0;         // flag to get real time from host OS on first Lisa timer request.


       DEBUG_LOG(0,"irq handlers");
       init_IRQ();


       DEBUG_LOG(0,"vias");
       init_vias();


       DEBUG_LOG(0,"profile hd's");
       init_Profiles();


      // removed 2005.11.10
      // switch(maxlisaram)
      // {
      //  case 512  : TWOMEGMLIM=0x0007ffff; break;
      //  case 1024 : TWOMEGMLIM=0x000fffff; break; **** bug here ***
      //  case 1536 : TWOMEGMLIM=0x001fFFFF; break;  // this might not wrap around property!
      //  default:    TWOMEGMLIM=0x001fffff; maxlisaram=2048; break;
      // }

       TWOMEGMLIM=0x001fffff;

      // Simulate physical Lisa memory boards
      switch(my_lisaconfig->mymaxlisaram)
      {
       case 512  : maxlisaram=0x100000;  minlisaram=0x080000;  break;     // single 512KB board in slot 1
       case 1024 : maxlisaram=0x180000;  minlisaram=0x080000;  break;     // two 512KB boards in slot1, slot 2
       case 1536 : maxlisaram=0x200000;  minlisaram=0x080000;  break;     // 512KB board in slot1, 1024KB board in slot 2
       default:    maxlisaram=0x200000;  minlisaram=0x000000;  break;     // two 1024KB boards in slot 1,2
      }

       // maxlisaram! New!  And! Improved! Now available in bytes at a register Store Near You!  OpCodes are Standing by!
       // Act now!  don't delay!  Limited Time Offer!  JSR Now!  Restrictions Apply!  Before engaging in any strenous programming
       // activity, you should always consult your MMU today!  Not 0xFD1C insured!


      TWOMEGMLIM=maxlisaram-1;
   videolatchaddress=maxlisaram-0x10000;

   videolatch=(maxlisaram>>15)-1; // last page of ram is by default what the video latch gets set to.
   lastvideolatch=videolatch;  lastvideolatchaddress=videolatchaddress;

   if (lisaram) free(lisaram);          // remove old junk if it exists

   lisaram=(uint8 *)malloc(maxlisaram+512);     // added 512 bytes at the end to avoid overflows.
   memset(lisaram,0xff,maxlisaram+128);

   if (my_lisaconfig->kbid) set_keyboard_id(my_lisaconfig->kbid);  else set_keyboard_id(-1);


    // Parse sn and correct if needed  ////////////////////////////////////////////////////////////////////////////////////////
	{


        //char mybuffer[5];
        int i,j; char c;
        char *s=(char *)(my_lisaconfig->myserial.c_str());
        for (i=0; (c=s[i]); i++)
            if ( !ishex(c) )
			{
                //ALERT_LOG(1,"Warning, invalid serial number: (%s) reset to default.\n",s );
                my_lisaconfig->myserial.Printf("%s",LISA_CONFIG_DEFAULTSERIAL); break;
			}

        if (i<31) DEBUG_LOG(1,"Warning: serial # less than 32 bytes (%d)!\n",i);

        for (i=0,j=0; i<32; i+=2,j++)
                serialnum240[j]=(gethex(s[i])<<4)|gethex(s[i|1]);

        vidfixromchk(serialnum240);



        my_lisaconfig->myserial.Printf("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
                        serialnum240[0],                 serialnum240[1],
                        serialnum240[2],                 serialnum240[3],
                        serialnum240[4],                 serialnum240[5],
                        serialnum240[6],                 serialnum240[7],
                        serialnum240[8],                 serialnum240[9],
                        serialnum240[10],                serialnum240[11],
                        serialnum240[12],                serialnum240[13],
                        serialnum240[14],                serialnum240[15]);

        DEBUG_LOG(0,"Serial # used: %s", my_lisaconfig->myserial.c_str());


    }//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    // If either serial port is set as loopback, both must be.
    if (my_lisaconfig->serial2_setting.IsSameAs("LOOPBACK", false) ||
        my_lisaconfig->serial1_setting.IsSameAs("LOOPBACK", false)) {
        my_lisaconfig->serial1_setting = "LOOPBACK";
        my_lisaconfig->serial2_setting = "LOOPBACK";
    }


    connect_device_to_serial(0, &scc_a_port_F, &serial_a,
        &my_lisaconfig->serial1_setting, &my_lisaconfig->serial1_param,
        &scc_a_telnet_port);


    connect_device_to_serial(1, &scc_b_port_F, &serial_b,
        &my_lisaconfig->serial2_setting, &my_lisaconfig->serial2_param,
        &scc_b_telnet_port);

    DEBUG_LOG(0,"Initializing SCC Z8530");
    initialize_scc();

    if (!lisaram) {
        wxMessageBox(_T("Could not allocate memory for the Lisa to use."),
            _T("Out of Memory!"), wxICON_INFORMATION | wxOK);
        return 23;
    }

  DEBUG_LOG(0,"setting motherboard latches");
  softmemerror=0; harderror=0; videoirq=0; bustimeout=0; videobit=0;

  DEBUG_LOG(0,"Checking host CPU bit order sanity.");
  reg68k_sanity_check_bitorder();

  DEBUG_LOG(0,"generator cpu_68k fn");
  cpu68k_init();

  DEBUG_LOG(0,"lisa memory management unit (A30N-V2.0)");
  init_lisa_mmu();

  DEBUG_LOG(0,"initializing ipc allocator");
  init_ipct_allocator();

  DEBUG_LOG(0,"Rom to use: %s",(char *)(my_lisaconfig->rompath.c_str() ));

  if (!my_lisaconfig->rompath.Len() )
  {
    wxMessageBox(_T(
   "Please configure the Lisa before starting it. Specifially, it needs a ROM as well as a Profile definition."
                   ), _T("Configuration Needed!"), wxICON_INFORMATION | wxOK);

          if (!my_LisaConfigFrame)    my_LisaConfigFrame=new LisaConfigFrame( wxT("Preferences"), my_lisaconfig);
          #if defined(__WXMOTIF__)
          int x, y;

          my_LisaConfigFrame->GetSize(&x, &y);
#ifndef  __WXOSX__
         x+=WINXDIFF; y+=WINYDIFF;
#endif
          my_LisaConfigFrame->SetSize(wxDefaultCoord, wxDefaultCoord,x,y);
          #endif

          my_LisaConfigFrame->Show();

          return -2;
  }


  if (my_lisaconfig->rompath.Len())
  {
      DEBUG_LOG(0,"Loading Lisa ROM");
      if      (read_dtc_rom((char *)(my_lisaconfig->rompath.c_str()),   lisarom)==0)
               {
                if (checkromchksum())
                   {
                    if (!yesnomessagebox(wxT("BOOT ROM checksum doesn't match, this may crash the emulator.  Continue anyway?"),
                         wxT("ROM Checksum mismatch"))  ) return -2;
                   }
                 DEBUG_LOG(0,"Load of DTC assembled ROM successful");
                 fixromchk();
               }
      else if (read_split_rom((char *)(my_lisaconfig->rompath.c_str()), lisarom)==0)
               {
                if (checkromchksum())
                   {
                    if (!yesnomessagebox(wxT("BOOT ROM checksum doesn't match, this may crash the emulator.  Continue anyway?"),
                         wxT("ROM Checksum mismatch"))  ) return -2;
                   }

                  DEBUG_LOG(0,"Load of split ROM");
                  fixromchk();
               }
      else if (read_rom((char *)(my_lisaconfig->rompath.c_str()),       lisarom)==0)
               {
                if (checkromchksum())
                   {
                    if (!yesnomessagebox(wxT("BOOT ROM checksum doesn't match, this may crash the emulator.  Continue anyway?"),
                         wxT("ROM Checksum mismatch"))  ) return -2;
                   }

                  DEBUG_LOG(0,"Load of normal ROM successful");
                  fixromchk();
               }
      else
      {

          wxMessageBox(_T("Could not open/find a suitable boot ROM"), _T("ROM Needed!!!"), wxICON_INFORMATION | wxOK);
          // Homer: No TV and no beer make Homer something something");
          // Marge: Go crazy?");
          // Homer: Don't mind if I do!");
          return -2;
      }
	  //my_lisaconfig->save();
  }
  else
  {
          wxMessageBox(_T("Could not open/find a suitable boot ROM or path is empty."), _T("ROM Needed"), wxICON_INFORMATION | wxOK);
          return -3;
  }

  if (has_xl_screenmod())
  {

   // This warning falg isn't really a configuration setting as such, but leave it here for the moment
   if (!my_lisaconfig->saw_3a_warning)
   wxMessageBox(_T("This is the XL Screen Modification ROM! You will only be able to run MacWorks."),
                    _T("3A ROM!"),   wxICON_INFORMATION | wxOK);


   my_lisaconfig->saw_3a_warning=1;
   lisa_vid_size_x=608;
   lisa_vid_size_y=431;
   effective_lisa_vid_size_x=608;
   effective_lisa_vid_size_y=431;

   screen_origin_x=140+56;
   screen_origin_y=130+34;

   lisa_vid_size_x=608;
   lisa_vid_size_y=431;

   lisa_vid_size_xbytes=76;
   has_lisa_xl_screenmod=1;

   disable_display_menu();
   setvideomode(0x3a);
  }
  else
  {
   lisa_vid_size_x=720;
   lisa_vid_size_y=364;
   effective_lisa_vid_size_x=720;
   effective_lisa_vid_size_y=500;

   screen_origin_x=140;
   screen_origin_y=130;

   lisa_vid_size_x=720;
   lisa_vid_size_y=364;


   lisa_vid_size_xbytes=90;
   has_lisa_xl_screenmod=0;

   setvideomode(lisa_ui_video_mode);

   enable_display_menu();


  }

  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  // Handle Parallel Ports

  // Connect profile/printer to builtin parallel port
  connect_device_to_via(2, my_lisaconfig->parallel, &my_lisaconfig->parallelp);

  // ----------------------------------------------------------------------------------

  // Dual Parallel Port Expansion Cards

  memset(dualparallelrom,0xff,2048);

  // First, we have to load the card ROM, without it, it won't work.
   if (my_lisaconfig->dualrom.Len())
   {
    if (read_parallel_card_rom((char *)(my_lisaconfig->dualrom.c_str()))==0)
       {
        // for now only Dual Parallel Port cards are supported.

          if (my_lisaconfig->slot1.IsSameAs("dualparallel",false) )
            {
                    connect_2x_parallel_to_slot(0);
                    connect_device_to_via(3, my_lisaconfig->s1h, &my_lisaconfig->s1hp);
                    connect_device_to_via(4, my_lisaconfig->s1l, &my_lisaconfig->s1lp);
            }

          if (my_lisaconfig->slot2.IsSameAs("dualparallel",false) )
            {
                    connect_2x_parallel_to_slot(1);
                    connect_device_to_via(5, my_lisaconfig->s2h, &my_lisaconfig->s2hp);
                    connect_device_to_via(6, my_lisaconfig->s2l, &my_lisaconfig->s2lp);
            }


          if (my_lisaconfig->slot3.IsSameAs("dualparallel",false))
            {
                    connect_2x_parallel_to_slot(2);
                    connect_device_to_via(7, my_lisaconfig->s3h, &my_lisaconfig->s3hp);
                    connect_device_to_via(8, my_lisaconfig->s3l, &my_lisaconfig->s3lp);
            }


       }
    else
       {
        wxMessageBox(_T("Could not open/find a suitable Parallel Card ROM. Expansion slots disabled."),
                     _T("Parallel Card ROM Needed"),
                        wxICON_INFORMATION | wxOK);
       }
   }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////



    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    DEBUG_LOG(0,"initializing video");
    disable_vidram(); videoramdirty|=9;

    DEBUG_LOG(0,"Calling Redraw Pixels");
    LisaRedrawPixels();


    DEBUG_LOG(0,"Resetting cpu68k");
    cpu68k_reset();

    DEBUG_LOG(0,"done initializing...");

    debug_off();


  return 0;
  } ///////////////////// end of load.///////////////////


 // just to be a bastard!  why? because split roms are lame!
 extern "C" void rename_rompath(char *rompath)
 {
   if (!my_lisaconfig)  return;

   my_lisaconfig->rompath=rompath;
   my_lisaconfig->Save(pConfig, floppy_ram);

   if (my_LisaConfigFrame) my_LisaConfigFrame->m_rompath->SetValue(my_lisaconfig->rompath);
   DEBUG_LOG(0,"saved %s as default\n",rompath);
 }

extern "C" void force_refresh(void)                    //20060202.;
{
            my_lisaframe->Refresh(false,NULL);
            my_lisaframe->Update();
}

extern "C" void save_pram(void)
{
 my_lisaconfig->Save(pConfig, floppy_ram);// save it so defaults are created
}

void invalidate_configframe(void) {my_LisaConfigFrame=NULL;}


void save_configs(void)
{
  my_lisaconfig->Save(pConfig, floppy_ram);// save it so defaults are created
  save_global_prefs();
}


            //               0     1     2     3     4     5     6
            //              5M   10M   16M   20M   32M   40M    64M
extern "C" int pickprofilesize(char *filename)
{
 wxString choices[]={    wxT( "5M - any OS"),
                         wxT("10M - any OS"),
                         wxT("16M - MacWorks only"),
                         wxT("20M - MacWorks only"),
                         wxT("32M - MacWorks only"),
                         wxT("40M - MacWorks only"),
                         wxT("64M - MacWorks only") };

 wxString txt;
 wxSingleChoiceDialog *d;
 txt.Printf("%s does not exist.  What size drive should I create?",filename);

 d=new wxSingleChoiceDialog(my_lisaframe,
                        txt,
                        wxT("Hard Drive Size?"),
                        7,             choices);

 if (d->ShowModal()==wxID_CANCEL) {delete d; return -1;}
 int r=d->GetSelection();
 delete d;
 return r;
}


// bridge to LisaConfigFrame to let it know what config file is opened.
wxString get_config_filename(void)  { return myconfigfile;}

// due to window sizing bugs, this will require a restart.  I know, it sucks, it sucks a lot.
// if you have a way of fixing this, please submit a bug fix. :-(
//void turn_skins_on(void)   { skins_on_next_run=1; save_global_prefs(); wxMessageBox(_T("LisaEm with start with skins the next time you run it."), _T("Skins turned on"), wxICON_INFORMATION | wxOK);}; //my_lisaframe->LoadImages();  black(); my_lisaframe->Hide(); my_lisaframe->Show(true); setvideomode(lisa_ui_video_mode);}
//void turn_skins_off(void)  { skins_on_next_run=0; save_global_prefs(); wxMessageBox(_T("LisaEm will start without skins the next time you run it."), _T("Skins turned off"), wxICON_INFORMATION | wxOK);}; //my_lisaframe->UnloadImages();black(); my_lisaframe->Hide(); my_lisaframe->Show(true); setvideomode(lisa_ui_video_mode);}

void turn_skins_on(void)   { skins_on_next_run=1; skins_on=1; save_global_prefs(); my_lisaframe->LoadImages();  black(); my_lisaframe->Hide(); my_lisaframe->Show(true); setvideomode(lisa_ui_video_mode);}
void turn_skins_off(void)  { skins_on_next_run=0; skins_on=0; save_global_prefs(); my_lisaframe->UnloadImages();black(); my_lisaframe->Hide(); my_lisaframe->Show(true); setvideomode(lisa_ui_video_mode);}

#ifdef ENABLE_EMULATOR

extern "C" void contrastchange(void) {my_lisawin->ContrastChange();}

void setvideomode(int mode) {my_lisawin->SetVideoMode(mode);}



//----- ImageWriter interfaces to old C code. -----//

#include "imagewriter-wx.h"

ImageWriter *imagewriter[10];           // two serial ports, one parallel, 6 max dual parallel port cards, 9 possible


extern "C" int ImageWriter_LisaEm_Init(int iwnum)
{
 if (iwnum>10) return -1;

 imagewriter[iwnum]=new ImageWriter(my_lisaframe,
                                    my_lisaconfig->iw_png_on,
                                    my_lisaconfig->iw_png_path,
                                    my_lisaconfig->iw_dipsw_1);

 imagewriter[iwnum]->iwid=iwnum;         // set printer ID
 return 0;
}

extern "C" void iw_shutdown(void)
{
  int i;
  for (i=0; i<10; i++) if (imagewriter[i]!=NULL) {delete imagewriter[i]; imagewriter[i]=NULL;}
}

extern "C" void iw_formfeed(int iw)              {if (iw<10 && iw>-1 && imagewriter[iw]) imagewriter[iw]->iw_formfeed();     }
extern "C" void ImageWriterLoop(int iw,uint8 c)  {if (iw<10 && iw>-1 && imagewriter[iw]) imagewriter[iw]->ImageWriterLoop(c);}



void uninit_gui(void);

// from generator
unsigned int gen_quit = 0;
unsigned int gen_debugmode = 1;






#ifdef DEBUG
void    dumpallmmu(void);
#endif
void    dumpvia(void);

/////------------------------------------------------------------------------------------------------------------------------


void banner(void)
{

 //         .........1.........2.........3.........4.........5.........6.........7
 //         123456789012345678901234567890123456789012345678901234567890123456789012345678
    printf("\n\n");
    printf("-----------------------------------------------------------------------\n");
    printf("  The Lisa II Emulator   Version 0.9.9-DEV   http://lisaem.sunder.net  \n");
    printf("  -------------------------------------------------------------------  \n");
    printf("  Copyright  (C)   MMVI   by Ray A. Arachelian,   All Rights Reserved  \n");
    printf("  Released  under  the terms of  the  GNU Public License  Version 2.0  \n");
    printf("  -------------------------------------------------------------------  \n");
    printf("  For historical/archival/educational use only - no warranty provied.  \n");
    printf("  -------------------------------------------------------------------  \n");
    printf("  Portions of this software contain bits of code from the following:   \n");
    printf("  generator - www.squish.net/generator  wxWidgets.org  - www.ijg.org   \n");
    printf("   Many thanks to David T. Craig for the tons of Lisa documentation    \n");
    printf("     Many thanks to James McPhail for Lisa and 68000 hardware help     \n");
    printf("-----------------------------------------------------------------------\n\n");

#ifdef DEBUG
    printf("\nDEBUG is compiled in.\n");
    fprintf(buglog,"\nDEBUG is compiled in.\n");
#endif

#ifdef DEBUGMEMCALLS
    printf("\nDEBUGMEMCALLS is enabled.\n");
    fprintf(buglog,"\nDEBUGMEMCALLS is enabled.\n");
    ALERT_LOG(0,"DEBUGMEMCALLS is on.");
#endif
}



//uint8 evenparity(uint8 data)          // naive way
//{ return  !( (   (data & 0x01) ? 1:0) ^
//             (   (data & 0x02) ? 1:0) ^
//             (   (data & 0x04) ? 1:0) ^
//             (   (data & 0x08) ? 1:0) ^
//             (   (data & 0x10) ? 1:0) ^
//             (   (data & 0x20) ? 1:0) ^
//             (   (data & 0x40) ? 1:0) ^
//             (   (data & 0x80) ? 1:0)   );}
//

uint8 evenparity(uint8 data)            // from http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel
{
 uint32 v=data;
//v ^= v >> 16;       // not needed since we're working on a byte - only here for completeness, but commented out
//v ^= v >> 8;        // ditto
  v ^= v >> 4;
  v &= 0xf;
  return !(  (0x6996 >> v) & 1  );
}

uint8 oddparity(uint8 data)            // from BitTwiddling hacks.
{
 uint32 v=data;

 v ^= v >> 4;
 v &= 0xf;

 return (  (0x6996 >> v) & 1  );
}




// This redraws the lisa display ram into the Ximage
// If you are porting to another OS, this is one of
// the functions to replace.  This is slow, so it should be called as rarely as possible.
// i.e. only when the Lisa switches video screens by changing videolatch

extern "C" void XLLisaRedrawPixels(void);          // proto to supress warning below
extern "C" void LisaRedrawPixels(void);


extern "C" void LisaRedrawPixels(void)
{
 my_lisawin->repaintall |= REPAINT_VIDEO_TO_SKIN;
 my_lisawin->Refresh(true,NULL);
 my_lisawin->Update();
}

#ifdef __cplusplus
extern "C"
#endif
void LisaScreenRefresh(void)
{
 LisaRedrawPixels();
}





extern "C" void sound_off(void)
{

    if (cpu68k_clocks-my_lisaframe->lastclk<50000) return;  // prevent sound from shutting down immediately
    wxSound::Stop();
    //FILE *f; f=fopen("z:\\sound.log","a+"); fprintf(f,"stopped sound at %016llx\n",cpu68k_clocks); fclose(f);
}



extern "C" void sound_play(uint16 t2)
{


    int samples=22050*2;                // a second


    int data_size=0;
    int cycles, halfcycles;
    unsigned char vhigh, vlow; //, l;


    if (t2>0xaf) return;                                  // out of range
    if (my_lisaframe->lastt2==t2 && (cpu68k_clocks-my_lisaframe->lastclk < 50000)) return;  // duplicate call if timing <1/100th of a second

    my_lisaframe->lastt2=t2;
    my_lisaframe->lastclk=cpu68k_clocks;


    static const unsigned char header[45]=
    {
        // 0     1    2    3    4    5    6    7    8    9    a    b    c    d    e    f
/*00*/  0x52,0x49,0x46,0x46,   0,   0,0x00,0x00,0x57,0x41,0x56,0x45,0x66,0x6D,0x74,0x20,  // RIFFF-..WAVEfmt
/*10*/  0x10,0x00,0x00,0x00,   1,   0,0x01,0x00,0x22,0x56,0x00,0x00,0x22,0x56,0x00,0x00,  //  ........"V.."V..
/*20*/  0x01,0x00,0x08,0x00,0x64,0x61,0x74,0x61,0x22,0x2D,0x00,0x00//0x80,0x80,0x80,0x80  //  ....data"-......
    };           //        d    a    t    a [LSB____________MSB]

    static unsigned char data[22050*5+45+1024];
    unsigned char *dataptr;
    //data=(unsigned char *)malloc(45+samples+1024);  // allocate a buffer, make it 22Kbps
    memcpy(data,header,45);        // copy the header over it.


    data[0x16]=1;      // 1=1 channel = mono, 2= 2 channels = stereo
    data[0x17]=0;


    data[0x18]=0x22;   // bit rate LSB.   0x5622=22050Hz   22Khz
    data[0x19]=0x56;   //                 0x2D22=11554Hz   11Khz
    data[0x1a]=0x00;   //                 0xAC44=44100Hz   44Khz
    data[0x1b]=0x00;   // bit rate MSB


    data[0x1c]=0x22;   // bytes per second
    data[0x1d]=0x56;   // should match bit rate for 8 bit samples
    data[0x1e]=0x00;
    data[0x1f]=0x00;

    data[0x20]=1;      // Bytes Per Sample:
    data[0x21]=0;      // 1=8 bit Mono, 2=8 bit Stereo or 16 bit Mono, 4=16 bit Stereo

    data[0x22]=8;      // 8, or 16  bits/sample
    data[0x23]=0;

    dataptr=&data[0x2c]; data_size=0;

    volume=(volume & 0x0f);

    vlow =128-(volume<<3);
    vhigh=128+(volume<<3);

    cycles=t2;
    if (!(my_lisaconfig->iorom & 0x20)) cycles=cycles*8/10;       // cycles *=0.8
    cycles=(cycles>>1)+(cycles>>2);                // cycles *=0.75; (1/2 + 1/4th)
    halfcycles=cycles>>1;                          // cycles /=2;

    int i=0;
    if (t2>0x90) samples=cycles;                   // prevent click from turning into beep
    while (samples>=cycles)
    {
        i++;
     memset(dataptr,vhigh,halfcycles); dataptr+=halfcycles;
     memset(dataptr,vlow, halfcycles); dataptr+=halfcycles;
     samples-=cycles; data_size+=cycles;
    }

    data[0x28]=((data_size)    ) & 0xff;
    data[0x29]=((data_size)>>8 ) & 0xff;
    data[0x2a]=((data_size)>>16) & 0xff;
    data[0x2b]=((data_size)>>24) & 0xff;

    data[0x04]=((data_size+36)    ) & 0xff;
    data[0x05]=((data_size+36)>>8 ) & 0xff;
    data[0x06]=((data_size+36)>>16) & 0xff;
    data[0x07]=((data_size+36)>>24) & 0xff;


    dataptr=&data[0];

    if (my_lisa_sound!=NULL) delete my_lisa_sound;
    my_lisa_sound=new wxSound;  //my_lisa_sound->Create(dataptr,data_size);

    // this should work, but it doesn't on all platforms
    //my_lisa_sound->Create(data_size,dataptr);
    //my_lisa_sound->Play(wxSOUND_ASYNC);

    // so instead, use the super lame ass way to play a sound
    FILE *f;
    f=fopen("tmpsnd.wav","wb");
    if (f) {
             fwrite(dataptr,data_size+45,1,f); fclose(f);
             my_lisa_sound->Create(_T("tmpsnd.wav"),false);
             // we do not check for sound_effects_on here as this is the Lisa beeping.
             my_lisa_sound->Play(wxSOUND_ASYNC);
             unlink("tmpsnd.wav");
           }
    errno=0;  // avoid interfearance with other stuff like libdc42.
    return;
}


#endif
