using System;
using System.Windows.Forms;
using System.Drawing;


namespace ZXMAK2.Controls.Debugger
{
	public class DasmPanel : Control
	{
		public DasmPanel()
		{
			TabStop = true;
			//         BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
			this.Font = new Font("Courier", 13, System.Drawing.FontStyle.Regular, GraphicsUnit.Pixel);
			Size = new System.Drawing.Size(424, 148);
			ControlStyles styles = ControlStyles.Selectable |
								   ControlStyles.UserPaint |
								   ControlStyles.ResizeRedraw |
								   ControlStyles.StandardClick |           // csClickEvents
								   ControlStyles.UserMouse |               // csCaptureMouse
								   ControlStyles.ContainerControl |        // csAcceptsControls?
								   ControlStyles.StandardDoubleClick |     // csDoubleClicks
				//                                ControlStyles.Opaque  |                 // csOpaque
								   0;
			base.SetStyle(styles, true);

			mouseTimer = new Timer();
			mouseTimer.Enabled = false;
			mouseTimer.Interval = 50;
			mouseTimer.Tick += OnMouseTimer;

			fLineHeight = 1;
			fVisibleLineCount = 0;
			fTopAddress = 0;
			fActiveLine = 0;
			fBreakColor = Color.Red;
			fBreakForeColor = Color.Black;
			UpdateLines();
		}

		public Color BreakpointColor
		{
			get { return fBreakColor; }
			set { fBreakColor = value; Refresh(); }
		}
		public Color BreakpointForeColor
		{
			get { return fBreakForeColor; }
			set { fBreakForeColor = value; Refresh(); }
		}
		public ushort TopAddress
		{
			get { return fTopAddress; }
			set
			{
				fTopAddress = value;
				fActiveLine = 0;
				UpdateLines();
				Invalidate();
			}
		}
		public ushort ActiveAddress
		{
			get { if ((fActiveLine >= 0) && (fActiveLine < fLineCount)) return fADDRS[fActiveLine]; return 0; }
			set
			{
				for (int i = 0; i <= fVisibleLineCount; i++)
					if (fADDRS[i] == value)
					{
						if (fActiveLine != i)
						{
							if (i == fVisibleLineCount)
							{
								fTopAddress = fADDRS[1];
								fActiveLine = i - 1;
							}
							else
								fActiveLine = i;
						}
						UpdateLines();
						Refresh();
						return;
					}
				TopAddress = value;
			}
		}

		public delegate bool ONCHECKCPU(object Sender, ushort ADDR);
		public event ONCHECKCPU CheckBreakpoint = null;
		public event ONCHECKCPU CheckExecuting = null;
		public delegate void ONGETDATACPU(object Sender, ushort ADDR, int len, out byte[] data);
		public event ONGETDATACPU GetData = null;
		public delegate void ONGETDASMCPU(object Sender, ushort ADDR, out string DASM, out int len);
		public event ONGETDASMCPU GetDasm = null;
		public delegate void ONCLICKCPU(object Sender, ushort Addr);
		public event ONCLICKCPU BreakpointClick = null;
		public event ONCLICKCPU DasmClick = null;



		// private...
		private Timer mouseTimer;

		private ushort fTopAddress;

		private Color fBreakColor;
		private Color fBreakForeColor;
		static private int fGutterWidth = 30;

		private int fLineCount
		{
			get { return fVisibleLineCount + 3; }
		}
		private int fVisibleLineCount;
		private int fLineHeight;
		private int fActiveLine;
		ushort[] fADDRS = null;
		string[] fStrADDRS = null;
		string[] fStrDATAS = null;
		string[] fStrDASMS = null;
		bool[] fBreakpoints = null;
		private Bitmap bitmap = null;

		public void DrawLines(Graphics g, int x, int y, int wid, int hei)
		{
			if ((Height <= 0) || (Width <= 0)) return;
			if (!Visible) return;
			if ((bitmap == null) || (bitmap.Width != wid) || (bitmap.Height != hei))
				bitmap = new Bitmap(wid, hei);

			using (Graphics gp = Graphics.FromImage(bitmap))
			{
				int wa = (int)gp.MeasureString("DDDD", this.Font).Width;               // "DDDD" width (addr)
				int wd = (int)gp.MeasureString("DDDDDDDDDDDDDDDD", this.Font).Width;   // "DDDDDDDDDDDDDDDD" width (data)
				int wtab = 8;
				int wsp = 8;

				int CurrentY = 0;
				Color ink;
				Color paper;

				gp.FillRectangle(new SolidBrush(BackColor), 0, 0, bitmap.Width, bitmap.Height);

				for (int line = 0; line < fVisibleLineCount; line++)
				{
					ink = ForeColor;
					paper = BackColor;

					bool breakLine = fBreakpoints[line];
					bool execLine = false;
					if (CheckExecuting != null)
						execLine = CheckExecuting(this, fADDRS[line]);

					Rectangle liner = new Rectangle(fGutterWidth, CurrentY, bitmap.Width, fLineHeight);

					if (breakLine)
					{
						ink = fBreakForeColor;
						paper = fBreakColor;
						gp.FillRectangle(new SolidBrush(paper), liner);
					}
					if (line == fActiveLine)
					{
						if (Focused)
						{
							ink = Color.White;
							paper = Color.Navy;
						}
						else
						{
							ink = Color.Silver;
							paper = Color.Gray;
						}
						gp.FillRectangle(new SolidBrush(paper), liner);
						/*
						if ((line == fActiveLine) && Focused) // doted border around selected line...
						{
						   Point[] lins = new Point[5] 
						   { 
							  new Point(fGutterWidth, CurrentY), new Point(fGutterWidth, CurrentY+fLineHeight-1),
							  new Point(bitmap.Width-1, CurrentY+fLineHeight-1), new Point(bitmap.Width-1, CurrentY),
							  new Point(fGutterWidth, CurrentY) 
						   };
						   gp.DrawLines(new Pen(Color.Yellow), lins);
						}
						*/
					}
					#region Draw gutter icons...
					if (execLine)      // execarrow icon
					{
						int r = 4;    // base
						int cx = 2 + r;
						int cy = CurrentY + (fLineHeight / 2);
						Point[] arr = new Point[7] { new Point(cx,cy-5), new Point(cx, cy-2),
                                    new Point(cx-3, cy-2),
                                    new Point(cx-3, cy+2), new Point(cx, cy+2),
                                    new Point(cx, cy+5),
                                    new Point(cx+5, cy) };
						gp.FillPolygon(new SolidBrush(Color.Lime), arr);
						gp.DrawPolygon(new Pen(Color.Black), arr);
						Point[] shine = new Point[5] { new Point(cx-2, cy+1), new Point(cx-2, cy-1),
                                      new Point(cx+1, cy-1), new Point(cx+1, cy-3),
                                      new Point(cx+4, cy) };
						gp.DrawLines(new Pen(Color.Yellow), shine);
					}
					if (breakLine)  // breakpoint icon
					{
						int r = 4;    // half radius
						Rectangle bpRect;
						int cx = 2 + r;
						int cy = CurrentY + (fLineHeight / 2);
						if (!execLine)
						{
							bpRect = new Rectangle(cx - r, cy - r, /*cx +*/ r + r + 1, /*cy +*/ r + r + 1);
						}
						else
						{
							cx += 16;
							bpRect = new Rectangle(cx - r, cy - r, /*cx +*/ r + r + 1, /*cy +*/ r + r + 1);
						}
						gp.FillEllipse(new SolidBrush(fBreakColor), bpRect);
						gp.DrawEllipse(new Pen(Color.Black), bpRect);
					}
					#endregion


					gp.DrawString(fStrADDRS[line], this.Font, new SolidBrush(ink), fGutterWidth + wsp, CurrentY);
					gp.DrawString(fStrDATAS[line], this.Font, new SolidBrush(ink), fGutterWidth + wsp + wa + wtab, CurrentY);
					gp.DrawString(fStrDASMS[line], this.Font, new SolidBrush(ink), fGutterWidth + wsp + wa + wtab + wd + wtab, CurrentY);

					CurrentY += fLineHeight;
				}
			}
			g.DrawImageUnscaled(bitmap, x, y);// DrawImage(bitmap, x, y);
		}
		public void UpdateLines()
		{
			fADDRS = new ushort[fLineCount];
			fStrADDRS = new string[fLineCount];
			fStrDATAS = new string[fLineCount];
			fStrDASMS = new string[fLineCount];
			fBreakpoints = new bool[fLineCount];

			ushort CurADDR = fTopAddress;
			for (int i = 0; i < fLineCount; i++)
			{
				fADDRS[i] = CurADDR;

				fStrADDRS[i] = CurADDR.ToString("X4");
				string dasm;
				int len;
				byte[] data;

				if (GetDasm != null)
					GetDasm(this, CurADDR, out dasm, out len);
				else
				{
					dasm = "???";
					len = 1;
				}
				if (GetData != null)
					GetData(this, CurADDR, len, out data);
				else
					data = new byte[] { 0x00 };

				fStrDASMS[i] = dasm;
				string sdata = "";
				int maxdata = data.Length;
				if (maxdata > 7) maxdata = 7;
				for (int j = 0; j < maxdata; j++)
					sdata += data[j].ToString("X2");
				if (maxdata < data.Length)
					sdata += "..";
				fStrDATAS[i] = sdata;
				if (CheckBreakpoint != null)
				{
					if (CheckBreakpoint(this, CurADDR))
						fBreakpoints[i] = true;
				}
				else
					fBreakpoints[i] = false;
				CurADDR += (ushort)len;
			}
		}

		private void ControlUp()
		{
			fActiveLine--;
			if (fActiveLine < 0)
			{
				fActiveLine++;
				fTopAddress = (ushort)(fADDRS[0] - 1);
				UpdateLines();
			}
		}
		private void ControlDown()
		{
			fActiveLine++;
			if (fActiveLine >= fVisibleLineCount)
			{
				fTopAddress = fADDRS[1];
				fActiveLine--;
				UpdateLines();
			}
		}
		private void ControlPageUp()
		{
			for (int i = 0; i < (fVisibleLineCount - 1); i++)
			{
				fTopAddress--;
				UpdateLines();
			}
		}
		private void ControlPageDown()
		{
			if (fVisibleLineCount > 0)
			{
				fTopAddress = fADDRS[fVisibleLineCount - 1];
				UpdateLines();
			}
			fTopAddress = fADDRS[1];
			UpdateLines();
		}

		protected override void OnKeyDown(KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
				case Keys.Down:
					ControlDown();
					Invalidate();
					break;
				case Keys.Up:
					ControlUp();
					Invalidate();
					break;
				case Keys.PageDown:
					ControlPageDown();
					Invalidate();
					break;
				case Keys.PageUp:
					ControlPageUp();
					Invalidate();
					break;
				case Keys.Enter:
					if (DasmClick != null)
						DasmClick(this, fADDRS[fActiveLine]);
					UpdateLines();
					Refresh();
					break;
			}
			base.OnKeyDown(e);
		}
		protected override void OnMouseDown(MouseEventArgs e)
		{
			if ((e.Button & MouseButtons.Left) != 0)
			{
				int nl = (e.Y - 1) / fLineHeight;
				if (nl < fVisibleLineCount)
					if (nl != fActiveLine)
					{
						fActiveLine = nl;
						Invalidate();
					}
				mouseTimer.Enabled = true;
			}
			base.OnMouseDown(e);
		}
		protected override void OnMouseMove(MouseEventArgs e)
		{
			if ((e.Button & MouseButtons.Left) != 0)
			{
				int nl = (e.Y - 1) / fLineHeight;
				if (nl < 0) return;
				if (nl < fVisibleLineCount)
					if (nl != fActiveLine)
					{
						fActiveLine = nl;
						Invalidate();
					}
			}
			base.OnMouseMove(e);
		}
		protected override void OnMouseWheel(MouseEventArgs e)
		{
			int delta = -e.Delta / 120;  //WHEEL_DELTA=120
			if (delta < 0)
				for (int i = 0; i < -delta; i++)
					ControlUp();
			else
				for (int i = 0; i < delta; i++)
					ControlDown();
			Invalidate();

			base.OnMouseWheel(e);
		}
		protected override void OnMouseCaptureChanged(EventArgs e)
		{
			mouseTimer.Enabled = false;
			base.OnMouseCaptureChanged(e);
		}
		private void OnMouseTimer(object sender, EventArgs e)
		{
			Point mE = PointToClient(MousePosition);
			int nl = (mE.Y - 1); ;
			if (nl < 0)
			{
				ControlUp();
				Invalidate();
			}
			if (nl >= (fVisibleLineCount * fLineHeight))
			{
				ControlDown();
				Invalidate();
			}
		}

		protected override void OnPaintBackground(PaintEventArgs e)
		{
			//         base.OnPaintBackground(e);
			/*
					 Pen penBlack = new Pen(Color.Black);
					 Pen penSilver = new Pen(Color.Ivory);
					 e.Graphics.DrawLine(penBlack, 0, 0, Width - 1, 0);
					 e.Graphics.DrawLine(penBlack, 0, 0, 0, Height - 1);

					 e.Graphics.DrawLine(penSilver, 0, Height - 1, Width - 1, Height - 1);
					 e.Graphics.DrawLine(penSilver, Width - 1, Height - 1, Width - 1, 0);
			 */
		}
		protected override void OnPaint(PaintEventArgs e)
		{
			//         base.OnPaint(e);
			//         fAntispacing = this.Font.GetHeight(e.Graphics);
			//         int lh = (int)Math.Ceiling((double)this.Font.GetHeight(e.Graphics)*((double)this.Font.FontFamily.GetEmHeight(this.Font.Style) / (double)this.Font.FontFamily.GetLineSpacing(this.Font.Style)));
			//         fAntispacing = (fAntispacing - (float)lh)/2f;
			int lh = (int)e.Graphics.MeasureString("3D,", this.Font).Height;
			int lc = ((Height - 2) / lh);
			if (lc < 0) lc = 0;
			if ((fVisibleLineCount != lc) || (fLineHeight != lh))
			{
				fLineHeight = lh;
				fVisibleLineCount = lc;
				UpdateLines();
			}
			// chk...
			if ((fActiveLine >= fVisibleLineCount) && (fVisibleLineCount > 0))
			{
				fActiveLine = fVisibleLineCount - 1;
				Invalidate();
			}
			else if ((fActiveLine < 0) && (fVisibleLineCount > 0))
			{
				fActiveLine = 0;
				Invalidate();
			}
			DrawLines(e.Graphics, 0, 0, ClientRectangle.Width, ClientRectangle.Height);// Width - 2, Height - 2);
		}

		protected override void OnGotFocus(EventArgs e)
		{
			base.OnGotFocus(e);
			Invalidate();
		}
		protected override void OnLostFocus(EventArgs e)
		{
			base.OnLostFocus(e);
			Invalidate();
		}
		protected override bool IsInputKey(Keys keyData)
		{
			Keys keys1 = keyData & Keys.KeyCode;
			switch (keys1)
			{
				case Keys.Left:
				case Keys.Up:
				case Keys.Right:
				case Keys.Down:
					return true;
			}
			return base.IsInputKey(keyData);
		}

		protected override void OnMouseDoubleClick(MouseEventArgs e)
		{
			if ((e.Button & MouseButtons.Left) != 0)
			{
				int nl = e.Y / fLineHeight;
				if (nl < fVisibleLineCount)
				{
					if (e.X <= fGutterWidth)
					{
						if (BreakpointClick != null)
							BreakpointClick(this, fADDRS[nl]);
						UpdateLines();
						Refresh();
					}
					else
					{
						if (DasmClick != null)
							DasmClick(this, fADDRS[nl]);
						UpdateLines();
						Refresh();
					}
				}
			}
			base.OnMouseDoubleClick(e);
		}
	}
	public class DataPanel : Control
	{
		public DataPanel()
		{
			TabStop = true;
			this.Font = new Font("Courier", 13, System.Drawing.FontStyle.Regular, GraphicsUnit.Pixel);
			Size = new System.Drawing.Size(424, 99);
			ControlStyles styles = ControlStyles.Selectable |
								   ControlStyles.UserPaint |
								   ControlStyles.ResizeRedraw |
								   ControlStyles.StandardClick |           // csClickEvents
								   ControlStyles.UserMouse |               // csCaptureMouse
								   ControlStyles.ContainerControl |        // csAcceptsControls?
								   ControlStyles.StandardDoubleClick |     // csDoubleClicks
								   0;
			base.SetStyle(styles, true);

			mouseTimer = new Timer();
			mouseTimer.Enabled = false;
			mouseTimer.Interval = 50;
			mouseTimer.Tick += OnMouseTimer;

			fLineHeight = 1;
			fVisibleLineCount = 0;
			fTopAddress = 0;
			fActiveLine = 0;
			fActiveColumn = 0;
			fColCount = 8;
			UpdateLines();
		}

		public ushort TopAddress
		{
			get { return fTopAddress; }
			set
			{
				fTopAddress = value;
				fActiveLine = 0;
				UpdateLines();
				Invalidate();
			}
		}
		public int ColCount
		{
			get { return fColCount; }
			set
			{
				fColCount = value;
				UpdateLines();
				Invalidate();
			}
		}

		public delegate void ONGETDATACPU(object Sender, ushort ADDR, int len, out byte[] data);
		public event ONGETDATACPU GetData = null;
		public delegate void ONCLICKCPU(object Sender, ushort Addr);
		public event ONCLICKCPU DataClick = null;


		// private...
		private Timer mouseTimer;
		private ushort fTopAddress;

		static private int fGutterWidth = 30;
		private int fColCount;
		private int fLineCount
		{
			get { return fVisibleLineCount + 3; }
		}
		private int fVisibleLineCount;
		private int fLineHeight;
		private int fActiveLine;
		private int fActiveColumn;
		ushort[] fADDRS = null;
		byte[][] fBytesDATAS = null;
		private Bitmap bitmap = null;
		private int wa = 0;
		private int wd = 0;
		private int wtab = 0;
		private int wsp = 0;
		private int wsymb = 0;


		public void DrawLines(Graphics g, int x, int y, int wid, int hei)
		{
			if ((Height <= 0) || (Width <= 0)) return;
			if (!Visible) return;
			if ((bitmap == null) || (bitmap.Width != wid) || (bitmap.Height != hei))
				bitmap = new Bitmap(wid, hei);

			using (Graphics gp = Graphics.FromImage(bitmap))
			{
				int wdsp = 2;
				wa = (int)gp.MeasureString("DDDD", this.Font).Width;             // "DDDD" width (addr)
				wd = (int)gp.MeasureString("DD", this.Font).Width + wdsp * 2;        // "DD" width (data)
				wsymb = (int)gp.MeasureString("D", this.Font).Width;
				wtab = 8;
				wsp = 8;

				int CurrentY = 0;
				Color ink;
				Color paper;

				gp.FillRectangle(new SolidBrush(BackColor), 0, 0, bitmap.Width, bitmap.Height);


				for (int line = 0; line < fVisibleLineCount; line++)
				{
					ink = ForeColor;
					paper = BackColor;

					gp.DrawString(fADDRS[line].ToString("X4"), this.Font, new SolidBrush(ink), fGutterWidth + wsp, CurrentY);

					for (int col = 0; col < fColCount; col++)
					{
						ink = ForeColor;
						paper = BackColor;
						if ((line == fActiveLine) && (col == fActiveColumn))
						{
							if (Focused)
							{
								ink = Color.White;
								paper = Color.Navy;
							}
							else
							{
								ink = Color.Silver;
								paper = Color.Gray;
							}
							gp.FillRectangle(new SolidBrush(paper), new Rectangle(fGutterWidth + wsp + wa + wtab + (col * wd), CurrentY, wd, fLineHeight));
							gp.FillRectangle(new SolidBrush(Color.Gray), new Rectangle(fGutterWidth + wsp + wa + wtab + (fColCount * wd) + wtab + (col * wsymb), CurrentY, wsymb, fLineHeight));
							/*
							if (Focused) // doted border around selected line...
							{
							   Point[] lins = new Point[5] 
							   { 
								  new Point(fGutterWidth + wsp + wa + wtab + (col * wd), CurrentY), 
								  new Point(fGutterWidth + wsp + wa + wtab + (col * wd), CurrentY+fLineHeight-1),
								  new Point(fGutterWidth + wsp + wa + wtab + (col * wd)+wd-1, CurrentY+fLineHeight-1), 
								  new Point(fGutterWidth + wsp + wa + wtab + (col * wd)+wd-1, CurrentY),
								  new Point(fGutterWidth + wsp + wa + wtab + (col * wd), CurrentY) 
							   };
							   gp.DrawLines(new Pen(Color.Yellow), lins);
							}
							*/
						}
						gp.DrawString(fBytesDATAS[line][col].ToString("X2"), this.Font, new SolidBrush(ink), fGutterWidth + wsp + wa + wtab + (col * wd) + wdsp, CurrentY);
						string sch = new String(zxencode[fBytesDATAS[line][col]], 1);
						gp.DrawString(sch, this.Font, new SolidBrush(ink), fGutterWidth + wsp + wa + wtab + (fColCount * wd) + wtab + (col * wsymb), CurrentY);
					}
					CurrentY += fLineHeight;
				}
			}
			g.DrawImageUnscaled(bitmap, x, y);// DrawImage(bitmap, x, y);
		}
		public void UpdateLines()
		{
			fADDRS = new ushort[fLineCount];
			fBytesDATAS = new byte[fLineCount][];

			ushort CurADDR = fTopAddress;
			for (int i = 0; i < fLineCount; i++)
			{
				fADDRS[i] = CurADDR;

				if (GetData != null)
					GetData(this, CurADDR, fColCount, out fBytesDATAS[i]);
				else
				{
					fBytesDATAS[i] = new byte[fColCount];
					for (int j = 0; j < fColCount; j++)
						fBytesDATAS[i][j] = (byte)((fTopAddress + i * fColCount + j) & 0xFF);
				}
				CurADDR += (ushort)fColCount;
			}
		}

		private void ControlUp()
		{
			fActiveLine--;
			if (fActiveLine < 0)
			{
				fActiveLine++;
				fTopAddress -= (ushort)fColCount;
				UpdateLines();
			}
		}
		private void ControlDown()
		{
			fActiveLine++;
			if (fActiveLine >= fVisibleLineCount)
			{
				fTopAddress += (ushort)fColCount;
				fActiveLine--;
				UpdateLines();
			}
		}
		private void ControlLeft()
		{
			fActiveColumn--;
			if (fActiveColumn < 0)
			{
				fActiveColumn = fColCount - 1;
				ControlUp();
			}
			else
				UpdateLines();
		}
		private void ControlRight()
		{
			fActiveColumn++;
			if (fActiveColumn >= fColCount)
			{
				fActiveColumn = 0;
				ControlDown();
			}
			else
				UpdateLines();
		}
		private void ControlPageUp()
		{
			fTopAddress -= (ushort)(fColCount * fVisibleLineCount);
			UpdateLines();
		}
		private void ControlPageDown()
		{
			fTopAddress += (ushort)(fColCount * fVisibleLineCount);
			UpdateLines();
		}


		protected override void OnMouseDown(MouseEventArgs e)
		{
			if ((e.Button & MouseButtons.Left) != 0)
			{
				int nl = (e.Y - 1) / fLineHeight;
				if ((nl < fVisibleLineCount) && (nl >= 0))
				{
					if (nl != fActiveLine)
					{
						fActiveLine = nl;
						Invalidate();
					}
				}
				int nc;
				if (wd >= 0)
				{
					nc = ((e.X - 1) - (fGutterWidth + wsp + wa + wtab));
					if (nc >= 0) nc /= wd;
					else nc = -1;
				}
				else
					nc = 0;
				if ((nc < fColCount) && (nc >= 0))
				{
					if (nc != fActiveColumn)
					{
						fActiveColumn = nc;
						Invalidate();
					}
				}
				mouseTimer.Enabled = true;
			}
			base.OnMouseDown(e);
		}
		protected override void OnMouseMove(MouseEventArgs e)
		{
			if ((e.Button & MouseButtons.Left) != 0)
			{
				int nl = (e.Y - 1) / fLineHeight;
				if ((nl < fVisibleLineCount) && (nl >= 0))
				{
					if (nl != fActiveLine)
					{
						fActiveLine = nl;
						Invalidate();
					}
				}
				int nc;
				if (wd >= 0)
				{
					nc = ((e.X - 1) - (fGutterWidth + wsp + wa + wtab));
					if (nc >= 0) nc /= wd;
					else nc = -1;
				}
				else
					nc = 0;
				if ((nc < fColCount) && (nc >= 0))
				{
					if (nc != fActiveColumn)
					{
						fActiveColumn = nc;
						Invalidate();
					}
				}
			}
			base.OnMouseMove(e);
		}
		protected override void OnMouseWheel(MouseEventArgs e)
		{
			int delta = -e.Delta / 120;  //WHEEL_DELTA=120
			if (delta < 0)
				for (int i = 0; i < -delta; i++)
					ControlUp();
			else
				for (int i = 0; i < delta; i++)
					ControlDown();
			Invalidate();

			base.OnMouseWheel(e);
		}
		protected override void OnMouseCaptureChanged(EventArgs e)
		{
			mouseTimer.Enabled = false;
			base.OnMouseCaptureChanged(e);
		}
		private void OnMouseTimer(object sender, EventArgs e)
		{
			Point mE = PointToClient(MousePosition);
			int nl = (mE.Y - 1);
			if (nl < 0)
			{
				ControlUp();
				Invalidate();
			}
			if (nl >= (fVisibleLineCount * fLineHeight))
			{
				ControlDown();
				Invalidate();
			}

			int nc;
			if (wd >= 0)
			{
				nc = ((mE.X - 1) - (fGutterWidth + wsp + wa + wtab));
				if (nc >= 0) nc /= wd;
				else nc = -1;
			}
			else
				nc = 0;
			if ((nc < fColCount) && (nc >= 0))
			{
				if (nc != fActiveColumn)
				{
					fActiveColumn = nc;
					Invalidate();
				}
			}
		}
		protected override void OnMouseDoubleClick(MouseEventArgs e)
		{
			if ((e.Button & MouseButtons.Left) != 0)
			{
				int nl = e.Y / fLineHeight;
				int nc;
				if (wd >= 0)
					nc = ((e.X - 1) - (fGutterWidth + wsp + wa + wtab)) / wd;
				else
					nc = 0;
				if ((nl < fVisibleLineCount) && (nl >= 0))
				{
					if ((nc < fColCount) && (nc >= 0))
					{
						if (DataClick != null)
							DataClick(this, (ushort)(fADDRS[nl] + nc));
						UpdateLines();
						Refresh();
					}
				}
			}
			base.OnMouseDoubleClick(e);
		}


		protected override void OnGotFocus(EventArgs e)
		{
			base.OnGotFocus(e);
			Invalidate();
		}
		protected override void OnLostFocus(EventArgs e)
		{
			base.OnLostFocus(e);
			Invalidate();
		}
		protected override bool IsInputKey(Keys keyData)
		{
			Keys keys1 = keyData & Keys.KeyCode;
			switch (keys1)
			{
				case Keys.Left:
				case Keys.Up:
				case Keys.Right:
				case Keys.Down:
					return true;
			}
			return base.IsInputKey(keyData);
		}
		protected override void OnKeyDown(KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
				case Keys.Down:
					ControlDown();
					Invalidate();
					break;
				case Keys.Up:
					ControlUp();
					Invalidate();
					break;
				case Keys.Left:
					ControlLeft();
					Invalidate();
					break;
				case Keys.Right:
					ControlRight();
					Invalidate();
					break;
				case Keys.PageDown:
					ControlPageDown();
					Invalidate();
					break;
				case Keys.PageUp:
					ControlPageUp();
					Invalidate();
					break;
				case Keys.Enter:
					if (fVisibleLineCount > 0)
					{
						if (DataClick != null)
							DataClick(this, (ushort)(fADDRS[fActiveLine] + fActiveColumn));
					}
					UpdateLines();
					Refresh();
					break;
			}
			base.OnKeyDown(e);
		}
		protected override void OnPaintBackground(PaintEventArgs e)
		{
		}
		protected override void OnPaint(PaintEventArgs e)
		{
			int lh = (int)e.Graphics.MeasureString("3D,", this.Font).Height;
			int lc = ((Height - 2) / lh);
			if (lc < 0) lc = 0;
			if ((fVisibleLineCount != lc) || (fLineHeight != lh))
			{
				fLineHeight = lh;
				fVisibleLineCount = lc;
				UpdateLines();
			}
			// chk...
			if ((fActiveLine >= fVisibleLineCount) && (fVisibleLineCount > 0))
			{
				fActiveLine = fVisibleLineCount - 1;
				//            Invalidate();
			}
			else if ((fActiveLine < 0) && (fVisibleLineCount > 0))
			{
				fActiveLine = 0;
				//            Invalidate();
			}

			DrawLines(e.Graphics, 0, 0, ClientRectangle.Width, ClientRectangle.Height);// Width - 2, Height - 2);
		}

		static char[] zxencode = new char[256]
      {
         ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',  // 00..0F
         ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',  // 10..1F
         ' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/', // 20..2F
         '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',  // 30..3F
         '@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',  // 40..4F
         'P','Q','R','S','T','U','V','W','X','Y','Z','[','\\',']','↑','_', // 50..5F
         '₤','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',  // 60..6F
         'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~','©',  // 70..7F

         ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',  // 80..8F
         ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',  // 90..9F
         ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',  // A0..AF
         ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',  // B0..BF
         ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',  // C0..CF
         ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',  // D0..DF
         ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',  // E0..EF
         ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',  // F0..FF
      };
	}
}