Top secrets sources NedoPC pentevo

Rev

Rev 799 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed | ?url?

#include "std.h"

#include "resource.h"
#include "emul.h"
#include "vars.h"
#include "debug.h"
#include "dbgbpx.h"
#include "dbgcmd.h"
#include "config.h"
#include "util.h"

enum
{
   DB_STOP = 0,
   DB_CHAR,
   DB_SHORT,
   DB_PCHAR,
   DB_PSHORT,
   DB_PINT,
   DB_PFUNC
};

typedef bool (__cdecl *func_t)();

static unsigned calcerr;
unsigned calc(const Z80 *cpu, uintptr_t *script)
{
   unsigned stack[64];
   unsigned *sp = stack-1, x;
   while (*script) {
      switch (*script++) {
         case 'M':             *sp = cpu->DirectRm(*sp);   break;
         case '!':             *sp = !*sp;     break;
         case '~':             *sp = ~*sp;     break;
         case '+':             *(sp-1) += *sp; goto arith;
         case '-':             *(sp-1) -= *sp; goto arith;
         case '*':             *(sp-1) *= *sp; goto arith;
         case '/':             if (*sp) *(sp-1) /= *sp; goto arith;
         case '%':             if (*sp) *(sp-1) %= *sp; goto arith;
         case '&':             *(sp-1) &= *sp; goto arith;
         case '|':             *(sp-1) |= *sp; goto arith;
         case '^':             *(sp-1) ^= *sp; goto arith;
         case WORD2('-','>'):  *(sp-1) = cpu->DirectRm(*sp + sp[-1]); goto arith;
         case WORD2('>','>'):  *(sp-1) >>= *sp;goto arith;
         case WORD2('<','<'):  *(sp-1) <<= *sp;goto arith;
         case WORD2('!','='):  *(sp-1) = (sp[-1]!=*sp);goto arith;
         case '=':
         case WORD2('=','='):  *(sp-1) = (sp[-1]==*sp);goto arith;
         case WORD2('<','='):  *(sp-1) = (sp[-1]<=*sp);goto arith;
         case WORD2('>','='):  *(sp-1) = (sp[-1]>=*sp);goto arith;
         case WORD2('|','|'):  *(sp-1) = (sp[-1]||*sp);goto arith;
         case WORD2('&','&'):  *(sp-1) = (sp[-1]&&*sp);goto arith;
         case '<':             *(sp-1) = (sp[-1]<*sp);goto arith;
         case '>':             *(sp-1) = (sp[-1]>*sp);goto arith;
         arith:                sp--;  break;
         case DB_CHAR:
         case DB_SHORT:        x = unsigned(*script++); goto push;
         case DB_PCHAR:        x = *(unsigned char*)*script++; goto push;
         case DB_PSHORT:       x = 0xFFFF & *(unsigned*)*script++; goto push;
         case DB_PINT:         x = *(unsigned*)*script++; goto push;
         case DB_PFUNC:        x = ((func_t)*script++)(); goto push;
         push:                 *++sp = x; break;
      } // switch (*script)
   } // while
   if (sp != stack) calcerr = 1;
   return *sp;
}

static bool __cdecl get_dos_flag()
{
    return (comp.flags & CF_DOSPORTS) != 0;
}

#define DECL_REGS(var, cpu)                        \
   static struct                                   \
   {                                               \
      unsigned reg;                                \
      const void *ptr;                             \
      unsigned char size;                          \
   } var[] =                                       \
   {                                               \
                                                   \
      { WORD4('D','O','S',0), (const void *)get_dos_flag, 0 },  \
      { WORD4('O','U','T',0), &brk_port_out, 4 },  \
      { WORD2('I','N'), &brk_port_in, 4 },         \
      { WORD4('V','A','L',0), &brk_port_val, 1 },  \
      { WORD2('F','D'), &comp.p7FFD, 1 },          \
                                                   \
      { WORD4('A','F','\'',0), &cpu.alt.af, 2 },   \
      { WORD4('B','C','\'',0), &cpu.alt.bc, 2 },   \
      { WORD4('D','E','\'',0), &cpu.alt.de, 2 },   \
      { WORD4('H','L','\'',0), &cpu.alt.hl, 2 },   \
      { WORD2('A','\''), &cpu.alt.a, 1 },          \
      { WORD2('F','\''), &cpu.alt.f, 1 },          \
      { WORD2('B','\''), &cpu.alt.b, 1 },          \
      { WORD2('C','\''), &cpu.alt.c, 1 },          \
      { WORD2('D','\''), &cpu.alt.d, 1 },          \
      { WORD2('E','\''), &cpu.alt.e, 1 },          \
      { WORD2('H','\''), &cpu.alt.h, 1 },          \
      { WORD2('L','\''), &cpu.alt.l, 1 },          \
                                                   \
      { WORD2('A','F'), &cpu.af, 2 },              \
      { WORD2('B','C'), &cpu.bc, 2 },              \
      { WORD2('D','E'), &cpu.de, 2 },              \
      { WORD2('H','L'), &cpu.hl, 2 },              \
      { 'A', &cpu.a, 1 },                          \
      { 'F', &cpu.f, 1 },                          \
      { 'B', &cpu.b, 1 },                          \
      { 'C', &cpu.c, 1 },                          \
      { 'D', &cpu.d, 1 },                          \
      { 'E', &cpu.e, 1 },                          \
      { 'H', &cpu.h, 1 },                          \
      { 'L', &cpu.l, 1 },                          \
                                                   \
      { WORD2('P','C'), &cpu.pc, 2 },              \
      { WORD2('S','P'), &cpu.sp, 2 },              \
      { WORD2('I','X'), &cpu.ix, 2 },              \
      { WORD2('I','Y'), &cpu.iy, 2 },              \
                                                   \
      { 'I', &cpu.i, 1 },                          \
      { 'R', &cpu.r_low, 1 },                      \
   }



static unsigned char toscript(char *script, uintptr_t *dst)
{
   uintptr_t *d1 = dst;
   static struct {
      unsigned short op;
      unsigned char prior;
   } prio[] = {
      { '(', 10 },
      { ')', 0 },
      { '!', 1 },
      { '~', 1 },
      { 'M', 1 },
      { WORD2('-','>'), 1 },
      { '*', 2 },
      { '%', 2 },
      { '/', 2 },
      { '+', 3 },
      { '-', 3 },
      { WORD2('>','>'), 4 },
      { WORD2('<','<'), 4 },
      { '>', 5 },
      { '<', 5 },
      { '=', 5 },
      { WORD2('>','='), 5 },
      { WORD2('<','='), 5 },
      { WORD2('=','='), 5 },
      { WORD2('!','='), 5 },
      { '&', 6 },
      { '^', 7 },
      { '|', 8 },
      { WORD2('&','&'), 9 },
      { WORD2('|','|'), 10 }
   };

   const Z80 &cpu = CpuMgr.Cpu();

   DECL_REGS(regs, cpu);

   unsigned sp = 0;
   uintptr_t stack[128];
   for (char *p = script; *p; p++)
       if (p[1] != 0x27)
           *p = char(toupper(*p));

   while (*script)
   {
      if (*(unsigned char*)script <= ' ')
      {
          script++;
          continue;
      }

      if (*script == '\'')
      { // char
         *dst++ = DB_CHAR;
         *dst++ = uintptr_t(script[1]);
         if (script[2] != '\'') return 0;
         script += 3; continue;
      }

      if (isalnum(*script) && *script != 'M')
      {
         unsigned r = -1U, p = *(unsigned*)script;
         unsigned ln = 0;
         for (unsigned i = 0; i < _countof(regs); i++)
         {
            unsigned mask = 0xFF; ln = 1;
            if(regs[i].reg & 0xFF00)
            {
                mask = 0xFFFF;
                ln = 2;
            }
            if(regs[i].reg & 0xFF0000)
            {
                mask = 0xFFFFFF;
                ln = 3;
            }
            if (regs[i].reg == (p & mask)) { r = i; break; }
         }
         if (r != -1U)
         {
            script += ln;
            switch (regs[r].size)
            {
               case 0: *dst++ = DB_PFUNC; break;
               case 1: *dst++ = DB_PCHAR; break;
               case 2: *dst++ = DB_PSHORT; break;
               case 4: *dst++ = DB_PINT; break;
               default: errexit("BUG01");
            }
            *dst++ = (uintptr_t)regs[r].ptr;
         }
         else
         { // number
            if (*script > 'F') return 0;
            for (r = 0; isalnum(*script) && *script <= 'F'; script++)
               r = r*0x10 + unsigned((*script >= 'A') ? *script-'A'+10 : *script-'0');
            *dst++ = DB_SHORT;
            *dst++ = r;
         }
         continue;
      }
      // find operation
      unsigned char pr = 0xFF;
      unsigned r = (unsigned)*script++;
      if (strchr("<>=&|-!", (char)r) && strchr("<>=&|", *script))
         r += unsigned(0x100 * (*script++));
      for (unsigned i = 0; i < _countof(prio); i++)
      {
         if (prio[i].op == r)
         {
             pr = prio[i].prior;
             break;
         }
      }
      if (pr == 0xFF)
          return 0;
      if (r != '(')
      {
          while (sp && ((stack[sp] >> 16 <= pr) || (r == ')' && (stack[sp] & 0xFF) != '(')))
          { // get from stack
             *dst++ = stack[sp--] & 0xFFFF;
          }
      }
      if (r == ')')
          sp--; // del opening bracket
      else
          stack[++sp] = r+0x10000*pr; // put to stack
      if ((int)sp < 0)
          return 0; // no opening bracket
   }
   // empty stack
   while (sp)
   {
      if ((stack[sp] & 0xFF) == '(')
          return 0; // no closing bracket
      *dst++ = stack[sp--] & 0xFFFF;
   }
   *dst = DB_STOP;

   calcerr = 0;
   calc(&cpu, d1);
   return u8(1-calcerr);
}

static void script2text(char *dst, const uintptr_t *src)
{
   char stack[64][0x200], tmp[0x200];
   unsigned sp = 0;
   uintptr_t r;

   const Z80 &cpu = CpuMgr.Cpu();

   DECL_REGS(regs, cpu);

   while ((r = *src++))
   {
      if (r == DB_CHAR)
      {
         sprintf(stack[sp++], "'%c'", int(*src++));
         continue;
      }
      if (r == DB_SHORT)
      {
         sprintf(stack[sp], "0%X", unsigned(*src++));
         if (isdigit(stack[sp][1])) strcpy(stack[sp], stack[sp]+1);
         sp++;
         continue;
      }
      if (r >= DB_PCHAR && r <= DB_PFUNC)
      {
         unsigned sz = 0;
         switch(r)
         {
         case DB_PCHAR: sz = 1; break;
         case DB_PSHORT: sz = 2; break;
         case DB_PINT: sz = 4; break;
         case DB_PFUNC: sz = 0; break;
         }
         unsigned i;
         for (i = 0; i < _countof(regs); i++)
         {
            if ((*src == (uintptr_t)regs[i].ptr) && (sz == regs[i].size))
                break;
         }
         *(unsigned*)&(stack[sp++]) = regs[i].reg;
         src++;
         continue;
      }
      if (r == 'M' || r == '~' || r == '!')
      { // unary operators
         sprintf(tmp, "%c(%s)", int(r), stack[sp-1]);
         strcpy(stack[sp-1], tmp);
         continue;
      }
      // else binary operators
      sprintf(tmp, "(%s%s%s)", stack[sp-2], (char*)&r, stack[sp-1]);
      sp--; strcpy(stack[sp-1], tmp);
   }
   if (!sp)
       *dst = 0;
   else
       strcpy(dst, stack[sp-1]);
}

static void SetBpxButtons(HWND dlg)
{
   int focus = -1, text = 0, box = 0;
   HWND focusedWnd = GetFocus();
   if(focusedWnd == GetDlgItem(dlg, IDE_CBP) || focusedWnd == GetDlgItem(dlg, IDC_CBP))
   {
       focus = 0;
       text = IDE_CBP;
       box = IDC_CBP;
   }
   if(focusedWnd == GetDlgItem(dlg, IDE_BPX) || focusedWnd == GetDlgItem(dlg, IDC_BPX))
   {
       focus = 1;
       text = IDE_BPX;
       box = IDC_BPX;
   }
   if(focusedWnd == GetDlgItem(dlg, IDE_MEM) || focusedWnd == GetDlgItem(dlg, IDC_MEM) ||
       focusedWnd == GetDlgItem(dlg, IDC_MEM_R) || focusedWnd == GetDlgItem(dlg, IDC_MEM_W))
   {
       focus = 2;
       text = IDE_MEM;
       box = IDC_MEM;
   }

   SendDlgItemMessage(dlg, IDE_CBP, EM_SETREADONLY, (BOOL)(focus != 0), 0);
   SendDlgItemMessage(dlg, IDE_BPX, EM_SETREADONLY, (BOOL)(focus != 1), 0);
   SendDlgItemMessage(dlg, IDE_MEM, EM_SETREADONLY, (BOOL)(focus != 2), 0);

   int del0 = 0, add0 = 0, del1 = 0, add1 = 0, del2 = 0, add2 = 0;
   unsigned max = unsigned(SendDlgItemMessage(dlg, box, LB_GETCOUNT, 0, 0)),
            cur = unsigned(SendDlgItemMessage(dlg, box, LB_GETCURSEL, 0, 0)),
            len = unsigned(SendDlgItemMessage(dlg, text, WM_GETTEXTLENGTH, 0, 0));

   if (max && cur >= max) SendDlgItemMessage(dlg, box, LB_SETCURSEL, cur = 0, 0);

   if (focus == 0) { if (len && max < MAX_CBP) add0 = 1; if (cur < max) del0 = 1; }
   if (focus == 1) { if (len) add1 = 1; if (cur < max) del1 = 1; }
   if (focus == 2) {
      if (IsDlgButtonChecked(dlg, IDC_MEM_R) == BST_UNCHECKED && IsDlgButtonChecked(dlg, IDC_MEM_W) == BST_UNCHECKED) len = 0;
      if (len) add2 = 1; if (cur < max) del2 = 1;
   }

   EnableWindow(GetDlgItem(dlg, IDB_CBP_ADD), add0);
   EnableWindow(GetDlgItem(dlg, IDB_CBP_DEL), del0);
   EnableWindow(GetDlgItem(dlg, IDB_BPX_ADD), add1);
   EnableWindow(GetDlgItem(dlg, IDB_BPX_DEL), del1);
   EnableWindow(GetDlgItem(dlg, IDB_MEM_ADD), add2);
   EnableWindow(GetDlgItem(dlg, IDB_MEM_DEL), del2);

   unsigned defid = 0;
   if (add0) defid = IDB_CBP_ADD;
   if (add1) defid = IDB_BPX_ADD;
   if (add2) defid = IDB_MEM_ADD;
   if (defid) SendMessage(dlg, DM_SETDEFID, defid, 0);
}

static void ClearListBox(HWND box)
{
   while (SendMessage(box, LB_GETCOUNT, 0, 0))
      SendMessage(box, LB_DELETESTRING, 0, 0);
}

static void FillCondBox(HWND dlg, unsigned cursor)
{
   HWND box = GetDlgItem(dlg, IDC_CBP);
   ClearListBox(box);

   Z80 &cpu = CpuMgr.Cpu();
   for (unsigned i = 0; i < cpu.cbpn; i++)
   {
      char tmp[0x200]; script2text(tmp, cpu.cbp[i]);
      SendMessage(box, LB_ADDSTRING, 0, (LPARAM)tmp);
   }
   SendMessage(box, LB_SETCURSEL, cursor, 0);
}

static void FillBpxBox(HWND dlg, unsigned address)
{
   HWND box = GetDlgItem(dlg, IDC_BPX);
   ClearListBox(box);
   unsigned selection = 0;

   Z80 &cpu = CpuMgr.Cpu();
   unsigned end; //Alone Coder 0.36.7
   for (unsigned start = 0; start < 0x10000; )
   {
      if (!(cpu.membits[start] & MEMBITS_BPX))
      { start++; continue; }
      for (/*unsigned*/ end = start; end < 0xFFFF && (cpu.membits[end+1] & MEMBITS_BPX); end++);
      char tmp[16];
      if (start == end) sprintf(tmp, "%04X", start);
      else sprintf(tmp, "%04X-%04X", start, end);
      SendMessage(box, LB_ADDSTRING, 0, (LPARAM)tmp);
      if (start <= address && address <= end)
         selection = unsigned(SendMessage(box, LB_GETCOUNT, 0, 0));
      start = end+1;
   }
   if (selection) SendMessage(box, LB_SETCURSEL, selection-1, 0);
}

static void FillMemBox(HWND dlg, unsigned address)
{
   HWND box = GetDlgItem(dlg, IDC_MEM);
   ClearListBox(box);
   unsigned selection = 0;

   Z80 &cpu = CpuMgr.Cpu();
   unsigned end; //Alone Coder 0.36.7
   for (unsigned start = 0; start < 0x10000; ) {
      const unsigned char mask = MEMBITS_BPR | MEMBITS_BPW;
      if (!(cpu.membits[start] & mask)) { start++; continue; }
      unsigned active = cpu.membits[start];
      for (/*unsigned*/ end = start; end < 0xFFFF && !((active ^ cpu.membits[end+1]) & mask); end++);
      char tmp[16];
      if (start == end) sprintf(tmp, "%04X ", start);
      else sprintf(tmp, "%04X-%04X ", start, end);
      if (active & MEMBITS_BPR) strcat(tmp, "R");
      if (active & MEMBITS_BPW) strcat(tmp, "W");
      SendMessage(box, LB_ADDSTRING, 0, (LPARAM)tmp);
      if (start <= address && address <= end)
         selection = unsigned(SendMessage(box, LB_GETCOUNT, 0, 0));
      start = end+1;
   }
   if (selection) SendMessage(box, LB_SETCURSEL, selection-1, 0);
}

static char MoveBpxFromBoxToEdit(HWND dlg, unsigned box, unsigned edit)
{
   HWND hBox = GetDlgItem(dlg, int(box));
   unsigned max = unsigned(SendDlgItemMessage(dlg, box, LB_GETCOUNT, 0, 0)),
            cur = unsigned(SendDlgItemMessage(dlg, box, LB_GETCURSEL, 0, 0));
   if (cur >= max) return 0;
   char tmp[0x200];
   SendMessage(hBox, LB_GETTEXT, cur, (LPARAM)tmp);
   if (box == IDC_MEM && *tmp) {
      char *last = tmp + strlen(tmp);
      unsigned r = BST_UNCHECKED, w = BST_UNCHECKED;
      if(last[-1] == 'W')
      {
          w = BST_CHECKED;
          last--;
      }
      if(last[-1] == 'R')
      {
          r = BST_CHECKED;
          last--;
      }
      if (last[-1] == ' ') last--;
      *last = 0;
      CheckDlgButton(dlg, IDC_MEM_R, r);
      CheckDlgButton(dlg, IDC_MEM_W, w);
   }
   SetDlgItemText(dlg, edit, tmp);
   return 1;
}

struct MEM_RANGE { unsigned start, end; };

static int GetMemRamge(char *str, MEM_RANGE &range)
{
   while (*str == ' ') str++;
   for (range.start = 0; ishex(*str); str++)
      range.start = range.start*0x10 + hex(*str);
   if (*str == '-') {
      for (range.end = 0, str++; ishex(*str); str++)
         range.end = range.end*0x10 + hex(*str);
   } else range.end = range.start;
   while (*str == ' ') str++;
   if (*str) return 0;

   if (range.start > 0xFFFF || range.end > 0xFFFF || range.start > range.end) return 0;
   return 1;
}


static INT_PTR CALLBACK conddlg(HWND dlg, UINT msg, WPARAM wp, LPARAM lp)
{
    (void)lp;

   if (msg == WM_INITDIALOG)
   {
      FillCondBox(dlg, 0);
      FillBpxBox(dlg, 0);
      FillMemBox(dlg, 0);
      SetFocus(GetDlgItem(dlg, IDE_CBP));

set_buttons_and_return:
      SetBpxButtons(dlg);
      return 1;
   }
   if (msg == WM_SYSCOMMAND && (wp & 0xFFF0) == SC_CLOSE) EndDialog(dlg, 0);
   if (msg != WM_COMMAND) return 0;

   unsigned id = LOWORD(wp), code = HIWORD(wp); unsigned char mask = 0;
   if (id == IDCANCEL || id == IDOK) EndDialog(dlg, 0);
   char tmp[0x200];

   if (((id == IDE_BPX || id == IDE_CBP || id == IDE_MEM) && (code == EN_SETFOCUS || code == EN_CHANGE)) ||
       ((id == IDC_BPX || id == IDC_CBP || id == IDC_MEM) && code == LBN_SETFOCUS)) goto set_buttons_and_return;

   if (id == IDC_MEM_R || id == IDC_MEM_W) goto set_buttons_and_return;

   if (code == LBN_DBLCLK)
   {
      if (id == IDC_CBP && MoveBpxFromBoxToEdit(dlg, IDC_CBP, IDE_CBP)) goto del_cond;
      if (id == IDC_BPX && MoveBpxFromBoxToEdit(dlg, IDC_BPX, IDE_BPX)) goto del_bpx;
      if (id == IDC_MEM && MoveBpxFromBoxToEdit(dlg, IDC_MEM, IDE_MEM)) goto del_mem;
   }

   if (id == IDB_CBP_ADD)
   {
      SendDlgItemMessage(dlg, IDE_CBP, WM_GETTEXT, sizeof tmp, (LPARAM)tmp);
      SetFocus(GetDlgItem(dlg, IDE_CBP));
      Z80 &cpu = CpuMgr.Cpu();
      if (!toscript(tmp, cpu.cbp[cpu.cbpn])) {
         MessageBox(dlg, "Error in expression\nPlease do RTFM", nullptr, MB_ICONERROR);
         return 1;
      }
      SendDlgItemMessage(dlg, IDE_CBP, WM_SETTEXT, 0, 0);
      FillCondBox(dlg, cpu.cbpn++);
      cpu.dbgchk = isbrk(cpu);
      goto set_buttons_and_return;
   }

   if (id == IDB_BPX_ADD)
   {
      SendDlgItemMessage(dlg, IDE_BPX, WM_GETTEXT, sizeof tmp, (LPARAM)tmp);
      SetFocus(GetDlgItem(dlg, IDE_BPX));
      MEM_RANGE range;
      if (!GetMemRamge(tmp, range))
      {
         MessageBox(dlg, "Invalid breakpoint address / range", nullptr, MB_ICONERROR);
         return 1;
      }

      Z80 &cpu = CpuMgr.Cpu();
      for (unsigned i = range.start; i <= range.end; i++)
         cpu.membits[i] |= MEMBITS_BPX;
      SendDlgItemMessage(dlg, IDE_BPX, WM_SETTEXT, 0, 0);
      FillBpxBox(dlg, range.start);
      cpu.dbgchk = isbrk(cpu);
      goto set_buttons_and_return;
   }

   if (id == IDB_MEM_ADD)
   {
      SendDlgItemMessage(dlg, IDE_MEM, WM_GETTEXT, sizeof tmp, (LPARAM)tmp);
      SetFocus(GetDlgItem(dlg, IDE_MEM));
      MEM_RANGE range;
      if (!GetMemRamge(tmp, range))
      {
         MessageBox(dlg, "Invalid watch address / range", nullptr, MB_ICONERROR);
         return 1;
      }
      unsigned char mask = 0;
      if (IsDlgButtonChecked(dlg, IDC_MEM_R) == BST_CHECKED) mask |= MEMBITS_BPR;
      if (IsDlgButtonChecked(dlg, IDC_MEM_W) == BST_CHECKED) mask |= MEMBITS_BPW;

      Z80 &cpu = CpuMgr.Cpu();
      for (unsigned i = range.start; i <= range.end; i++)
          cpu.membits[i] |= mask;
      SendDlgItemMessage(dlg, IDE_MEM, WM_SETTEXT, 0, 0);
      CheckDlgButton(dlg, IDC_MEM_R, BST_UNCHECKED);
      CheckDlgButton(dlg, IDC_MEM_W, BST_UNCHECKED);
      FillMemBox(dlg, range.start);
      cpu.dbgchk = isbrk(cpu);
      goto set_buttons_and_return;
   }

   if (id == IDB_CBP_DEL)
   {
del_cond:
      SetFocus(GetDlgItem(dlg, IDE_CBP));
      unsigned cur = unsigned(SendDlgItemMessage(dlg, IDC_CBP, LB_GETCURSEL, 0, 0));
      Z80 &cpu = CpuMgr.Cpu();
      if (cur >= cpu.cbpn)
          return 0;
      cpu.cbpn--;
      memcpy(cpu.cbp[cur], cpu.cbp[cur+1], sizeof(cpu.cbp[0])*(cpu.cbpn-cur));

      if (cur && cur == cpu.cbpn)
          cur--;
      FillCondBox(dlg, cur);
      cpu.dbgchk = isbrk(cpu);
      goto set_buttons_and_return;
   }

   if (id == IDB_BPX_DEL)
   {
del_bpx:
      SetFocus(GetDlgItem(dlg, IDE_BPX));
      id = IDC_BPX; mask = u8(~MEMBITS_BPX);
del_range:
      unsigned cur = unsigned(SendDlgItemMessage(dlg, id, LB_GETCURSEL, 0, 0)),
               max = unsigned(SendDlgItemMessage(dlg, id, LB_GETCOUNT, 0, 0));
      if (cur >= max) return 0;
      SendDlgItemMessage(dlg, id, LB_GETTEXT, cur, (LPARAM)tmp);
      unsigned start, end;
      sscanf(tmp, "%X", &start);
      if (tmp[4] == '-') sscanf(tmp+5, "%X", &end); else end = start;

      Z80 &cpu = CpuMgr.Cpu();
      for (unsigned i = start; i <= end; i++)
          cpu.membits[i] &= mask;
      if (id == IDC_BPX) FillBpxBox(dlg, 0); else FillMemBox(dlg, 0);
      if (cur && cur == max)
          cur--;
      SendDlgItemMessage(dlg, id, LB_SETCURSEL, cur, 0);
      cpu.dbgchk = isbrk(cpu);
      goto set_buttons_and_return;
   }

   if (id == IDB_MEM_DEL)
   {
del_mem:
      SetFocus(GetDlgItem(dlg, IDE_MEM));
      id = IDC_MEM; mask = u8(~(MEMBITS_BPR | MEMBITS_BPW));
      goto del_range;
   }

   return 0;
}

void mon_bpdialog()
{
   DialogBox(hIn, MAKEINTRESOURCE(IDD_COND), wnd, conddlg);
}

static INT_PTR CALLBACK watchdlg(HWND dlg, UINT msg, WPARAM wp, LPARAM lp)
{
    (void)lp;

   char tmp[0x200]; unsigned i;
   static const int ids1[] = { IDC_W1_ON, IDC_W2_ON, IDC_W3_ON, IDC_W4_ON };
   static const int ids2[] = { IDE_W1, IDE_W2, IDE_W3, IDE_W4 };
   if (msg == WM_INITDIALOG) {
      for (i = 0; i < 4; i++) {
         CheckDlgButton(dlg, ids1[i], watch_enabled[i] ? BST_CHECKED : BST_UNCHECKED);
         script2text(tmp, watch_script[i]); SetWindowText(GetDlgItem(dlg, ids2[i]), tmp);
      }
      CheckDlgButton(dlg, IDC_TR_RAM, trace_ram ? BST_CHECKED : BST_UNCHECKED);
      CheckDlgButton(dlg, IDC_TR_ROM, trace_ram ? BST_CHECKED : BST_UNCHECKED);
reinit:
      for (i = 0; i < 4; i++)
         EnableWindow(GetDlgItem(dlg, ids2[i]), watch_enabled[i]);
      return 1;
   }
   if (msg == WM_COMMAND && (LOWORD(wp)==ids1[0] || LOWORD(wp)==ids1[1] || LOWORD(wp)==ids1[2] || LOWORD(wp)==ids1[3])) {
      for (i = 0; i < 4; i++)
         watch_enabled[i] = IsDlgButtonChecked(dlg, ids1[i]) == BST_CHECKED;
      goto reinit;
   }
   if ((msg == WM_SYSCOMMAND && (wp & 0xFFF0) == SC_CLOSE) || (msg == WM_COMMAND && LOWORD(wp) == IDCANCEL)) {
      trace_ram = IsDlgButtonChecked(dlg, IDC_TR_RAM) == BST_CHECKED;
      trace_rom = IsDlgButtonChecked(dlg, IDC_TR_ROM) == BST_CHECKED;
      for (i = 0; i < 4; i++)
         if (watch_enabled[i]) {
            SendDlgItemMessage(dlg, ids2[i], WM_GETTEXT, sizeof tmp, (LPARAM)tmp);
            if (!toscript(tmp, watch_script[i])) {
               sprintf(tmp, "Watch %u: error in expression\nPlease do RTFM", i+1);
               MessageBox(dlg, tmp, nullptr, MB_ICONERROR); watch_enabled[i] = 0;
               SetFocus(GetDlgItem(dlg, ids2[i]));
               return 0;
            }
         }
      EndDialog(dlg, 0);
   }
   return 0;
}

void mon_watchdialog()
{
   DialogBox(hIn, MAKEINTRESOURCE(IDD_OSW), wnd, watchdlg);
}

static void LoadBpx()
{
    char Line[100];
    char BpxFileName[FILENAME_MAX];

    addpath(BpxFileName, "bpx.ini");

    FILE *BpxFile = fopen(BpxFileName, "rt");
    if(!BpxFile)
        return;

    while(!feof(BpxFile))
    {
        fgets(Line, sizeof(Line), BpxFile);
        Line[sizeof(Line)-1] = 0;
        char Type = -1;
        int Start = -1, End = -1, CpuIdx = -1;
        int n = sscanf(Line, "%c%1d=%i-%i", &Type, &CpuIdx, &Start, &End);
        if(n < 3 || CpuIdx < 0 || CpuIdx >= (int)CpuMgr.GetCount() || Start < 0)
            continue;

        if(End < 0)
            End = Start;

        unsigned mask = 0;
        switch(Type)
        {
        case 'r': mask |= MEMBITS_BPR; break;
        case 'w': mask |= MEMBITS_BPW; break;
        case 'x': mask |= MEMBITS_BPX; break;
        default: continue;
        }

        Z80 &cpu = CpuMgr.Cpu(u32(CpuIdx));
        for (unsigned i = unsigned(Start); i <= unsigned(End); i++)
            cpu.membits[i] |= mask;
        cpu.dbgchk = isbrk(cpu);
    }
    fclose(BpxFile);
}

static void SaveBpx()
{
    char BpxFileName[FILENAME_MAX];

    addpath(BpxFileName, "bpx.ini");

    FILE *BpxFile = fopen(BpxFileName, "wt");
    if(!BpxFile)
        return;

    for(unsigned CpuIdx = 0; CpuIdx < CpuMgr.GetCount(); CpuIdx++)
    {
        Z80 &cpu = CpuMgr.Cpu(CpuIdx);

        for(int i = 0; i < 3; i++)
        {
            for (unsigned Start = 0; Start < 0x10000; )
            {
               static const unsigned Mask[] = { MEMBITS_BPR, MEMBITS_BPW, MEMBITS_BPX };
               if (!(cpu.membits[Start] & Mask[i]))
               {
                   Start++;
                   continue;
               }
               unsigned active = cpu.membits[Start];
               unsigned End;
               for (End = Start; End < 0xFFFF && !((active ^ cpu.membits[End+1]) & Mask[i]); End++);

               static const char Type[] = { 'r', 'w', 'x' };
               if(active & Mask[i])
               {
                   if (Start == End)
                       fprintf(BpxFile, "%c%1u=0x%04X\n", Type[i], CpuIdx, Start);
                   else
                       fprintf(BpxFile, "%c%1u=0x%04X-0x%04X\n", Type[i], CpuIdx, Start, End);
               }

               Start = End + 1;
            }
        }
    }

    fclose(BpxFile);
}

void init_bpx()
{
    LoadBpx();
}

void done_bpx()
{
    SaveBpx();
}