Added TAP file injection. Still incomplete.
This commit is contained in:
152
Core/Cpu/Z80.cs
152
Core/Cpu/Z80.cs
@@ -40,11 +40,13 @@ namespace Core.Cpu
|
|||||||
// The Memory Bus
|
// The Memory Bus
|
||||||
private readonly IMemory _memory;
|
private readonly IMemory _memory;
|
||||||
private readonly IO_Bus _simpleIoBus;
|
private readonly IO_Bus _simpleIoBus;
|
||||||
|
public TapManager _tapManager;
|
||||||
|
|
||||||
public Z80(IMemory memory, IO_Bus ioBus)
|
public Z80(IMemory memory, IO_Bus ioBus, TapManager tapManager)
|
||||||
{
|
{
|
||||||
_memory = memory;
|
_memory = memory;
|
||||||
_simpleIoBus = ioBus;
|
_simpleIoBus = ioBus;
|
||||||
|
_tapManager = tapManager;
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +131,12 @@ namespace Core.Cpu
|
|||||||
|
|
||||||
public int Step()
|
public int Step()
|
||||||
{
|
{
|
||||||
|
if (PC == 0x0556 && _tapManager.HasBlocks)
|
||||||
|
{
|
||||||
|
HandleInstantTapeLoad();
|
||||||
|
return 100; // Return a dummy number of T-States for the hijacking
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch the next opcode and increment the Program Counter
|
// Fetch the next opcode and increment the Program Counter
|
||||||
byte opcode = _memory.Read(PC++);
|
byte opcode = _memory.Read(PC++);
|
||||||
int tStates = ExecuteOpcode(opcode);
|
int tStates = ExecuteOpcode(opcode);
|
||||||
@@ -138,6 +146,52 @@ namespace Core.Cpu
|
|||||||
return tStates;
|
return tStates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleInstantTapeLoad()
|
||||||
|
{
|
||||||
|
// 1. Grab the next block from the virtual cassette
|
||||||
|
byte[] block = _tapManager.GetNextBlock();
|
||||||
|
if (block == null) return; // Tape ended unexpectedly
|
||||||
|
|
||||||
|
// 2. Verify the block type.
|
||||||
|
// The ROM passes the expected flag (0x00 for Header, 0xFF for Data) in the A register.
|
||||||
|
byte expectedFlag = AF.High;
|
||||||
|
if (block[0] != expectedFlag)
|
||||||
|
{
|
||||||
|
// Tape loading error! Simulate a failure by clearing the Carry flag and returning.
|
||||||
|
AF.Low &= unchecked((byte)~0x01);
|
||||||
|
ExecuteRet();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Copy the data block straight into the Spectrum's RAM!
|
||||||
|
// DE holds the number of bytes the ROM *wants*. We copy that much, skipping the Flag byte.
|
||||||
|
int bytesToCopy = DE.Word;
|
||||||
|
for (int i = 0; i < bytesToCopy; i++)
|
||||||
|
{
|
||||||
|
_memory.Write((ushort)(IX.Word + i), block[i + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Update the registers exactly how the ROM would after a successful load
|
||||||
|
IX.Word = (ushort)(IX.Word + bytesToCopy);
|
||||||
|
DE.Word = 0;
|
||||||
|
|
||||||
|
// 5. Set the Carry Flag to 1 (Success)
|
||||||
|
AF.Low |= 0x01;
|
||||||
|
|
||||||
|
// 6. Simulate a standard 'RET' instruction to exit the LD-BYTES routine safely
|
||||||
|
ExecuteRet();
|
||||||
|
}
|
||||||
|
|
||||||
|
// A quick helper to simulate a RET instruction manually
|
||||||
|
private void ExecuteRet()
|
||||||
|
{
|
||||||
|
byte pcLow = _memory.Read(SP);
|
||||||
|
SP++;
|
||||||
|
byte pcHigh = _memory.Read(SP);
|
||||||
|
SP++;
|
||||||
|
PC = (ushort)((pcHigh << 8) | pcLow);
|
||||||
|
}
|
||||||
|
|
||||||
// Reads a 16-bit word from the current PC (Little-Endian) and advances PC by 2
|
// Reads a 16-bit word from the current PC (Little-Endian) and advances PC by 2
|
||||||
private ushort FetchWord()
|
private ushort FetchWord()
|
||||||
{
|
{
|
||||||
@@ -1624,6 +1678,17 @@ namespace Core.Cpu
|
|||||||
_memory.Write(address36, n36);
|
_memory.Write(address36, n36);
|
||||||
|
|
||||||
return 19;
|
return 19;
|
||||||
|
case 0x5E: // LD E, (IX+d)
|
||||||
|
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||||
|
sbyte offset5E = (sbyte)FetchByte();
|
||||||
|
|
||||||
|
// 2. Calculate the exact memory address (IX + offset)
|
||||||
|
ushort address5E = (ushort)(IX.Word + offset5E);
|
||||||
|
|
||||||
|
// 3. Read the byte from memory and drop it into the E register
|
||||||
|
DE.Low = _memory.Read(address5E);
|
||||||
|
|
||||||
|
return 19; // 19 T-States
|
||||||
case 0x74: // LD (IX+d), H
|
case 0x74: // LD (IX+d), H
|
||||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||||
sbyte offset74 = (sbyte)FetchByte();
|
sbyte offset74 = (sbyte)FetchByte();
|
||||||
@@ -1646,6 +1711,55 @@ namespace Core.Cpu
|
|||||||
_memory.Write(address75, HL.Low);
|
_memory.Write(address75, HL.Low);
|
||||||
|
|
||||||
return 19;
|
return 19;
|
||||||
|
case 0x7E: // LD A, (IX+d)
|
||||||
|
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||||
|
sbyte offset7E = (sbyte)FetchByte();
|
||||||
|
|
||||||
|
// 2. Calculate the exact memory address (IX + offset)
|
||||||
|
ushort address7E = (ushort)(IX.Word + offset7E);
|
||||||
|
|
||||||
|
// 3. Read the byte from memory and drop it straight into the Accumulator (A)
|
||||||
|
AF.High = _memory.Read(address7E);
|
||||||
|
|
||||||
|
return 19;
|
||||||
|
case 0xBE: // CP (IX+d)
|
||||||
|
// 1. Fetch the displacement byte and calculate the address
|
||||||
|
sbyte offsetBE = (sbyte)FetchByte();
|
||||||
|
ushort addressBE = (ushort)(IX.Word + offsetBE);
|
||||||
|
|
||||||
|
// 2. Read the value from memory
|
||||||
|
byte cpVal = _memory.Read(addressBE);
|
||||||
|
|
||||||
|
// 3. Perform the phantom subtraction
|
||||||
|
int aVal = AF.High;
|
||||||
|
result = aVal - cpVal;
|
||||||
|
|
||||||
|
// --- 8-Bit Compare Flag Calculation (Identical to SUB) ---
|
||||||
|
newFlags = 0;
|
||||||
|
|
||||||
|
// S Flag (Bit 7): Set if the phantom result is negative
|
||||||
|
if ((result & 0x80) != 0) newFlags |= 0x80;
|
||||||
|
|
||||||
|
// Z Flag (Bit 6): Set if A perfectly matches the memory value (A - value == 0)
|
||||||
|
if ((result & 0xFF) == 0) newFlags |= 0x40;
|
||||||
|
|
||||||
|
// H Flag (Bit 4): Set if there was a borrow from Bit 3
|
||||||
|
if (((aVal & 0x0F) - (cpVal & 0x0F)) < 0) newFlags |= 0x10;
|
||||||
|
|
||||||
|
// P/V Flag (Bit 2): Set on Overflow
|
||||||
|
if ((((aVal ^ cpVal) & (aVal ^ result)) & 0x80) != 0) newFlags |= 0x04;
|
||||||
|
|
||||||
|
// N Flag (Bit 1): Always set to 1 for Subtractions/Compares
|
||||||
|
newFlags |= 0x02;
|
||||||
|
|
||||||
|
// C Flag (Bit 0): Set if A was smaller than the memory value
|
||||||
|
if (aVal < cpVal) newFlags |= 0x01;
|
||||||
|
|
||||||
|
AF.Low = newFlags;
|
||||||
|
|
||||||
|
// CRITICAL: Notice we do NOT update AF.High! The Accumulator is preserved.
|
||||||
|
|
||||||
|
return 19; // 19 T-States
|
||||||
case 0xE1: // POP IX
|
case 0xE1: // POP IX
|
||||||
// 1. Read the low byte from the top of the stack
|
// 1. Read the low byte from the top of the stack
|
||||||
byte popLow = _memory.Read(SP);
|
byte popLow = _memory.Read(SP);
|
||||||
@@ -1875,6 +1989,42 @@ namespace Core.Cpu
|
|||||||
AF.High = (byte)result;
|
AF.High = (byte)result;
|
||||||
|
|
||||||
return 19;
|
return 19;
|
||||||
|
case 0xBE: // CP (IY+d)
|
||||||
|
// 1. Fetch the displacement byte and calculate the address using IY
|
||||||
|
sbyte offsetBE = (sbyte)FetchByte();
|
||||||
|
ushort addressBE = (ushort)(IY.Word + offsetBE);
|
||||||
|
|
||||||
|
// 2. Read the value from memory
|
||||||
|
byte cpVal = _memory.Read(addressBE);
|
||||||
|
|
||||||
|
// 3. Perform the phantom subtraction
|
||||||
|
aVal = AF.High;
|
||||||
|
result = aVal - cpVal;
|
||||||
|
|
||||||
|
// --- 8-Bit Compare Flag Calculation (Identical to SUB) ---
|
||||||
|
newFlags = 0;
|
||||||
|
|
||||||
|
// S Flag (Bit 7): Set if the phantom result is negative
|
||||||
|
if ((result & 0x80) != 0) newFlags |= 0x80;
|
||||||
|
|
||||||
|
// Z Flag (Bit 6): Set if A perfectly matches the memory value
|
||||||
|
if ((result & 0xFF) == 0) newFlags |= 0x40;
|
||||||
|
|
||||||
|
// H Flag (Bit 4): Set if there was a borrow from Bit 3
|
||||||
|
if (((aVal & 0x0F) - (cpVal & 0x0F)) < 0) newFlags |= 0x10;
|
||||||
|
|
||||||
|
// P/V Flag (Bit 2): Set on Overflow
|
||||||
|
if ((((aVal ^ cpVal) & (aVal ^ result)) & 0x80) != 0) newFlags |= 0x04;
|
||||||
|
|
||||||
|
// N Flag (Bit 1): Always set to 1 for Subtractions/Compares
|
||||||
|
newFlags |= 0x02;
|
||||||
|
|
||||||
|
// C Flag (Bit 0): Set if A was smaller than the memory value
|
||||||
|
if (aVal < cpVal) newFlags |= 0x01;
|
||||||
|
|
||||||
|
AF.Low = newFlags;
|
||||||
|
|
||||||
|
return 19; // 19 T-States
|
||||||
case 0xCB: // The FD CB nested prefix
|
case 0xCB: // The FD CB nested prefix
|
||||||
{
|
{
|
||||||
sbyte displacement = (sbyte)FetchByte();
|
sbyte displacement = (sbyte)FetchByte();
|
||||||
|
|||||||
39
Core/Io/TapManager.cs
Normal file
39
Core/Io/TapManager.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Core.Io
|
||||||
|
{
|
||||||
|
public class TapManager
|
||||||
|
{
|
||||||
|
private Queue<byte[]> _blocks = new Queue<byte[]>();
|
||||||
|
|
||||||
|
|
||||||
|
public void LoadTapData(byte[] fileData)
|
||||||
|
{
|
||||||
|
_blocks.Clear();
|
||||||
|
int position = 0;
|
||||||
|
|
||||||
|
while (position < fileData.Length)
|
||||||
|
{
|
||||||
|
// 1. Read the 16-bit block length (Little Endian)
|
||||||
|
int blockLength = fileData[position] | (fileData[position + 1] << 8);
|
||||||
|
position += 2;
|
||||||
|
|
||||||
|
// 2. Extract the block payload
|
||||||
|
byte[] blockData = new byte[blockLength];
|
||||||
|
Array.Copy(fileData, position, blockData, 0, blockLength);
|
||||||
|
position += blockLength;
|
||||||
|
|
||||||
|
// 3. Queue it up
|
||||||
|
_blocks.Enqueue(blockData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] GetNextBlock()
|
||||||
|
{
|
||||||
|
return _blocks.Count > 0 ? _blocks.Dequeue() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasBlocks => _blocks.Count > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -850,6 +850,13 @@ namespace Desktop
|
|||||||
mnemonic = $"LD (IX{sign}{d}), 0x{n:X2}";
|
mnemonic = $"LD (IX{sign}{d}), 0x{n:X2}";
|
||||||
instructionLength = 4;
|
instructionLength = 4;
|
||||||
}
|
}
|
||||||
|
else if (ddOpcode == 0x5E) // LD E, (IX+d)
|
||||||
|
{
|
||||||
|
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||||
|
string sign = d >= 0 ? "+" : "";
|
||||||
|
mnemonic = $"LD E, (IX{sign}{d})";
|
||||||
|
instructionLength = 3;
|
||||||
|
}
|
||||||
else if (ddOpcode == 0x74) // LD (IX+d), H
|
else if (ddOpcode == 0x74) // LD (IX+d), H
|
||||||
{
|
{
|
||||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||||
@@ -864,6 +871,20 @@ namespace Desktop
|
|||||||
mnemonic = $"LD (IX{sign}{d}), L";
|
mnemonic = $"LD (IX{sign}{d}), L";
|
||||||
instructionLength = 3;
|
instructionLength = 3;
|
||||||
}
|
}
|
||||||
|
else if (ddOpcode == 0x7E) // LD A, (IX+d)
|
||||||
|
{
|
||||||
|
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||||
|
string sign = d >= 0 ? "+" : "";
|
||||||
|
mnemonic = $"LD A, (IX{sign}{d})";
|
||||||
|
instructionLength = 3;
|
||||||
|
}
|
||||||
|
else if (ddOpcode == 0xBE) // CP (IX+d)
|
||||||
|
{
|
||||||
|
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||||
|
string sign = d >= 0 ? "+" : "";
|
||||||
|
mnemonic = $"CP (IX{sign}{d})";
|
||||||
|
instructionLength = 3;
|
||||||
|
}
|
||||||
else if (ddOpcode == 0xE1) // POP IX
|
else if (ddOpcode == 0xE1) // POP IX
|
||||||
{
|
{
|
||||||
mnemonic = "POP IX";
|
mnemonic = "POP IX";
|
||||||
|
|||||||
43
Desktop/Form1.Designer.cs
generated
43
Desktop/Form1.Designer.cs
generated
@@ -29,37 +29,72 @@
|
|||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
picScreen = new PictureBox();
|
picScreen = new PictureBox();
|
||||||
|
menuStrip1 = new MenuStrip();
|
||||||
|
fileToolStripMenuItem = new ToolStripMenuItem();
|
||||||
|
openToolStripMenuItem = new ToolStripMenuItem();
|
||||||
((System.ComponentModel.ISupportInitialize)picScreen).BeginInit();
|
((System.ComponentModel.ISupportInitialize)picScreen).BeginInit();
|
||||||
|
menuStrip1.SuspendLayout();
|
||||||
SuspendLayout();
|
SuspendLayout();
|
||||||
//
|
//
|
||||||
// picScreen
|
// picScreen
|
||||||
//
|
//
|
||||||
picScreen.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
|
|
||||||
picScreen.BackColor = Color.Black;
|
picScreen.BackColor = Color.Black;
|
||||||
picScreen.Location = new Point(13, 13);
|
picScreen.Location = new Point(13, 33);
|
||||||
picScreen.Margin = new Padding(4);
|
picScreen.Margin = new Padding(4);
|
||||||
picScreen.Name = "picScreen";
|
picScreen.Name = "picScreen";
|
||||||
picScreen.Size = new Size(960, 768);
|
picScreen.Size = new Size(900, 720);
|
||||||
picScreen.SizeMode = PictureBoxSizeMode.Zoom;
|
picScreen.SizeMode = PictureBoxSizeMode.Zoom;
|
||||||
picScreen.TabIndex = 0;
|
picScreen.TabIndex = 0;
|
||||||
picScreen.TabStop = false;
|
picScreen.TabStop = false;
|
||||||
//
|
//
|
||||||
|
// menuStrip1
|
||||||
|
//
|
||||||
|
menuStrip1.ImageScalingSize = new Size(24, 24);
|
||||||
|
menuStrip1.Items.AddRange(new ToolStripItem[] { fileToolStripMenuItem });
|
||||||
|
menuStrip1.Location = new Point(0, 0);
|
||||||
|
menuStrip1.Name = "menuStrip1";
|
||||||
|
menuStrip1.Size = new Size(922, 33);
|
||||||
|
menuStrip1.TabIndex = 1;
|
||||||
|
menuStrip1.Text = "menuStrip1";
|
||||||
|
//
|
||||||
|
// fileToolStripMenuItem
|
||||||
|
//
|
||||||
|
fileToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { openToolStripMenuItem });
|
||||||
|
fileToolStripMenuItem.Name = "fileToolStripMenuItem";
|
||||||
|
fileToolStripMenuItem.Size = new Size(54, 29);
|
||||||
|
fileToolStripMenuItem.Text = "File";
|
||||||
|
//
|
||||||
|
// openToolStripMenuItem
|
||||||
|
//
|
||||||
|
openToolStripMenuItem.Name = "openToolStripMenuItem";
|
||||||
|
openToolStripMenuItem.Size = new Size(158, 34);
|
||||||
|
openToolStripMenuItem.Text = "Open";
|
||||||
|
openToolStripMenuItem.Click += loadTAPToolStripMenuItem_Click;
|
||||||
|
//
|
||||||
// Form1
|
// Form1
|
||||||
//
|
//
|
||||||
AutoScaleDimensions = new SizeF(10F, 25F);
|
AutoScaleDimensions = new SizeF(10F, 25F);
|
||||||
AutoScaleMode = AutoScaleMode.Font;
|
AutoScaleMode = AutoScaleMode.Font;
|
||||||
ClientSize = new Size(987, 791);
|
ClientSize = new Size(922, 760);
|
||||||
Controls.Add(picScreen);
|
Controls.Add(picScreen);
|
||||||
|
Controls.Add(menuStrip1);
|
||||||
KeyPreview = true;
|
KeyPreview = true;
|
||||||
|
MainMenuStrip = menuStrip1;
|
||||||
Margin = new Padding(4);
|
Margin = new Padding(4);
|
||||||
Name = "Form1";
|
Name = "Form1";
|
||||||
Text = "Parsons Sinclair ZX Spectrum 48K - 2026";
|
Text = "Parsons Sinclair ZX Spectrum 48K - 2026";
|
||||||
((System.ComponentModel.ISupportInitialize)picScreen).EndInit();
|
((System.ComponentModel.ISupportInitialize)picScreen).EndInit();
|
||||||
|
menuStrip1.ResumeLayout(false);
|
||||||
|
menuStrip1.PerformLayout();
|
||||||
ResumeLayout(false);
|
ResumeLayout(false);
|
||||||
|
PerformLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private PictureBox picScreen;
|
private PictureBox picScreen;
|
||||||
|
private MenuStrip menuStrip1;
|
||||||
|
private ToolStripMenuItem fileToolStripMenuItem;
|
||||||
|
private ToolStripMenuItem openToolStripMenuItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace Desktop
|
|||||||
private Z80 _cpu = null!;
|
private Z80 _cpu = null!;
|
||||||
private MemoryBus _memoryBus = null!;
|
private MemoryBus _memoryBus = null!;
|
||||||
private IO_Bus _simpleIoBus = null!;
|
private IO_Bus _simpleIoBus = null!;
|
||||||
|
private TapManager _tapManager = null!;
|
||||||
private int _ulaFrameCount = 0;
|
private int _ulaFrameCount = 0;
|
||||||
|
|
||||||
// The 16 physical colors of the ZX Spectrum (ARGB format)
|
// The 16 physical colors of the ZX Spectrum (ARGB format)
|
||||||
@@ -52,12 +53,12 @@ namespace Desktop
|
|||||||
{
|
{
|
||||||
_memoryBus = new MemoryBus();
|
_memoryBus = new MemoryBus();
|
||||||
_simpleIoBus = new IO_Bus();
|
_simpleIoBus = new IO_Bus();
|
||||||
|
_tapManager = new TapManager();
|
||||||
_memoryBus.CrapRAMData();
|
_memoryBus.CrapRAMData();
|
||||||
byte[] romData = RomLoader.Load("48.rom");
|
byte[] romData = RomLoader.Load("48.rom");
|
||||||
_memoryBus.LoadRom(romData);
|
_memoryBus.LoadRom(romData);
|
||||||
|
|
||||||
_cpu = new Z80(_memoryBus, _simpleIoBus);
|
_cpu = new Z80(_memoryBus, _simpleIoBus, _tapManager);
|
||||||
|
|
||||||
// Pass 'this' so the DebuggerForm can talk back to this main window
|
// Pass 'this' so the DebuggerForm can talk back to this main window
|
||||||
DebuggerForm debugger = new DebuggerForm(_cpu, _memoryBus, this);
|
DebuggerForm debugger = new DebuggerForm(_cpu, _memoryBus, this);
|
||||||
@@ -69,6 +70,25 @@ namespace Desktop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inside Desktop/Form1.cs
|
||||||
|
private void loadTAPToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
using (OpenFileDialog ofd = new OpenFileDialog())
|
||||||
|
{
|
||||||
|
ofd.Filter = "Spectrum TAP Files|*.tap";
|
||||||
|
if (ofd.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
// The Desktop UI reads the file from the hard drive
|
||||||
|
byte[] tapBytes = System.IO.File.ReadAllBytes(ofd.FileName);
|
||||||
|
|
||||||
|
// The pure Core logic processes the bytes
|
||||||
|
_cpu._tapManager.LoadTapData(tapBytes);
|
||||||
|
|
||||||
|
MessageBox.Show("Tape inserted! Type LOAD \"\" and press Enter.", "Tape Deck");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Public so the Debugger's background thread can call it 50 times a second
|
// Public so the Debugger's background thread can call it 50 times a second
|
||||||
public void RenderScreen()
|
public void RenderScreen()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -117,4 +117,7 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
|
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>17, 17</value>
|
||||||
|
</metadata>
|
||||||
</root>
|
</root>
|
||||||
Reference in New Issue
Block a user