diff --git a/Form1.Designer.cs b/Form1.Designer.cs index 45e2f57..e66c534 100644 --- a/Form1.Designer.cs +++ b/Form1.Designer.cs @@ -37,6 +37,13 @@ private void InitializeComponent() timer1 = new System.Windows.Forms.Timer(components); label1 = new Label(); panel1 = new Panel(); + groupBox3 = new GroupBox(); + label5 = new Label(); + label4 = new Label(); + radioButton7 = new RadioButton(); + radioButton6 = new RadioButton(); + textBox6 = new TextBox(); + textBox5 = new TextBox(); groupBox1 = new GroupBox(); radioButton5 = new RadioButton(); radioButton4 = new RadioButton(); @@ -50,6 +57,7 @@ private void InitializeComponent() label2 = new Label(); ((System.ComponentModel.ISupportInitialize)pictureBox1).BeginInit(); panel1.SuspendLayout(); + groupBox3.SuspendLayout(); groupBox1.SuspendLayout(); groupBox2.SuspendLayout(); SuspendLayout(); @@ -61,7 +69,7 @@ private void InitializeComponent() pictureBox1.BorderStyle = BorderStyle.Fixed3D; pictureBox1.Location = new Point(0, 0); pictureBox1.Name = "pictureBox1"; - pictureBox1.Size = new Size(2027, 1261); + pictureBox1.Size = new Size(1652, 1233); pictureBox1.SizeMode = PictureBoxSizeMode.Zoom; pictureBox1.TabIndex = 0; pictureBox1.TabStop = false; @@ -118,6 +126,7 @@ private void InitializeComponent() // // panel1 // + panel1.Controls.Add(groupBox3); panel1.Controls.Add(groupBox1); panel1.Controls.Add(checkBox3); panel1.Controls.Add(checkBox2); @@ -125,11 +134,89 @@ private void InitializeComponent() panel1.Controls.Add(radioButton1); panel1.Controls.Add(groupBox2); panel1.Dock = DockStyle.Right; - panel1.Location = new Point(2019, 0); + panel1.Location = new Point(1644, 0); panel1.Name = "panel1"; - panel1.Size = new Size(218, 1261); + panel1.Size = new Size(218, 1233); panel1.TabIndex = 7; // + // groupBox3 + // + groupBox3.BackColor = Color.SkyBlue; + groupBox3.Controls.Add(label5); + groupBox3.Controls.Add(label4); + groupBox3.Controls.Add(radioButton7); + groupBox3.Controls.Add(radioButton6); + groupBox3.Controls.Add(textBox6); + groupBox3.Controls.Add(textBox5); + groupBox3.Location = new Point(0, 457); + groupBox3.Name = "groupBox3"; + groupBox3.Size = new Size(218, 229); + groupBox3.TabIndex = 18; + groupBox3.TabStop = false; + groupBox3.Text = "Size"; + // + // label5 + // + label5.AutoSize = true; + label5.Location = new Point(24, 139); + label5.Name = "label5"; + label5.Size = new Size(22, 25); + label5.TabIndex = 5; + label5.Text = "Y"; + // + // label4 + // + label4.AutoSize = true; + label4.Location = new Point(23, 102); + label4.Name = "label4"; + label4.Size = new Size(23, 25); + label4.TabIndex = 4; + label4.Text = "X"; + // + // radioButton7 + // + radioButton7.AutoSize = true; + radioButton7.Checked = true; + radioButton7.Location = new Point(22, 64); + radioButton7.Name = "radioButton7"; + radioButton7.Size = new Size(118, 29); + radioButton7.TabIndex = 3; + radioButton7.TabStop = true; + radioButton7.Text = "Resized to"; + radioButton7.UseVisualStyleBackColor = true; + radioButton7.CheckedChanged += radioButton7_CheckedChanged; + // + // radioButton6 + // + radioButton6.AutoSize = true; + radioButton6.Location = new Point(22, 30); + radioButton6.Name = "radioButton6"; + radioButton6.Size = new Size(99, 29); + radioButton6.TabIndex = 2; + radioButton6.Text = "Original"; + radioButton6.UseVisualStyleBackColor = true; + radioButton6.CheckedChanged += radioButton6_CheckedChanged; + // + // textBox6 + // + textBox6.Location = new Point(85, 136); + textBox6.Name = "textBox6"; + textBox6.Size = new Size(71, 31); + textBox6.TabIndex = 1; + textBox6.Text = "768"; + textBox6.TextAlign = HorizontalAlignment.Right; + textBox6.TextChanged += textBox6_TextChanged; + // + // textBox5 + // + textBox5.Location = new Point(85, 99); + textBox5.Name = "textBox5"; + textBox5.Size = new Size(71, 31); + textBox5.TabIndex = 0; + textBox5.Text = "1024"; + textBox5.TextAlign = HorizontalAlignment.Right; + textBox5.TextChanged += textBox5_TextChanged; + // // groupBox1 // groupBox1.BackColor = Color.Honeydew; @@ -149,9 +236,9 @@ private void InitializeComponent() radioButton5.AutoSize = true; radioButton5.Location = new Point(22, 146); radioButton5.Name = "radioButton5"; - radioButton5.Size = new Size(70, 29); + radioButton5.Size = new Size(116, 29); radioButton5.TabIndex = 3; - radioButton5.Text = "EGA"; + radioButton5.Text = "EGA (true)"; radioButton5.UseVisualStyleBackColor = true; radioButton5.CheckedChanged += radioButton5_CheckedChanged; // @@ -160,9 +247,9 @@ private void InitializeComponent() radioButton4.AutoSize = true; radioButton4.Location = new Point(22, 111); radioButton4.Name = "radioButton4"; - radioButton4.Size = new Size(72, 29); + radioButton4.Size = new Size(176, 29); radioButton4.TabIndex = 2; - radioButton4.Text = "CGA"; + radioButton4.Text = "CGA (compatible)"; radioButton4.UseVisualStyleBackColor = true; radioButton4.CheckedChanged += radioButton4_CheckedChanged; // @@ -194,8 +281,6 @@ private void InitializeComponent() // checkBox3 // checkBox3.AutoSize = true; - checkBox3.Checked = true; - checkBox3.CheckState = CheckState.Checked; checkBox3.Location = new Point(22, 194); checkBox3.Name = "checkBox3"; checkBox3.Size = new Size(166, 29); @@ -236,7 +321,7 @@ private void InitializeComponent() groupBox2.Controls.Add(textBox3); groupBox2.Controls.Add(label1); groupBox2.Dock = DockStyle.Bottom; - groupBox2.Location = new Point(0, 952); + groupBox2.Location = new Point(0, 924); groupBox2.Name = "groupBox2"; groupBox2.Size = new Size(218, 309); groupBox2.TabIndex = 16; @@ -265,15 +350,18 @@ private void InitializeComponent() // AutoScaleDimensions = new SizeF(10F, 25F); AutoScaleMode = AutoScaleMode.Font; - ClientSize = new Size(2237, 1261); + ClientSize = new Size(1862, 1233); Controls.Add(panel1); Controls.Add(pictureBox1); Name = "Form1"; - Text = "Form1"; + Text = "RetroScreen"; + FormClosing += Form1_FormClosing; Shown += Form1_Shown; ((System.ComponentModel.ISupportInitialize)pictureBox1).EndInit(); panel1.ResumeLayout(false); panel1.PerformLayout(); + groupBox3.ResumeLayout(false); + groupBox3.PerformLayout(); groupBox1.ResumeLayout(false); groupBox1.PerformLayout(); groupBox2.ResumeLayout(false); @@ -303,5 +391,12 @@ private void InitializeComponent() private Label label3; private Label label2; private GroupBox groupBox2; + private GroupBox groupBox3; + private RadioButton radioButton7; + private RadioButton radioButton6; + private TextBox textBox6; + private TextBox textBox5; + private Label label5; + private Label label4; } } diff --git a/Form1.cs b/Form1.cs index ca788b3..bedbf9f 100644 --- a/Form1.cs +++ b/Form1.cs @@ -1,3 +1,5 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Eventing.Reader; using System.Drawing; @@ -20,13 +22,14 @@ namespace CGATest public partial class Form1 : Form { static int width = 1500, height = 500; - Bitmap newframe = new Bitmap(1500, 500); + volatile Bitmap newframe = new Bitmap(1500, 500); - StreamReader vIn; - //StreamReader vIn = new StreamReader(Console.OpenStandardInput(), System.Text.Encoding.Latin1, bufferSize: 128000); + public volatile StreamReader vIn; + public volatile ConcurrentQueue bufferedData = new ConcurrentQueue(); + static int maxBuffer = 300000; bool endofstream = false; - int TB1 = 0, TB2 = 0, screens = 0; + public volatile int TB1 = 0, TB2 = 0, TB3 = 0, screens = 0; int xposmax = 0, yposmax = 0, xposmax_b = 1, yposmax_b = 1, yposA = 0; @@ -40,9 +43,16 @@ public partial class Form1 : Form // Color Mode 0-RGB, 1-MDA, 2-CGA, 3-EGA int colorMode = 0; + // size + bool originalSize = false; + int xResize = 1024, yResize = 768; + //Composite Stuff int syncpulses = 0, sync_long = 0, sync_short = 0; - bool composite_sync = true; + bool composite_sync = false; + + // process + volatile ConcurrentQueue processIds = new ConcurrentQueue(); public Form1() { @@ -56,7 +66,6 @@ public Form1() ftTimer.Start(); pictureBox1.SizeMode = PictureBoxSizeMode.CenterImage; - } @@ -66,46 +75,115 @@ private void TShowFrames(object myObject, EventArgs myEventArgs) textBox1.Text = TB1.ToString(); textBox2.Text = TB2.ToString(); + //textBox4.Text = TB3.ToString(); screens = 0; } - private void TShowPic() + private async void TShowPic() { - try + Bitmap? resized = null; + + var t = Task.Run(() => { while (true) { - this.Invoke(() => - { + if (newframe != null) + if (originalSize) + { + pictureBox1.Image = (Bitmap)newframe.Clone(); + } + else { + resized = new Bitmap(newframe, new Size(xResize, yResize)); + pictureBox1.Image = (Bitmap)resized.Clone(); + } + // Wait for a 50Hz refresh + Thread.Sleep(20); + } + } + ); + } - Bitmap resized = new Bitmap(newframe, new Size(1024, 768)); - //RectangleF cloneRect = new RectangleF(0, 0, 1024, 768); - pictureBox1.Image = (Bitmap)resized.Clone(); + // Read input stream to buffer + private async void TBufferInput() + { - //pictureBox1.Image = (Bitmap)newframe.Clone(); + Process sigrok = null; + int buffSize = 32; + int maxBuff = 2000000; + bool loop = true; + int readchar = 0; + char[] c = new char[buffSize]; + + + + string[] args = Environment.GetCommandLineArgs(); + if (args.Length == 1) // do we start sigrok on our own? + { + Console.WriteLine("Starting Sigrok"); + + // Sigrok process start + var startSigrok = new ProcessStartInfo + { + FileName = @"C:\Program Files\sigrok\sigrok-cli\sigrok-cli", + UseShellExecute = false, + RedirectStandardOutput = true, + WorkingDirectory = @"C:\Program Files\sigrok\sigrok-cli\", + Arguments = @"-d fx2lafw -O binary --config ""samplerate=24 MHz"" --continuous", + StandardOutputEncoding = Encoding.Latin1 // required to get only one byte per char + }; + + sigrok = Process.Start(startSigrok); + + sigrok.PriorityClass = ProcessPriorityClass.High; + + // Queue Id for later housekeeping + processIds.Enqueue(sigrok.Id); + + // Attach sigrok to stdout + vIn = sigrok.StandardOutput; + } + else + // or is there a pipe? + if (args[1] == "-") + { + vIn = new StreamReader(Console.OpenStandardInput(), System.Text.Encoding.Latin1, bufferSize: 131072); + } + else + { + Console.WriteLine("Only \"-\" is allowed for standard-in. Exiting."); + Application.Exit(); + } + + while (loop && vIn != null) + { + readchar = await vIn.ReadAsync(c, 0, buffSize); + TB3 = bufferedData.Count; + // if (TB3 { //Bitmap bmp = new Bitmap(width, (yposmax > 200 ? yposmax : 200)); Bitmap bmp = new Bitmap((xposmax_b > 10 ? xposmax : 10), (yposmax_b > 10 ? yposmax_b : 10)); - int readchar; + int readchar, readFail; int rawdata = 0; bool hsync_raw = false, vsync_raw = false; bool hsync = false, vsync = false; @@ -206,10 +290,14 @@ await Task.Run(async () => int xpos = 0, ypos = 0; int color; int hsync_filter = 0, vsync_filter = 0; + + // Colors incl. intensity bits + byte redL = 0, greenL = 0, blueL = 0, redH = 0, greenH = 0, blueH = 0; byte red = 0, green = 0, blue = 0; + bool loop = true; bool vcompositeflag = false, armed = false; - int x = 0; + //experiment BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); @@ -218,78 +306,133 @@ await Task.Run(async () => await Task.Run(() => { - while (loop) + while (loop) + { + + // A timeout in case no data is added to the queue. Multibyte read-async can get stuck then + readFail = 0; + while (!bufferedData.TryDequeue(out readchar)) { - readchar = vIn.Read(); - if (readchar == -1) + readFail++; + if (readFail > 100) { - loop = false; - endofstream = true; - } - else - { - rawdata = readchar; + readchar = -1; + break; } + // Give it some time + Thread.Sleep(50); + } - // Safe former sync state - vsyncc = vsync; hsyncc = hsync; + // Catch standard case of file end + if (readchar == -1) + { + loop = false; + endofstream = true; + break; + } + else + { + rawdata = readchar; + } - // Extract Sync Signal - vsync_raw = (rawdata & 128) > 0; - hsync_raw = (rawdata & 64) > 0; + // Safe former sync state + vsyncc = vsync; hsyncc = hsync; - //invert sync signal if required - if (invert_sync) - { - hsync_raw = !hsync_raw; - vsync_raw = !vsync_raw; - } + // Extract Sync Signal + vsync_raw = (rawdata & 128) > 0; + hsync_raw = (rawdata & 64) > 0; + //invert sync signal if required + if (invert_sync) + { + hsync_raw = !hsync_raw; + vsync_raw = !vsync_raw; + } + + + // Extract Color + if (invert_color) + color = (int)rawdata ^ 0x3F; + else + color = (int)rawdata & 0x3F; - // Extract Color - if (invert_color) - color = (int)rawdata ^ 0x3F; - else - color = (int)rawdata & 0x3F; + /* + // Generic bit shifting to extract color bits + blue = (byte)(127 * (byte)(color & 1)); + color = color >> 1; + green = (byte)(127 * (byte)(color & 1)); + color = color >> 1; + red = (byte)(127 * (byte)(color & 1)); - // Generic bit shifting to extract color bits - blue = (byte)(127 * (byte)(color & 1)); - color = color >> 1; - green = (byte)(127 * (byte)(color & 1)); - color = color >> 1; - red = (byte)(127 * (byte)(color & 1)); + switch (colorMode) + { + case 0: //RGB is just one intensity level + red = (byte)(red * 2); + green = (byte)(green * 2); + blue = (byte)(blue * 2); + break; + case 1: // MDA relies on green and intensity on green + color = color >> 1; + green = (byte)(green * ((byte)(color & 1) + 1)); + blue = green; + red = green; + break; + case 2: // CGA has intensity as well (on green) + color = color >> 1; + blue = (byte)(blue * ((byte)(color & 1) + 1)); + green = (byte)(green * ((byte)(color & 1) + 1)); + red = (byte)(red * ((byte)(color & 1) + 1)); + break; + case 3: // For EGA + color = color >> 1; + green = (byte)(green * ((byte)(color & 1) + 1)); + color = color >> 1; + red = (byte)(red * ((byte)(color & 1) + 1)); + color = color >> 1; + blue = (byte)(blue * ((byte)(color & 1) + 1)); + break; + }*/ + + //Low bits + blueL = (byte)(color & 1); + color = color >> 1; + greenL = (byte)(color & 1); + color = color >> 1; + redL = (byte)(color & 1); + //High bits (intensity) + color = color >> 1; + greenH = (byte)(color & 1); + color = color >> 1; + redH = (byte)(color & 1); + color = color >> 1; + blueH = (byte)(color & 1); switch (colorMode) { case 0: //RGB is just one intensity level - red = (byte)(red * 2); - green = (byte)(green * 2); - blue = (byte)(blue * 2); + red = (byte)(redL * 255); + green = (byte)(greenL * 255); + blue = (byte)(blueL * 255); break; case 1: // MDA relies on green and intensity on green - color = color >> 1; - green = (byte)(green * ((byte)(color & 1) + 1)); + green = (byte)((greenL * 170) + (greenH * 85)); blue = green; red = green; break; case 2: // CGA has intensity as well (on green) - color = color >> 1; - blue = (byte)(blue * ((byte)(color & 1) + 1)); - green = (byte)(green * ((byte)(color & 1) + 1)); - red = (byte)(red * ((byte)(color & 1) + 1)); + red = (byte)((redL * 170) + (greenH * 85)); + green = (byte)((greenL * 170) + (greenH * 85)); + blue = (byte)((blueL * 170) + (greenH * 85)); break; case 3: // For EGA - color = color >> 1; - green = (byte)(green * ((byte)(color & 1) + 1)); - color = color >> 1; - red = (byte)(red * ((byte)(color & 1) + 1)); - color = color >> 1; - blue = (byte)(blue * ((byte)(color & 1) + 1)); + green = (byte)(85 * ((greenL) | (greenH << 1))); + blue = (byte)(85 * ((blueL) | (blueH << 1))); + red = (byte)(85 * ((redL) | (redH << 1))); break; } - // Remove Sync Noise by scanning 3 subsequent signals + // Remove Sync Noise by scanning 3 subsequent signals (filterbits set to b111 dec7 vsync_filter = vsync_filter << 1; hsync_filter = hsync_filter << 1; vsync_filter = (vsync_filter | (vsync_raw ? 1 : 0)); @@ -300,25 +443,31 @@ await Task.Run(() => //composite handling if (composite_sync) { + // Count the time the sync signal is present if (vsync) syncpulses++; else { + // We have no signal now.... + + // Get a high watermark for vsync if (syncpulses > sync_long) { sync_long = syncpulses; } + // If we just got a smal sync, arm the system for an upcoming hsync if (syncpulses > 0 && syncpulses < (sync_long / 10)) { armed = true; } + // Armed and a long sync pulse -> vsync if (armed && (syncpulses > (sync_long * 0.9))) { vcompositeflag = true; armed = false; } - else + else // Special case inverted hysnc during vsync, keep sync signal active to fill the gap if (!armed) vsync = true; syncpulses = 0; @@ -349,7 +498,7 @@ await Task.Run(() => { if (xpos < width) { - if (xpos < xposmax_b && ypos < yposmax_b) + if (xpos < xposmax_b && ypos < yposmax_b) // Within defined bitmap ? // Fast setpixel unsafe { @@ -371,7 +520,7 @@ await Task.Run(() => TB1 = xposmax; TB2 = yposmax; - if (xposmax_b < 10 || yposmax_b < 10) return null; // received fram too small, discard + if (xposmax_b < 10 || yposmax_b < 10) return null; // received frame is too small, discard else { //experiment @@ -393,12 +542,21 @@ private void reset() //vIn.DiscardBufferedData(); } - private async void Form1_FormClosing(object sender, FormClosingEventArgs e) + private void Form1_FormClosing(object sender, FormClosingEventArgs e) { + int p = -1; + + processIds.TryDequeue(out p); + Console.WriteLine("Closing and shutting down " + p.ToString()); + + // Housekeeping, kill sigrok process if present + try + { + Process processes = Process.GetProcessById(p); + if (p != 0) processes.Kill(); + } + catch (Exception ex) { Console.WriteLine(ex.Message); } - System.Threading.Thread.Sleep(1000); - System.Windows.Forms.Application.ExitThread(); - System.Windows.Forms.Application.Exit(); } @@ -442,6 +600,48 @@ private void radioButton5_CheckedChanged(object sender, EventArgs e) colorMode = 3; } + private void radioButton6_CheckedChanged(object sender, EventArgs e) + { + originalSize = true; + } + + private void radioButton7_CheckedChanged(object sender, EventArgs e) + { + originalSize = false; + } + + private void textBox5_TextChanged(object sender, EventArgs e) + { + try + { + int xr = Convert.ToInt16(textBox5.Text); + + if (xr > 0) + { + originalSize = false; + radioButton6.Checked = false; + radioButton7.Checked = true; + xResize = xr; + } + } catch { textBox5.Text = ""; } + } + + private void textBox6_TextChanged(object sender, EventArgs e) + { + try + { + int yr = Convert.ToInt16(textBox6.Text); + + if (yr > 0) + { + originalSize = false; + radioButton6.Checked = false; + radioButton7.Checked = true; + yResize = yr; + } + } + catch { textBox6.Text = ""; } + } } } diff --git a/Form1.resx b/Form1.resx index 80e1e44..0b1f46f 100644 --- a/Form1.resx +++ b/Form1.resx @@ -120,4 +120,7 @@ 17, 17 + + 85 + \ No newline at end of file