// *********************************************************************
//    Copyright (c) 1989-2002  Warren Furlow
//    Sound, Timer, Trace Copyright (c) 2001 Zharkoi Oleg
//
// 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.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// *********************************************************************

// *********************************************************************
// HP41.h: header file
// *********************************************************************

#ifndef __HP41_H__
#define __HP41_H__

#include"HP41Hpil.h"

class TcpIp;
class Udp;

// ram registers each have 7 bytes
struct RAM_REG
  {
  byte Reg[7];
  };

enum {eSoundNone=0,eSoundSpeaker,eSoundWave};       // sound modes

#define NORMAL 0                      // for keys - normal position
#define MOVE 1                        // for keys - moved position

// Processor performance characteristics
// ProcCycles / ProcInterval=5.8 for a real machine: 578 cycles / 100 ms per interval
// 5780 inst/sec = 1E6 / 173 ms for a halfnut HP-41CX instruction (older models run at 158 ms)
// time in milliseconds between processor runs:
#define DEFAULT_PROC_INTERVAL 50
#define MAX_PROC_INTERVAL 100
#define MIN_PROC_INTERVAL 10
// number of processor cycles to run each time:
#define DEFAULT_PROC_CYCLES 578

#define DEFAULT_INST_SPEED 173        // milliseconds of actual instruction cycle to simulate for Speaker sound
#define DEFAULT_CONTRAST 5            // contrast value
#define DISPLAY_BOOST 75              // this value is necessary for CAT 2 in SST mode to prevent display glitches
#define MAX_RAM 0x400                 // maximum number of ram registers

// no. of instructions / s for timer increment
#define INSTR_TIMERINC (1000000/DEFAULT_INST_SPEED)               

#define DEFAULT_AUTOIDY 1             // AUTO IDY feature enabled
#define DEFAULT_RFC     1             // RFC frame after CMD

#define DEFAULT_ADDROUT "localhost"   // Virtual HP-IL out server
#define DEFAULT_PORTOUT 60001         // Virtual HP-IL out port
#define DEFAULT_PORTIN  60000         // Virtual HP-IL in Port

#define DEFAULT_ADDUDP  "localhost"   // printer simulation
#define DEFAULT_PORTUDP 5025          // printer simulation

// user code load/save
struct Cat1Label
  {
  char szText[16];
  int StartReg,StartByte;
  int EndReg,EndByte;
  };

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

// Module Memory Structures - see MOD file structures for field descriptions
struct ModuleHeader
  {
  char szFullFileName[_MAX_PATH];
  char szFileFormat[5];
  char szTitle[50];
  char szVersion[10];
  char szPartNumber[20];
  char szAuthor[50];
  char szCopyright[100];
  char szLicense[200];
  char szComments[255];
  byte Category;
  byte Hardware;
  byte MemModules;
  byte XMemModules;
  byte Original;
  byte AppAutoUpdate;
  byte NumPages;
  byte HeaderCustom[32];
  };
struct ModulePage
  {
  ModuleHeader *pModule;          // pointer to module that this page is a part of, or NULL if none
  ModulePage *pAltPage;           // pointer to alternate page if any (HEPAX use)
  char szName[50];
  char szID[9];
  byte ImageNo;                   // image no. in MOD file
  byte Page;                      // file data - unchanged
  byte ActualPage;                // running data- the actual location this page is loaded in
  byte PageGroup;                 // file data - unchanged
  byte Bank;
  byte BankGroup;                 // file data - unchanged
  uint ActualBankGroup;           // running data - BankGroup is unique to file only
  flag fRAM;
  flag fWriteProtect;
  flag fFAT;                      // could be incorrectly set to false if a .ROM file loaded
  flag fHEPAX;                    // if a HEPAX page
  flag fWWRAMBOX;                 // if a W&W RAMBOX page
  word Image[4096];
  byte PageCustom[32];
  };

/****************************/
// The HP-41 class
/****************************/
class HP41
  {
public:
  HP41();
  ~HP41();
  static const word TypeA[];  // Class 0 Type A Parameter table
  static HP41 *pThis;         // to give callback procs a this pointer
  enum {eAwake=0,eLightSleep=1,eDeepSleep=2}; // sleep modes
  void HardwareReset();
  void Wakeup();
  void Sleep(int eNewMode);
  int IsSleeping() const;
  void MemoryLost();
  void EnableRun();
  void DisableRun();
  void Run();
  void Halt();
  void StartHpil();
  void StopHpil();
  void SetProcParams(int ProcIntervalIn,int ProcCyclesIn);
  void GetProcParams(int &ProcIntervalOut,int &ProcCyclesOut) const;
  static void CALLBACK ExecuteProc(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime);
  static void CALLBACK TimerProc(UINT uId,UINT uMsg,DWORD_PTR dwUser,DWORD_PTR dw1,DWORD_PTR dw2);

  // HP41Graphics.cpp:
  static const byte HP41::ASCIItoLCD[];
  void ConvertASCIItoLCD(const char *szASCII,CString& str_dis) const;
  static COLORREF GetLcdColor(word Contrast,COLORREF FontColor);
  void Draw(CDC *pCDC);
  void GetLCD(char *str) const;
  void GetDisplay(char *str) const;
  void GetXAnnunciator(char *str) const;
  void GetAnnunciator(char *str) const;
  void GetShortAnnunciator(char *str) const;
  int GetContrast() const;
  void SetContrast(int ContrastVal);
  void SetFontColor(int ColorVal);
  int GetFontColor() const;
  int GetIndicator() const {return(Indicator);}
  void SetIndicator(int IndicatorIn) {Indicator=IndicatorIn;}
  LOGPALETTE *GetPalKeyboard() const;

  // HP41File.cpp:
  int LoadMOD(ModuleHeader *&pModuleOut,const char *pszFullPath);
  int SaveMOD(ModuleHeader *pModule,const char *pszFullPath) const;
  void UnloadMOD(ModuleHeader *pModule);
  ModulePage* FreePage(ModulePage *pPage,ModuleHeader *pModule);
  void FreePage(uint page,uint bank,ModuleHeader *pModule);
  void ReconfigHepax();
  void ReconfigRamboxBS();
  int LoadConfig(const char *pszLodFile);
  int SaveConfig(const char *pszLodFile,flag bNoMessage=FALSE);
  int GetUserCode(const char *pszUCFile,char *pszError);
  int Catalog1(flag &fFirst,Cat1Label *pLbl);
  int PutUserCode(const char *pszUCFile,char *pszError,const std::vector<Cat1Label *>& vpLbl) const;
  static flag LoadBMP(const char *pszFile,BITMAPINFOHEADER *&pBM,LOGPALETTE *&pPalette,BITMAPINFO *&pPaletteIndex);
  static void FreeBMP(BITMAPINFOHEADER *&pBM,LOGPALETTE *&pPalette,BITMAPINFO *&pPaletteIndex);

  // HP41Keyboard.cpp:
  enum {eKeyboardNone=0,eKeyboardTiny=4,eKeyboardSmall=1,eKeyboardMedium=2,eKeyboardLarge=3,eKeyboardXLarge=5};
  enum {eFontLCD1,eFontLCD2,eFontLCD3,eFontLCD4};
  void SetKeyDown(byte KeyCode);
  void SetKeyUp(byte KeyCode);
  void CreateLcdFont(int eKbd,flag fTrueType, CFont& CFontLCD);
  void SetKeyboard(int eKbd,flag fTrueType,flag fShowTitle,flag fShowMenu);
  int GetKeyboard() const;
  bool IsKeyPressed() const;
  byte MapPointToKey(CPoint pt) const;
  byte MapKeyToKey(flag fAlt,flag fExtended,int PCKey);
  void MoveKey(byte Key,int State);

  // HP41Trace.cpp
  std::vector<std::pair<uint,uint> > vecGlbLbl[199];
  __inline DWORD GetHash(DWORD dwVal);
  void InitGlobalTraceMap();
  void FindGlobalName(uint addr, char *name);  // give address to get label name
  uint FindGlobalAddr(const char *name);
  void ChangeInstSet(uint newset);
  uint GetInstSet() const;
  const char *GetOpcodeName(uint opc) const;
  const char *GetTEFName(uint tef) const;
  void PrintMem(const RAM_REG& RAM) const;
  void PrintRegisters();
  void StartTrace();
  void StopTrace();
  void SwitchTrace();
  flag GetTrace() const;
  void TraceHpilFrame(LPCTSTR lpDirText, word wFrame);
  void __cdecl TraceFormat(LPCTSTR lpFormat, ...);
  void TraceOut();
  void trace();
  void trace_class0();
  void trace_subclass0();
  void trace_subclass1();
  void trace_subclass2();
  void trace_subclass3();
  void trace_subclass4();
  void trace_subclass5();
  void trace_subclass6();
  void trace_subclass7();
  void trace_subclass8();
  void trace_subclass9();
  void trace_subclassA();
  void trace_subclassB();
  void trace_subclassC();
  void trace_subclassD();
  void trace_subclassE();
  void trace_subclassF();
  void trace_class1();
  void trace_class2();
  void trace_class3();

  void SetSoundMode(int val);
  int GetSoundMode() const;
  CFont *GetFontLCD(void) { return(&CFontLCD); }

  // trace
  // trace log file modes
  enum TRACE_MODE { TRACE_FILE_NEW = 0, TRACE_FILE_APPEND };
  CRITICAL_SECTION csTrace;
  FILE *hLogFile;
  uint InstSetIndex;            // instruction set: 0 - HP, 1 - Zencode, 2 - JDA, 3 - Special
  char szTraceFilename[_MAX_PATH];
  TRACE_MODE eTraceMode;        // trace mode new/append
  flag fTrace;                  // trace on/off
  flag fTraceRegister;          // show register content in trace file (bit0 = CPU, bit1 = Timer, bit2 = Infrared)
  flag fTraceOpcode;            // show the numeric opcode
  flag fOctal;                  // display PC address in octal
  flag fTraceHpil;              // show HPIL frames
  char szTraceLabel[14];        // global label
  char szTraceOut[100];
  word TraceTyte1;              // same as Tyte1 but for tracing use only
  word PC_LAST,PC_TRACE;        // PC_REG for last executed instruction and for tracing at any address
  byte pagebank_trace;          // bank of page
  word perph_in_control_last;   // perph_in_control for last executed instruction
  word control_perph_last;      // control_perph for last executed instruction
  word perph_selected_last;     // perph_selected for last executed instruction
  word ram_selected_last;       // ram_selected for last executed instruction
  word perph_in_control_trace;  // state of perph_in_control for tracing use only
  word control_perph_trace;     // number of perph in control for tracing use only
  word perph_selected_trace;    // state of perph_selected for tracing use only
  word ram_selected_trace;      // state of ram_selected for tracing use only

  // graphics
  BITMAPINFOHEADER *pBMKeyboard;
  CBitmap CBMKeyboard;
  LOGPALETTE *pPalKeyboard;
  BITMAPINFO *pPalIndexKeyboard;
  CPalette CPalKeyboard;
  flag fUsePal;
  RECT ActiveRect;       //Clicks outside this area used for dragging if !MainWindow.ShowTitle

  // keyboard and font
  CFont CFontLCD;
  CFont CFontAnnun;
  RECT *pRectKeyboard;
  RECT *pRectLCD;
  RECT *pRectAnnun;
  int eKeyboard;                         // current keyboard
  int eFont;                             // current font
  int UseAltPunc;                        // use alternate punc chars (for ttf wider punc chars only)

  // activity dot
  word Indicator;                        // enables activity indicator
  RECT RectIndicator;
  CBrush brushRed,brushGray;

  // X (close button)
  RECT *pRectCloseButton;                // rect of close button

  // ROM variables
  CPtrList ModuleList;                   // pointers to the loaded modules
  ModulePage *PageMatrix[16][4];         // matrix of page pointers
  ModulePage *pCurPage;                  // current page pointer
  word CurPage;                          // current page number
  byte active_bank[16];                  // array[page] of banks that are enabled
  word NextActualBankGroup;              // counter for loading actual bank groups

  // CPU registers
  CRITICAL_SECTION csFI;                 // multithreaded FI_REG access
  RAM_REG *pRAM;
  byte A_REG[14],B_REG[14],C_REG[14],M_REG[14],N_REG[14];
  word G_REG,F_REG,ST_REG,Q_REG,P_REG,KEY_REG,XST_REG;
  word *PT_REG;                          // set to address of Q_REG or P_REG
  word PT_PREV;                          // set to value of previous PT
  word FI_REG;                           //14 bits used - there is no actual physical register - this is just an input stream from peripherals
  word CARRY,KEYDOWN,BATTERY;
  word PC_REG,RET_STK0,RET_STK1,RET_STK2,RET_STK3;
  word BASE;                             // ==10 for decimal mode, ==16 for hex mode
  word BASECY;                           // nibble base carry

  // CPU variables
  unsigned short eSleepMode;
  word perph_in_control,perph_selected,ram_selected;    // modes dependant on previous instructions that set modes
  word control_perph;              // number of perph in control
  word Tyte1;                      // current instruction (ten bit byte)
  word Tyte2;                      // valid only if tyte1 is two byte instruction
  word TytePrev;                   // previous instruction code
  word Modifier;                   // instruction modifier
  word FirstTEF,LastTEF;           // starting and ending tef pointers (always starting at least significant digit)
  word Boost;                      // boost the main loop by this many more cycles if it is near completion

  // display registers and variables
  byte DIS_C_REG[12];              // bit 8 for chars 1-12 (lower bit)
  byte DIS_B_REG[12];              // bits 7-4 for chars 1-12 (occupy lower nybbles)
  byte DIS_A_REG[12];              // bits 3-0 for chars 1-12 (occupy lower nybbles)
  word DIS_ANNUN_REG;              // 12 bit annunciator register
  word UpdateDisplay;              // set to 1 when display needs to be updated
  word UpdateAnnun;                // set to 1 when annunciators need to be updated
  word DisplayOn;                  // set to 1 when LCD is turned on
  word Contrast;                   // 0-15 value for contrast (half nut)
  COLORREF FontColor;              // RBG font color value

  // timer registers
  word TimerSelA;                  // ==1 if TIMER=A, ==0 if TIMER=B
  byte CLK_A[14], CLK_B[14];       // clock A&B - 56 bits
  byte ALM_A[14], ALM_B[14];       // alarm A&B - 56 bits
  byte SCR_A[14], SCR_B[14];       // scratch A&B - 56 bits
  byte INTV_CNT[14], INTV_TV[14];  // interval timer - only low 20 bits used - INTV_CNT is counter, INTV_TV is terminal value
  byte ACC_F[14];                  // accuracy factor - only low 13 bits used
  byte TMR_S[14];                  // status bits - only low 13 bits used
  word FI_REG_Timer;               // FI flags timer
  flag fTimerEnable;               // enable timer increment
  uint TimerInstrCnt;              // instruction counter for timer increment in single step mode
  MMRESULT TimerEvent;             // ==NULL if no timer, higher precision multimedia timers used for callbacks

  // thermal printer
  byte m_byPrtStat;                // printer status bits: LCA SCO DWM TEO EOL

  // HP-IL
  Hpil *m_pHpil;                   // HP-IL module

  // TCP/IP
  TcpIp *m_pTcpIp;                 // virtual HP-IL loop
  CString strAddrOut;              // virtual HP-IL loop settings
  word wPortOut;
  word wPortIn;

  // UDP
  flag m_fUdpCfgLoaded;           // loaded UDP configuration data from .lod file
  CString m_strAddrUdp;           // UDP address or COM port of printer
  dword m_dwPortUdp;              // UDP port or baud rate of printer

  // instruction delay
  LARGE_INTEGER PCPerf, PCCount[2];
  UINT64 InstrNSec, HpilInstrNSec;

  // keyboard scanner states
  enum {eKbdIdle=0,eKbdPressed=1,eKbdReleased=2};

  // control and state variables
  UINT_PTR ExecuteEvent;          // ==0 if no timer, non zero if timer is set - main loop execution timer callback
  int MinCLRKEY;                  // a counter for keeping a key pressed for a minimum number of CLRKEY instructions
  byte keytable[256];             // array with pressed keys
  unsigned short eKeystate;       // keyboard scanner state
  int ProcInterval;               // time in milliseconds between processor runs
  int ProcCycles;                 // number of processor cycles to run each time
  word MemModules;                // number of regular Memory modules (1-4)
  word XMemModules;               // number of Extended Memory modules (1-3)
  flag fPrinter,fCardReader,fTimer,fWand,fHPIL,fInfrared;    // 1 if the associated hardware is loaded by a module
  bool SpeakerSoundAvail;         // Win9x PC speaker available
  int SoundMode;                  // current sound mode
  flag fRunEnable;                // ==0 if Run() is disabled

  // RAM view
  flag fRamWritten;               // ==1 wrote register to RAM

  // breakpoint control
  flag fBreakPtsEnable;           // ==1 if breakpoints are enabled
  flag fBreak;                    // ==1 if breakpoint raised
  int nBreakPts;                  // count of breakpoints
  struct
    {
    word En;                      // enable
    byte PageBank;                // bank in page [0,1,2,3]
    word Addr;                    // address
    } BreakPts[100];              // ordered list of breakpoints

  inline word GetNextTyte();
  void SetPC(word addr);
  int RamExist(word addr) const;
  int RamExistEnabled(word addr) const;
  int IsRegisterEmpty(word addr) const;
  word GetRamBufferEnd() const;
  byte GetFlag(uint flagNo) const;

  void Execute(void);

  void Class0();
  void Subclass0();
  void Enbank(int BankSet);
  void Subclass1();
  void Subclass2();
  void Subclass3();
  void Subclass4();
  void Subclass5();
  void Subclass6();
  void Subclass7();
  void Subclass8();
  void Subclass9();
  void SubclassA();
  void SubclassB();
  bool HepaxROMBLK(word src,word dest);
  void SubclassC();
  void SubclassD();
  void SubclassE();
  void SubclassF();
  void wdata();
  void rdata();

  void Class1();
  void PushReturn(word addr);
  word PopReturn(void);
  void LongJump(word addr,flag fPush);

  void Class2();
  int ArithCarry(unsigned short tyte);
  void ConvertTEF();
  byte Adder(byte nib1,byte nib2);
  byte Subtractor(byte nib1,byte nib2);

  void Class3();

  void DisplayWrite();
  void DisplayRead();
  void DisplayRotRight(byte REG[],int Times);
  void DisplayRotLeft(byte REG[],int Times);
  void AnnunWrite();
  void HalfnutWrite();
  void HalfnutRead();

  CRITICAL_SECTION csTimer;
  void InitTimer();
  void ResetTimer();
  void DeInitTimer();
  void TimerUpdateFI();
  void IncAllTimers();
  uint GetInstrTimerUpdate() const;
  void ConvertToReg14(byte *DEST_REG,UINT64 Src);
  void ConvertToUINT64(UINT64 *Dest,byte *SRC_REG);
  void TimerWrite();
  void TimerRead();

  void exec_perph_printer();
  void exec_perph_infrared();
  bool InfraredTimerRunning() const;
  bool InfraredRamEnabled() const;
  void InfraredSelectFI(bool bSelect);
  void InfraredUpdateFI();
  void InfraredUpdateTimer();
  void Speaker(short Freq, int Duration);

  dword dwConnectTimeout;
  flag fEnableAutoIDY;
  flag fEnableRFC;

  // user code support
  int DecodeUCByte(int PrevType,byte CurrByte);
  void CalcOffset(int LowReg,int LowByte,int HighReg,int HighByte,int &RegOff,int &ByteOff);
  static void NextByte(int &RegIndex,int &ByteIndex,int Step=1);
  void PrevGlobal(int CurrReg,int CurrByte,int &PrevReg,int &PrevByte,int &RegOff,int &ByteOff) const;
  bool IsPrevGlobalEnd(int CurrReg,int CurrByte,int &StartReg,int &StartByte);

private:
  // HP41File.cpp
  bool isPageUsed(uint page, uint bank) const;

  // HP41Kernel.cpp
  bool isConfigHepax() const;
  void UnconfigHepax();

  // HP41Trace.cpp
  static void ILMnemo(WORD wFrame,CString& strMne);

  // HP41Timer.cpp
  TIMECAPS tc;
  bool bAccurateTimer;
  uint LastTimerUpdate;

  static bool IsRegZero(byte *REG);
  static void IncRegTimer(byte *REG);

  // HP41Infrared.cpp
  byte m_byIrTransmitter;          // transmitter
  int  m_nIrAlm;                   // CPU cycle counter for timer frequency
  int  m_nIrbusyCnt;               // CPU cycle counter for timer send frame
  word m_wIrFI_REG;                // FI flags of IR module
  bool m_bIrSelected;              // IR module selected

  inline int InfraredTimerCnt() const;
  };
#endif // __HP41_H__
