Commits

Kareem Callender  committed 35b020d Draft

Corrected a little thing in the code returning an instruction relating to
db instructions.
Corrected instructionCache issue.
Reduced complexity of Labels, added unique ID for equality comparison.
As a result of the reduce, equality is determined by unique ID.
Labels that pint to the same offset should be possible.
Reduced complexity of loading/saving labels, added comments, added
locking behavior for both comments and "symbols".
Added default.txt, for default labels on new projects
In all forms, use the actual symbol list instead of a different method.
Added a comment adding/editing form, still unused. Comments aren't
usable yet...

  • Participants
  • Parent commits 83a2169

Comments (0)

Files changed (18)

File gbread/ASM/GBInstructions.cs

 			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.ret, Arg1 = NCCond },
 			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.pop, Arg1 = RegDE },
 			new GBInstruction() { InstSize = 3, ArgCount = 2, InstType = InstructionType.jp, Arg1 = NCCond, Arg2 = WordArg },
-			new GBInstruction() { InstSize = 1, ArgCount = 0, InstType = InstructionType.db },
+			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.db, Arg1 = ByteArg },
 			new GBInstruction() { InstSize = 3, ArgCount = 2, InstType = InstructionType.call, Arg1 = NCCond, Arg2 = WordArg },
 			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.push, Arg1 = RegDE },
 			new GBInstruction() { InstSize = 2, ArgCount = 1, InstType = InstructionType.sub, Arg1 = ByteArg },
 			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.ret, Arg1 = CCond },
 			new GBInstruction() { InstSize = 1, ArgCount = 0, InstType = InstructionType.reti },
 			new GBInstruction() { InstSize = 3, ArgCount = 2, InstType = InstructionType.jp, Arg1 = CCond, Arg2 = WordArg },
-			new GBInstruction() { InstSize = 1, ArgCount = 0, InstType = InstructionType.db },
+			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.db, Arg1 = ByteArg },
 			new GBInstruction() { InstSize = 3, ArgCount = 2, InstType = InstructionType.call, Arg1 = CCond, Arg2 = WordArg },
-			new GBInstruction() { InstSize = 1, ArgCount = 0, InstType = InstructionType.db },
+			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.db, Arg1 = ByteArg },
 			new GBInstruction() { InstSize = 2, ArgCount = 2, InstType = InstructionType.sbc, Arg1 = RegA, Arg2 = ByteArg },
 			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.rst, Arg1 = new GBArgument(){ ArgType = GBArgumentType.Byte, NumArg = 0x18 } },
 			#endregion
 			new GBInstruction() { InstSize = 2, ArgCount = 2, InstType = InstructionType.ld, Arg1 = MemMapArg, Arg2 = RegA },
 			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.pop, Arg1 = RegHL },
 			new GBInstruction() { InstSize = 1, ArgCount = 2, InstType = InstructionType.ld, Arg1 = CMap, Arg2 = RegA },
-			new GBInstruction() { InstSize = 1, ArgCount = 0, InstType = InstructionType.db },
-			new GBInstruction() { InstSize = 1, ArgCount = 0, InstType = InstructionType.db },
+			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.db, Arg1 = ByteArg },
+			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.db, Arg1 = ByteArg },
 			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.push, Arg1 = RegHL },
 			new GBInstruction() { InstSize = 2, ArgCount = 1, InstType = InstructionType.and, Arg1 = ByteArg },
 			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.rst, Arg1 = new GBArgument(){ ArgType = GBArgumentType.Byte, NumArg = 0x20 } },
 			new GBInstruction() { InstSize = 2, ArgCount = 2, InstType = InstructionType.add, Arg1 = RegSP, Arg2 = ByteArg },
 			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.jp, Arg1 = RegHL },
 			new GBInstruction() { InstSize = 3, ArgCount = 2, InstType = InstructionType.ld, Arg1 = MemMapArg, Arg2 = RegA },
-			new GBInstruction() { InstSize = 1, ArgCount = 0, InstType = InstructionType.db },
-			new GBInstruction() { InstSize = 1, ArgCount = 0, InstType = InstructionType.db },
-			new GBInstruction() { InstSize = 1, ArgCount = 0, InstType = InstructionType.db },
+			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.db, Arg1 = ByteArg },
+			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.db, Arg1 = ByteArg },
+			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.db, Arg1 = ByteArg },
 			new GBInstruction() { InstSize = 2, ArgCount = 1, InstType = InstructionType.xor, Arg1 = ByteArg },
 			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.rst, Arg1 = new GBArgument(){ ArgType = GBArgumentType.Byte, NumArg = 0x28 } },
 			#endregion
 			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.pop, Arg1 = RegAF },
 			new GBInstruction() { InstSize = 1, ArgCount = 2, InstType = InstructionType.ld, Arg1 = RegA, Arg2 = CMap },
 			new GBInstruction() { InstSize = 1, ArgCount = 0, InstType = InstructionType.di },
-			new GBInstruction() { InstSize = 1, ArgCount = 0, InstType = InstructionType.db },
+			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.db, Arg1 = ByteArg },
 			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.push, Arg1 = RegAF },
 			new GBInstruction() { InstSize = 2, ArgCount = 1, InstType = InstructionType.or, Arg1 = ByteArg },
 			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.rst, Arg1 = new GBArgument(){ ArgType = GBArgumentType.Byte, NumArg = 0x30 } },
 			new GBInstruction() { InstSize = 1, ArgCount = 2, InstType = InstructionType.ld, Arg1 = RegSP, Arg2 = RegHL },
 			new GBInstruction() { InstSize = 3, ArgCount = 2, InstType = InstructionType.ld, Arg1 = RegA, Arg2 = MemMapArg },
 			new GBInstruction() { InstSize = 1, ArgCount = 0, InstType = InstructionType.ei },
-			new GBInstruction() { InstSize = 1, ArgCount = 0, InstType = InstructionType.db },
-			new GBInstruction() { InstSize = 1, ArgCount = 0, InstType = InstructionType.db },
+			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.db, Arg1 = ByteArg },
+			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.db, Arg1 = ByteArg },
 			new GBInstruction() { InstSize = 2, ArgCount = 1, InstType = InstructionType.cp, Arg1 = ByteArg },
 			new GBInstruction() { InstSize = 1, ArgCount = 1, InstType = InstructionType.rst, Arg1 = new GBArgument(){ ArgType = GBArgumentType.Byte, NumArg = 0x38 } }
 			#endregion

File gbread/ASM/InstructionUnit.cs

 //along with LibGBasm.  If not, see <http://www.gnu.org/licenses/>.
 namespace LibGBasm
 {
-	/// <summary>
-	/// Represents the type of instruction.
-	/// </summary>
-	public enum InstructionType { adc, add, and, bit, call, ccf, cb, cp, cpl, db, dw, ei, daa, dec, di, halt, inc, jp, jr, ld, ldi, ldd, ldhl, nop, or, pop, push, res, ret, reti, rl, rla, rlc, rlca, rot, rr, rra, rrc, rrca, rst, sbc, scf, set, sla, sra, srl, stop, sub, swap, xor }
+    /// <summary>
+    /// Represents the type of instruction.
+    /// </summary>
+    public enum InstructionType { adc, add, and, bit, call, ccf, cb, cp, cpl, db, dw, ei, daa, dec, di, halt, inc, jp, jr, ld, ldi, ldd, ldhl, nop, or, pop, push, res, ret, reti, rl, rla, rlc, rlca, rot, rr, rra, rrc, rrca, rst, sbc, scf, set, sla, sra, srl, stop, sub, swap, xor }
 
-	/// <summary>
-	/// The type of a GBArgument. Depending on the type of argument, the values within are interpreted differently.
-	/// </summary>
-	public enum GBArgumentType { None, Bit, Byte, Word, MemMapWord, Conditional, RegisterSingle, MemMapRegisterSingle, RegisterDouble, MemMapRegisterDouble }
+    /// <summary>
+    /// The type of a GBArgument. Depending on the type of argument, the values within are interpreted differently.
+    /// </summary>
+    public enum GBArgumentType { None, Bit, Byte, Word, MemMapWord, Conditional, RegisterSingle, MemMapRegisterSingle, RegisterDouble, MemMapRegisterDouble }
 
-	/// <summary>
-	/// Represents a single register.
-	/// </summary>
-	public enum GBRegisterSingle { a, b, c, d, e, h, l }
+    /// <summary>
+    /// Represents a single register.
+    /// </summary>
+    public enum GBRegisterSingle { a, b, c, d, e, h, l }
 
-	//Represents a register pair.
-	public enum GBRegisterDouble { af, bc, de, hl, sp }
+    /// <summary>
+    /// Represents a register pair.
+    /// </summary>
+    public enum GBRegisterDouble { af, bc, de, hl, sp }
 
-	/// <summary>
-	/// Represents a conditional statement.
-	/// </summary>
-	public enum GBConditional { nz, z, nc, c }
+    /// <summary>
+    /// Represents a conditional statement.
+    /// </summary>
+    public enum GBConditional { nz, z, nc, c }
 
-	/// <summary>
-	/// Represents a single GB instruction.
-	/// </summary>
-	public struct GBInstruction
-	{
-		/// <summary>
-		/// The bank containing the instruction.
-		/// </summary>
-		public byte Bank;
-		/// <summary>
-		/// The address of the instruction.
-		/// </summary>
-		public ushort Address;
-		/// <summary>
-		/// The size of the argument, in bytes.
-		/// </summary>
-		public int InstSize;
-		/// <summary>
-		/// The type of instruction.
-		/// </summary>
-		public InstructionType InstType;
-		/// <summary>
-		/// The number of arguments to this instruction.
-		/// </summary>
-		public int ArgCount;
-		/// <summary>
-		/// The first argument of the instruction.
-		/// </summary>
-		public GBArgument Arg1;
-		/// <summary>
-		/// The second argument of the instruction.
-		/// </summary>
-		public GBArgument Arg2;
-	}
+    /// <summary>
+    /// Represents a single GB instruction.
+    /// </summary>
+    public struct GBInstruction
+    {
+        /// <summary>
+        /// The bank containing the instruction.
+        /// </summary>
+        public byte Bank;
+        /// <summary>
+        /// The address of the instruction.
+        /// </summary>
+        public ushort Address;
+        /// <summary>
+        /// The size of the argument, in bytes.
+        /// </summary>
+        public int InstSize;
+        /// <summary>
+        /// The type of instruction.
+        /// </summary>
+        public InstructionType InstType;
+        /// <summary>
+        /// The number of arguments to this instruction.
+        /// </summary>
+        public int ArgCount;
+        /// <summary>
+        /// The first argument of the instruction.
+        /// </summary>
+        public GBArgument Arg1;
+        /// <summary>
+        /// The second argument of the instruction.
+        /// </summary>
+        public GBArgument Arg2;
+    }
 
-	/// <summary>
-	/// Represents an argument to a GB instruction.
-	/// </summary>
-	public struct GBArgument
-	{
-		/// <summary>
-		/// The type of argument.
-		/// </summary>
-		public GBArgumentType ArgType;
-		/// <summary>
-		/// The number value of the argument.
-		/// </summary>
-		public ushort NumArg;
-		/// <summary>
-		/// The RegisterSingle value of the argument, if it has one.
-		/// </summary>
-		public GBRegisterSingle RegSingleArg;
-		/// <summary>
-		/// The RegisterDouble value of the argument, if it has one.
-		/// </summary>
-		public GBRegisterDouble RegDoubleArg;
-		/// <summary>
-		/// The Conditional value of the argument, if it has one.
-		/// </summary>
-		public GBConditional CondArg;
-	}
+    /// <summary>
+    /// Represents an argument to a GB instruction.
+    /// </summary>
+    public struct GBArgument
+    {
+        /// <summary>
+        /// The type of argument.
+        /// </summary>
+        public GBArgumentType ArgType;
+        /// <summary>
+        /// The number value of the argument.
+        /// </summary>
+        public ushort NumArg;
+        /// <summary>
+        /// The RegisterSingle value of the argument, if it has one.
+        /// </summary>
+        public GBRegisterSingle RegSingleArg;
+        /// <summary>
+        /// The RegisterDouble value of the argument, if it has one.
+        /// </summary>
+        public GBRegisterDouble RegDoubleArg;
+        /// <summary>
+        /// The Conditional value of the argument, if it has one.
+        /// </summary>
+        public GBConditional CondArg;
+    }
 }

File gbread/ASM/LibGBasm.cs

 //along with LibGBasm.  If not, see <http://www.gnu.org/licenses/>.
 namespace LibGBasm
 {
-	public class GBASM
-	{
-		/// <summary>
-		/// Given a binary file and the appropriate offsets, constructs a GBInstructionUnit containing information about the instruction and its arguments.
-		/// </summary>
-		/// <param name="baseFile">The file to read from.</param>
-		/// <param name="org">The origin address of the file.</param>
-		/// <param name="offset">The offset into the file to read from.</param>
-		/// <param name="outputInstruction">The GBInstruction to write the information to.</param>
-		/// <returns>The success of the operation. Returns false if the information fetching went wrong for any reason
-		/// </returns>
-		public static bool GetInstruction(byte[] baseFile, int org, int offset, ref GBInstruction outputInstruction)
-		{
-			outputInstruction = new GBInstruction();
-			if (baseFile == null || offset > baseFile.Length - 1) return false;
-			byte inst = baseFile[offset];
-			int address = org + offset;
-			if (inst == 0xCB)
-			{
-				if (offset > baseFile.Length - 2) return false;
-				outputInstruction = GBInstructions.CBInstructionUnitTable[baseFile[offset + 1]];
-			}
-			else outputInstruction = GBInstructions.InstructionUnitTable[baseFile[offset]];
-			if (offset + outputInstruction.InstSize > baseFile.Length) return false;
-			outputInstruction.Bank = (byte)(address >> 14);
-			outputInstruction.Address = (ushort)(address & 0x3FFF);
-			if (address > 0x4000)
-			{
-				outputInstruction.Address += 0x4000;
-			}
-			if (outputInstruction.InstSize == 1 && outputInstruction.InstType == InstructionType.db)
-			{
-				outputInstruction.ArgCount = 1;
-				outputInstruction.Arg1.ArgType = GBArgumentType.Byte;
-				outputInstruction.Arg1.NumArg = baseFile[offset];
-			}
-			else if (outputInstruction.InstSize == 2 && inst != 0xCB)
-			{
-				if (outputInstruction.ArgCount == 1)
-				{
-					if (outputInstruction.InstType == InstructionType.jr)
-					{
-						//jr nn
-						int modifier = (baseFile[offset + 1] < 0x80 ? baseFile[offset + 1] : -(0x100 - baseFile[offset + 1]));
-						ushort newAddress = (ushort)(outputInstruction.Address + 2 + modifier);
-						outputInstruction.Arg1.NumArg = newAddress;
-					}
-					else
-					{
-						//and, or, sub, cp, xor nn
-						outputInstruction.Arg1.NumArg = baseFile[offset + 1];
-					}
-				}
-				else if (outputInstruction.ArgCount == 2)
-				{
-					if (outputInstruction.InstType == InstructionType.jr)
-					{
-						//jr nn
-						int modifier = (baseFile[offset + 1] < 0x80 ? baseFile[offset + 1] : -(0x100 - baseFile[offset + 1]));
-						ushort newAddress = (ushort)(outputInstruction.Address + 2 + modifier);
-						outputInstruction.Arg2.NumArg = newAddress;
-					}
-					else if (outputInstruction.Arg1.ArgType == GBArgumentType.MemMapWord)
-					{
-						outputInstruction.Arg1.NumArg = (ushort)(0xFF00 + baseFile[offset + 1]);
-					}
-					else if (outputInstruction.Arg2.ArgType == GBArgumentType.MemMapWord)
-					{
-						outputInstruction.Arg2.NumArg = (ushort)(0xFF00 + baseFile[offset + 1]);
-					}
-					else
-					{
-						outputInstruction.Arg2.NumArg = baseFile[offset + 1];
-					}
-				}
-			}
-			else if (outputInstruction.InstSize == 3)
-			{
-				if (outputInstruction.ArgCount == 1 && outputInstruction.Arg1.ArgType == GBArgumentType.Word)
-				{
-					//jp nnnn, call nnnn
-					outputInstruction.Arg1.NumArg = System.BitConverter.ToUInt16(baseFile, offset + 1);
-				}
-				else if (outputInstruction.ArgCount == 2)
-				{
-					if (outputInstruction.Arg1.ArgType == GBArgumentType.MemMapWord)
-					{
-						outputInstruction.Arg1.NumArg = System.BitConverter.ToUInt16(baseFile, offset + 1);
-					}
-					else if (outputInstruction.Arg2.ArgType == GBArgumentType.Word ||
-						outputInstruction.Arg2.ArgType == GBArgumentType.MemMapWord)
-					{
-						outputInstruction.Arg2.NumArg = System.BitConverter.ToUInt16(baseFile, offset + 1);
-					}
-				}
-			}
-			return true;
-		}
+    using System;
 
-		/// <summary>
-		/// Creates a byte-sized data instruction.
-		/// </summary>
-		/// <param name="baseFile">The file to read from.</param>
-		/// <param name="org">The origin address of the file.</param>
-		/// <param name="offset">The offset into the file to read from.</param>
-		/// <param name="outputInstruction">The GBInstruction to write the information to.</param>
-		/// <returns>The success of the operation. Returns false if the information fetching went wrong for any reason.
-		/// </returns>
-		public static bool CreateDBInstruction(byte[] baseFile, int org, int offset, ref GBInstruction outputInstruction)
-		{
-			outputInstruction = new GBInstruction();
-			if (baseFile == null || offset > baseFile.Length - 1) return false;
-			int address = org + offset;
-			outputInstruction.InstSize = 1;
-			outputInstruction.InstType = InstructionType.db;
-			outputInstruction.Bank = (byte)(address >> 14);
-			outputInstruction.Address = (ushort)(address & 0x3FFF);
-			outputInstruction.ArgCount = 1;
-			outputInstruction.Arg1.ArgType = GBArgumentType.Byte;
-			outputInstruction.Arg1.NumArg = baseFile[offset];
-			if (address > 0x4000)
-			{
-				outputInstruction.Address += 0x4000;
-			}
-			return true;
-		}
+    public class GBASM
+    {
+        /// <summary>
+        /// Given a binary file and the appropriate offsets, constructs a GBInstructionUnit containing information about the instruction and its arguments.
+        /// </summary>
+        /// <param name="baseFile">The file to read from.</param>
+        /// <param name="org">The origin address of the file.</param>
+        /// <param name="offset">The offset into the file to read from.</param>
+        /// <param name="outputInstruction">The GBInstruction to write the information to.</param>
+        /// <returns>The success of the operation. Returns false if the information fetching went wrong for any reason
+        /// </returns>
+        public static bool GetInstruction(byte[] baseFile, int org, int offset, ref GBInstruction outputInstruction)
+        {
+            outputInstruction = new GBInstruction();
+            if (baseFile == null || offset > baseFile.Length - 1)
+                return false;
 
-		/// <summary>
-		/// Creates a word-sized data instruction.
-		/// </summary>
-		/// <param name="baseFile">The file to read from.</param>
-		/// <param name="org">The origin address of the file.</param>
-		/// <param name="offset">The offset into the file to read from.</param>
-		/// <param name="outputInstruction">The GBInstruction to write the information to.</param>
-		/// <returns>The success of the operation. Returns false if the information fetching went wrong for any reason.
-		/// </returns>
-		public static bool CreateDWInstruction(byte[] baseFile, int org, int offset, ref GBInstruction outputInstruction)
-		{
-			outputInstruction = new GBInstruction();
-			if (baseFile == null || offset > baseFile.Length - 2) return false;
-			int address = org + offset;
-			outputInstruction.InstSize = 2;
-			outputInstruction.InstType = InstructionType.dw;
-			outputInstruction.Bank = (byte)(address >> 14);
-			outputInstruction.Address = (ushort)(address & 0x3FFF);
-			outputInstruction.ArgCount = 1;
-			outputInstruction.Arg1.ArgType = GBArgumentType.Word;
-			outputInstruction.Arg1.NumArg = System.BitConverter.ToUInt16(baseFile, offset);
-			if (address > 0x4000)
-			{
-				outputInstruction.Address += 0x4000;
-			}
-			return true;
-		}
-	}
+            // Holds the "actual" address of the instruction, in case the array provided isn't the full file.
+            int address = org + offset;
+            byte inst = baseFile[offset];
+            if (inst == 0xCB)
+            {
+                // If the inst is a CB instruction, it's a 2-byte one, and a different table is used.
+                if (offset > baseFile.Length - 2)
+                {
+                    // In case the inst is part of a bigger file, interpret as a DB instruction instead.
+                    // Prior behavior: return false
+                    return GBASM.CreateDBInstruction(baseFile, org, offset, ref outputInstruction);
+                }
+                outputInstruction = GBInstructions.CBInstructionUnitTable[baseFile[offset + 1]];
+            }
+            else
+            {
+                outputInstruction = GBInstructions.InstructionUnitTable[baseFile[offset]];
+            }
+
+            // If the instruction gotten is too big, then return a DB/DW instruction, as default.
+            if (offset + outputInstruction.InstSize > baseFile.Length)
+            {
+                int retSize = 3 - ((offset + outputInstruction.InstSize) - baseFile.Length);
+                switch (retSize)
+                {
+                    case 1:
+                        {
+                            return GBASM.CreateDBInstruction(baseFile, org, offset, ref outputInstruction);
+                        }
+
+                    case 2:
+                        {
+                            return GBASM.CreateDWInstruction(baseFile, org, offset, ref outputInstruction);
+                        }
+
+                    default:
+                        {
+                            return false;
+                        }
+                }
+            }
+
+            // Adjust the gotten inst's bank and address manually
+            outputInstruction.Bank = (byte)(address >> 14);
+            outputInstruction.Address = (ushort)(address & 0x3FFF);
+            if (address > 0x4000)
+            {
+                outputInstruction.Address += 0x4000;
+            }
+
+            // Finally, adjust the values of the arguments if they are dependant on the other bytes
+            // in the instruction.
+            if (outputInstruction.InstSize == 1 && outputInstruction.InstType == InstructionType.db)
+            {
+                outputInstruction.Arg1.NumArg = baseFile[offset];
+            }
+            else if (outputInstruction.InstSize == 2 && inst != 0xCB)
+            {
+                if (outputInstruction.ArgCount == 1)
+                {
+                    if (outputInstruction.InstType == InstructionType.jr)
+                    {
+                        //jr nn
+                        int modifier = (baseFile[offset + 1] < 0x80 ? baseFile[offset + 1] : -(0x100 - baseFile[offset + 1]));
+                        ushort newAddress = (ushort)(outputInstruction.Address + 2 + modifier);
+                        outputInstruction.Arg1.NumArg = newAddress;
+                    }
+                    else
+                    {
+                        //and, or, sub, cp, xor nn
+                        outputInstruction.Arg1.NumArg = baseFile[offset + 1];
+                    }
+                }
+                else if (outputInstruction.ArgCount == 2)
+                {
+                    if (outputInstruction.InstType == InstructionType.jr)
+                    {
+                        //jr nn
+                        int modifier = (baseFile[offset + 1] < 0x80 ? baseFile[offset + 1] : -(0x100 - baseFile[offset + 1]));
+                        ushort newAddress = (ushort)(outputInstruction.Address + 2 + modifier);
+                        outputInstruction.Arg2.NumArg = newAddress;
+                    }
+                    else if (outputInstruction.Arg1.ArgType == GBArgumentType.MemMapWord)
+                    {
+                        outputInstruction.Arg1.NumArg = (ushort)(0xFF00 + baseFile[offset + 1]);
+                    }
+                    else if (outputInstruction.Arg2.ArgType == GBArgumentType.MemMapWord)
+                    {
+                        outputInstruction.Arg2.NumArg = (ushort)(0xFF00 + baseFile[offset + 1]);
+                    }
+                    else
+                    {
+                        outputInstruction.Arg2.NumArg = baseFile[offset + 1];
+                    }
+                }
+            }
+            else if (outputInstruction.InstSize == 3)
+            {
+                if (outputInstruction.ArgCount == 1 && outputInstruction.Arg1.ArgType == GBArgumentType.Word)
+                {
+                    //jp nnnn, call nnnn
+                    outputInstruction.Arg1.NumArg = BitConverter.ToUInt16(baseFile, offset + 1);
+                }
+                else if (outputInstruction.ArgCount == 2)
+                {
+                    if (outputInstruction.Arg1.ArgType == GBArgumentType.MemMapWord)
+                    {
+                        outputInstruction.Arg1.NumArg = BitConverter.ToUInt16(baseFile, offset + 1);
+                    }
+                    else if (outputInstruction.Arg2.ArgType == GBArgumentType.Word ||
+                        outputInstruction.Arg2.ArgType == GBArgumentType.MemMapWord)
+                    {
+                        outputInstruction.Arg2.NumArg = BitConverter.ToUInt16(baseFile, offset + 1);
+                    }
+                }
+            }
+            return true;
+        }
+
+        /// <summary>
+        /// Creates a byte-sized data instruction.
+        /// </summary>
+        /// <param name="baseFile">The file to read from.</param>
+        /// <param name="org">The origin address of the file.</param>
+        /// <param name="offset">The offset into the file to read from.</param>
+        /// <param name="outputInstruction">The GBInstruction to write the information to.</param>
+        /// <returns>Returns true if the creation was successful, and false otherwise.
+        /// </returns>
+        public static bool CreateDBInstruction(byte[] baseFile, int org, int offset, ref GBInstruction outputInstruction)
+        {
+            outputInstruction = new GBInstruction();
+            if (baseFile == null || offset > baseFile.Length - 1)
+                return false;
+            int address = org + offset;
+            outputInstruction.InstSize = 1;
+            outputInstruction.InstType = InstructionType.db;
+            outputInstruction.Bank = (byte)(address >> 14);
+            outputInstruction.Address = (ushort)(address & 0x3FFF);
+            outputInstruction.ArgCount = 1;
+            outputInstruction.Arg1.ArgType = GBArgumentType.Byte;
+            outputInstruction.Arg1.NumArg = baseFile[offset];
+            if (address > 0x4000)
+            {
+                outputInstruction.Address += 0x4000;
+            }
+            return true;
+        }
+
+        /// <summary>
+        /// Creates a word-sized data instruction.
+        /// </summary>
+        /// <param name="baseFile">The file to read from.</param>
+        /// <param name="org">The origin address of the file.</param>
+        /// <param name="offset">The offset into the file to read from.</param>
+        /// <param name="outputInstruction">The GBInstruction to write the information to.</param>
+        /// <returns>Returns true if the creation was successful, and false otherwise.
+        /// </returns>
+        public static bool CreateDWInstruction(byte[] baseFile, int org, int offset, ref GBInstruction outputInstruction)
+        {
+            outputInstruction = new GBInstruction();
+            if (baseFile == null || offset > baseFile.Length - 2)
+                return false;
+            int address = org + offset;
+            outputInstruction.InstSize = 2;
+            outputInstruction.InstType = InstructionType.dw;
+            outputInstruction.Bank = (byte)(address >> 14);
+            outputInstruction.Address = (ushort)(address & 0x3FFF);
+            outputInstruction.ArgCount = 1;
+            outputInstruction.Arg1.ArgType = GBArgumentType.Word;
+            outputInstruction.Arg1.NumArg = BitConverter.ToUInt16(baseFile, offset);
+            if (address > 0x4000)
+            {
+                outputInstruction.Address += 0x4000;
+            }
+            return true;
+        }
+    }
 }

File gbread/Base/Disassembler.cs

-using System;
-using System.Collections.Generic;
-using System.Text;
-using LibGBasm;
+namespace GBRead.Base
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Linq;
+    using System.Text;
+    using LibGBasm;
 
-namespace GBRead.Base
-{
     public class Disassembler
     {
         private BinFile CoreFile;
 
         #region Label Display
 
-        public string ShowDataLabel(DataLabel dLabel)
+        // TODO: Handle the case where the DL goes out of bounds.
+        public string ShowDataLabel(DataLabel dataLabel)
         {
             StringBuilder ret = new StringBuilder(String.Empty);
-            int off = dLabel.Offset;
-            ret.AppendLine(dLabel.ToASMCommentString());
-            switch (dLabel.DSectionType)
+            ret.AppendLine(dataLabel.ToASMCommentString());
+            var labelsIn = (
+                from s in lc.FuncList
+                where s.Offset >= dataLabel.Offset && s.Offset < dataLabel.Offset + dataLabel.Length
+                select new { Name = s.Name, Offset = s.Offset, Comment = s.ToASMCommentString() })
+                .Concat(
+                from s in lc.DataList
+                where s.Offset > dataLabel.Offset && s.Offset < dataLabel.Offset + dataLabel.Length
+                select new { Name = s.Name, Offset = s.Offset, Comment = s.ToASMCommentString() })
+                .OrderBy(s => s.Offset)
+                .ThenBy(s => s.Name);
+            int currentLoc = dataLabel.Offset;
+            foreach (var label in labelsIn)
             {
-                case DataSectionType.Data:
-                case DataSectionType.Image:
-                    for (int i = 0; i < dLabel.Length; i++)
-                    {
-                        if (i % dLabel.DataLineLength == 0)
-                            ret.Append("\tdb ");
-                        byte x = (byte)CoreFile.ReadByte(off + i);
-                        switch (InstructionNumberFormat)
-                        {
-                            case OffsetFormat.Decimal:
-                                ret.Append(x.ToString());
-                                break;
-                            default:
-                                ret.Append("$" + x.ToString("X2"));
-                                break;
-                        }
-                        if (i % dLabel.DataLineLength == dLabel.DataLineLength - 1 || i == dLabel.Length - 1)
-                        {
-                            ret.AppendLine();
-                        }
-                        else
-                        {
-                            ret.Append(",");
-                        }
-                    }
-                    break;
-                default:
-                    break;
+                ret.Append(GetDBSection(currentLoc, label.Offset - currentLoc, dataLabel.DataLineLength));
+                ret.AppendLine(label.Comment);
+                currentLoc = label.Offset;
             }
+            ret.Append(GetDBSection(currentLoc, dataLabel.Length, dataLabel.DataLineLength));
             return ret.ToString();
         }
 
             return ret.ToString();
         }
 
+        private string GetDBSection(int off, int len, int lineSize)
+        {
+            if (len == 0)
+            {
+                return "";
+            }
+            StringBuilder sb = new StringBuilder();
+            int afterLast = off + len;
+            for (int currentOffset = off; currentOffset < afterLast; currentOffset += lineSize)
+            {
+                int dbCount = currentOffset + lineSize > afterLast ? afterLast - currentOffset : lineSize;
+                int last = currentOffset + dbCount - 1;
+                sb.Append("    db ");
+                for (int curOff = currentOffset; curOff < last; curOff++)
+                {
+                    byte curByte = (byte)CoreFile.ReadByte(curOff);
+                    switch (InstructionNumberFormat)
+                    {
+                        case OffsetFormat.Decimal:
+                            sb.Append(curByte.ToString() + ", ");
+                            break;
+                        default:
+                            sb.Append("$" + curByte.ToString("X2") + ", ");
+                            break;
+                    }
+                }
+                switch (InstructionNumberFormat)
+                {
+                    case OffsetFormat.Decimal:
+                        sb.Append(((byte)CoreFile.ReadByte(last)).ToString());
+                        break;
+                    default:
+                        sb.Append("$" + ((byte)CoreFile.ReadByte(last)).ToString("X2"));
+                        break;
+                }
+                sb.AppendLine();
+            }
+            return sb.ToString();
+        }
+
         #endregion Label Display
 
         #region Disassembly
 
         public string PrintASM(BinFile file, int baseOffset, int start, int length)
         {
-            StringBuilder output = new StringBuilder(String.Empty);
+            StringBuilder output = new StringBuilder();
+            GBInstruction isu = new GBInstruction();
             int current = start;
-            DataLabel dataSearch = new DataLabel(current);
-            FunctionLabel codeSearch = new FunctionLabel(current);
-            GBInstruction isu = new GBInstruction();
             while (current < start + length)
             {
-                byte currentInst = (byte)file.ReadByte(current);
-                if (lc.TryGetDataLabel(current, out dataSearch))
+                var labelsAt = from s in lc.FuncList
+                               where s.Offset == current
+                               orderby s.Name
+                               select s;
+                var dataAt = from s in lc.DataList
+                             where s.Offset == current
+                             select s;
+                foreach (var f in labelsAt)
+                {
+                    output.AppendLine(f.ToASMCommentString());
+                }
+                if (dataAt.Count() != 0)
                 {
                     if (HideDefinedData)
                     {
-                        output.AppendLine(String.Format("INCBIN \"{0}.bin\"", dataSearch.Name));
+                        output.AppendLine(String.Format("INCBIN \"{0}.bin\"", dataAt.First().Name));
                     }
                     else
                     {
-                        output.Append(ShowDataLabel(dataSearch));
+                        output.Append(ShowDataLabel(dataAt.First()));
                     }
-                    current += dataSearch.Length;
-                }
-                else if (lc.TryGetFuncLabel(current, out codeSearch))
-                {
-                    output.AppendLine(codeSearch.ToASMCommentString());
-                    output.AppendLine(GetInstruction(file, baseOffset, current, ref isu));
-                    current += isu.InstSize;
+                    current += dataAt.First().Length;
                 }
                 else
                 {
 
         private string GetInstruction(BinFile refFile, int OrgOffset, int BinaryOffset, ref GBInstruction isu)
         {
-            if (instructionCache.ContainsKey(OrgOffset + BinaryOffset))
+            int currentAddress = OrgOffset + BinaryOffset;
+            if (instructionCache.ContainsKey(currentAddress))
             {
-                isu = instructionCache[OrgOffset + BinaryOffset];
+                isu = instructionCache[currentAddress];
             }
             else
             {
                 bool success = false;
-                if (lc.isAddressMarkedAsData(OrgOffset + BinaryOffset))
+                if (lc.isAddressMarkedAsData(currentAddress))
                 {
                     success = GBASM.CreateDBInstruction(refFile.MainFile, OrgOffset, BinaryOffset, ref isu);
                 }
                 {
                     return "--Error--";
                 }
-                instructionCache.Add(isu.Bank + isu.Address, isu);
+                instructionCache.Add(Utility.GetRealAddress(isu.Bank, isu.Address), isu);
             }
             StringBuilder returned = new StringBuilder();
 
 
             if (PrintBitPattern)
             {
+                var bp = new string[] { "  ", "  ", "  ", "  " };
                 for (int i = 0; i < isu.InstSize; i++)
                 {
-                    returned.Append(refFile.ReadByte(BinaryOffset + i).ToString("X2"));
+                    bp[i] = refFile.ReadByte(BinaryOffset + i).ToString("X2");
                 }
-                switch (isu.InstSize)
-                {
-                    case 2:
-                        returned.Append("    ");
-                        break;
-
-                    case 1:
-                        returned.Append("      ");
-                        break;
-                    default:
-                        returned.Append("  ");
-                        break;
-                }
+                returned.Append(string.Join("",bp));
             }
 
             #endregion Check bit pattern printing
             #region Print instruction
 
             if (!(PrintBitPattern || PrintOffsets))
-                returned.Append("\t");
-            returned.Append(isu.InstType.ToString("G"));
+            {
+                returned.Append("    "); 
+            }
+            returned.Append(isu.InstType.ToString());
             string numArg = "";
             if (isu.ArgCount > 0)
             {
                 returned.Append(" ");
-                returned.Append(ArgumentToASMString(isu.Bank, isu.Address, isu.InstType, isu.Arg1));
+                numArg = ArgumentToASMString(isu.Bank, isu.Address, isu.InstType, isu.Arg1);
+                returned.Append(numArg);
             }
             if (isu.ArgCount == 2)
             {
                 returned.Append(",");
-                returned.Append(ArgumentToASMString(isu.Bank, isu.Address, isu.InstType, isu.Arg2));
+                numArg = ArgumentToASMString(isu.Bank, isu.Address, isu.InstType, isu.Arg2);
+                returned.Append(numArg);
             }
 
             #endregion Print instruction
 
             if (PrintComments)
             {
-                int tCount = returned.Length;
-                returned.Append("\t\t;");
+                returned.Append("  ;");
                 int x = refFile.ReadByte(BinaryOffset);
                 if (refFile.ReadByte(BinaryOffset) == 0xCB)
                 {
                     returned.AppendFormat(CBLongInst[x], numArg);
                 }
                 else
+                {
                     returned.AppendFormat(longInst[x], numArg);
+                }
             }
 
             #endregion Check comments

File gbread/Base/Label.cs

 
     public abstract class GenericLabel : IComparable<GenericLabel>
     {
+        protected static int counter = 0;
+
+        protected int _id;
+
+        public int ID { get { return _id; } protected set { _id = value; } }
+
         protected int _value;
 
         public int Value { get { return _value; } }
 
         public override int GetHashCode()
         {
-            return _value.GetHashCode();
+            return _id;
         }
 
         public int CompareTo(GenericLabel comp)
                 return _name.CompareTo(comp._name);
             }
             else
+            {
                 throw new ArgumentException("Object is not initialized.");
+            }
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (obj is GenericLabel)
+            {
+                return (obj as GenericLabel)._id == _id;
+            }
+            else
+            {
+                return false;
+            }
         }
     }
 
     {
         public int Offset { get { return _value; } set { _value = value; } }
 
-        public FunctionLabel(int newOffset, string labelName = "", string commentLines = "")
+        public FunctionLabel(int newOffset, string labelName = "", string nComment = "")
         {
+            _id = System.Threading.Interlocked.Increment(ref counter);
             _value = newOffset;
             _name = labelName.Equals(String.Empty) ? String.Format("F_{0:X6}", newOffset) : labelName;
-            if (!String.IsNullOrEmpty(commentLines))
+            if (!String.IsNullOrEmpty(nComment))
             {
-                _comment = commentLines;
+                _comment = nComment;
             }
         }
 
             }
             return returned;
         }
-
-        public override bool Equals(object obj)
-        {
-            if (obj is FunctionLabel)
-            {
-                return ((FunctionLabel)obj)._value == _value;
-            }
-            else
-                return false;
-        }
-
-        public override int GetHashCode()
-        {
-            return _value.GetHashCode();
-        }
     }
 
     public class DataLabel : GenericLabel
 
         public DataLabel(int newOffset, int newLength = 1, string labelName = "", int dataLen = 8, string cmt = "", DataSectionType dst = DataSectionType.Data, GBPalette pal = null)
         {
+            _id = System.Threading.Interlocked.Increment(ref counter);
             _value = newOffset;
             _length = newLength;
             _name = labelName == String.Empty ? String.Format("DS_{0:X6}", newOffset) : labelName;
                 _comment = cmt;
             }
             if (dataLen <= 0)
+            {
                 dataLen = 8;
+            }
             _dataLineLength = dataLen;
             dataSectType = dst;
             if (pal != null)
         {
             string returned = "";
             returned += _name + ":" + Environment.NewLine;
-            returned += String.Format("{0};Size: 0x{1:X} bytes", Environment.NewLine, _length);
+            returned += String.Format(";Size: 0x{0:X} bytes", _length);
             if (_comment != null)
             {
                 foreach (string commentLine in _comment.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries))
             }
             return returned;
         }
-
-        public override bool Equals(object obj)
-        {
-            if (obj is DataLabel)
-            {
-                return ((DataLabel)obj)._value == _value;
-            }
-            else
-                return false;
-        }
-
-        public override int GetHashCode()
-        {
-            return _value.GetHashCode();
-        }
     }
 
     public class VarLabel : GenericLabel
 
         public VarLabel(int a, string n = "", string cmt = "")
         {
+            _id = System.Threading.Interlocked.Increment(ref counter);
             _name = n.Equals(String.Empty) ? String.Format("V_{0:X4}", a) : n;
             _value = a;
             if (!String.IsNullOrEmpty(cmt))
             returned += Environment.NewLine;
             return returned;
         }
-
-        public override bool Equals(object obj)
-        {
-            if (obj is VarLabel)
-            {
-                return ((VarLabel)obj)._value == _value;
-            }
-            else
-                return false;
-        }
-
-        public override int GetHashCode()
-        {
-            return base.GetHashCode();
-        }
     }
 }

File gbread/Base/LabelContainer.cs

         private object varListLock = new object();
         private object commentListLock = new object();
         private object symbolListLock = new object();
+        private object dataAddrLock = new object();
+
 
         private List<FunctionLabel> _funcList = new List<FunctionLabel>();
         private List<DataLabel> _dataList = new List<DataLabel>();
             }
         }
 
+        public Dictionary<int, string> Comments
+        {
+            get
+            {
+                lock (symbolListLock)
+                {
+                    return _commentList;
+                }
+            }
+        }
+
+        public Dictionary<string, int> SymbolList
+        {
+            get
+            {
+                lock (symbolListLock)
+                {
+                    return _symbolList;
+                }
+            }
+        }
+
         #endregion Public Properties
 
         public LabelContainer()
                 {
                     _varList.Clear();
                 }
-                lock (commentListLock)
-                {
-                    _commentList.Clear();
-                }
                 _symbolList.Clear();
             }
+            lock (commentListLock)
+            {
+                _commentList.Clear();
+            }
             if (File.Exists("default.txt"))
             {
                 LoadLabelFile("default.txt");
 
         #region Adding, clearing, and removing labels
 
+        // TODO: Adjust behavior so that labels belonging to the same offset get printed.
+
         public bool TryGetFuncLabel(int current, out FunctionLabel label)
         {
             lock (funcListLock)
             {
-                var s = from item in _funcList where item.Value == current select item;
+                var s = from item in _funcList where item.Offset == current select item;
                 var success = s.Count() != 0;
                 label = success ? s.First() : new FunctionLabel(current);
                 return success;
         {
             lock (dataListLock)
             {
-                var s = from item in _dataList where item.Value == current select item;
+                var s = from item in _dataList where item.Offset == current select item;
                 var success = s.Count() != 0;
                 label = success ? s.First() : new DataLabel(current);
                 return success;
         {
             lock (varListLock)
             {
-                var s = from item in _varList where item.Value == current select item;
+                var s = from item in _varList where item.Variable == current select item;
                 var success = s.Count() != 0;
                 label = success ? s.First() : new VarLabel(current);
                 return success;
             }
         }
-
-        public bool isAddressMarkedAsData(int address)
-        {
-            lock (dataListLock)
-            {
-                return dataAddrs.Contains(address);
-            }
-        }
-
-        public int GetNextNonDataAddress(int address)
-        {
-            int offset = address;
-            lock (dataListLock)
-            {
-                while (dataAddrs.Contains(offset++)) { }
-            }
-            return offset;
-        }
-
-        public bool IsNameDefined(string name)
-        {
-            //Note: this function will not help if, in between calling this and
-            //AddLabel, another thread adds a label with the same name
-            //first.
-            lock (symbolListLock)
-            {
-                if (String.IsNullOrEmpty(name))
-                    return false;
-                return _symbolList.ContainsKey(name);
-            }
-        }
-
+        
         public void AddFuncLabel(FunctionLabel toBeAdded)
         {
             lock (symbolListLock)
             {
                 lock (funcListLock)
                 {
-                    if (_symbolList.ContainsKey(toBeAdded.Name) || _funcList.Contains(toBeAdded))
-                        return;
-                    _funcList.Add(toBeAdded);
-                    _symbolList.Add(toBeAdded.Name, toBeAdded.Offset);
+                    if (!_symbolList.ContainsKey(toBeAdded.Name))
+                    {
+                        _funcList.Add(toBeAdded);
+                        _symbolList.Add(toBeAdded.Name, toBeAdded.Offset); 
+                    }
                 }
             }
         }
             {
                 lock (dataListLock)
                 {
-                    if (_symbolList.ContainsKey(toBeAdded.Name) || _dataList.Contains(toBeAdded))
-                        return;
-                    _dataList.Add(toBeAdded);
-                    _symbolList.Add(toBeAdded.Name, toBeAdded.Value);
-                    RegisterDataAddresses(toBeAdded.Offset, toBeAdded.Length);
+                    if (!_symbolList.ContainsKey(toBeAdded.Name))
+                    {
+                        _dataList.Add(toBeAdded);
+                        _symbolList.Add(toBeAdded.Name, toBeAdded.Value);
+                        for (int i = toBeAdded.Offset; i < toBeAdded.Offset + toBeAdded.Length; i++)
+                        {
+                            dataAddrs.Add(i);
+                        } 
+                    }
                 }
             }
         }
             {
                 lock (varListLock)
                 {
-                    if (_symbolList.ContainsKey(toBeAdded.Name) || _varList.Contains(toBeAdded))
-                        return;
-                    _varList.Add(toBeAdded);
-                    _symbolList.Add(toBeAdded.Name, toBeAdded.Value);
+                    if (!_symbolList.ContainsKey(toBeAdded.Name))
+                    {
+                        _varList.Add(toBeAdded);
+                        _symbolList.Add(toBeAdded.Name, toBeAdded.Value);
+                    }
                 }
             }
         }
 
         public void AddComment(int offset, string comment)
         {
+            if (String.IsNullOrEmpty(comment))
+            {
+                return;
+            }
             lock (commentListLock)
             {
                 if (_commentList.ContainsKey(offset))
                 lock (dataListLock)
                 {
                     _dataList.Remove(toBeRemoved);
-                    DeregisterDataAddresses(toBeRemoved.Offset, toBeRemoved.Length);
                     _symbolList.Remove(toBeRemoved.Name);
+                    dataAddrs.RemoveWhere(x => x >= toBeRemoved.Offset && x < toBeRemoved.Offset + toBeRemoved.Length);
                 }
             }
         }
             }
         }
 
-        private void RegisterDataAddresses(int offset, int length)
+        public bool isAddressMarkedAsData(int address)
         {
-            for (int i = offset; i < offset + length; i++)
+            lock (dataListLock)
             {
-                dataAddrs.Add(i);
+                return dataAddrs.Contains(address);
             }
         }
 
-        private void DeregisterDataAddresses(int offset, int length)
+        public int GetNextNonDataAddress(int address)
         {
-            dataAddrs.RemoveWhere(x => x >= offset && x < offset + length);
+            lock (dataListLock)
+            {
+                int offset = address;
+                while (dataAddrs.Contains(offset++)) { }
+                return offset;
+            }
         }
 
         #endregion Adding, clearing, and removing labels
 
         #region Loading and Saving Label Files
 
+        // TODO: Make sure everything saves and loads properly.
+
         public void LoadLabelFile(string fileName)
         {
             using (TextReader tr = new StreamReader(fileName))
                     }
                     else
                     {
+                        List<Dictionary<string, string>> items = new List<Dictionary<string, string>>();
+                        int curItem = -1;
                         string currentLine;
                         while ((currentLine = tr.ReadLine()) != null)
                         {
-                            List<string> buf = new List<string>();
-                            while (tr.Peek() != '.')
-                            {
-                                string s = tr.ReadLine();
-                                if (s != "")
-                                {
-                                    buf.Add(tr.ReadLine());
-                                }
-                            }
-                            switch (currentLine.ToLower())
+                            switch (currentLine)
                             {
                                 case ".label":
                                     {
+                                        items.Add(new Dictionary<string, string>() { { "tag", "label" } });
+                                        curItem++;
+                                    }
+                                    break;
+                                case ".data":
+                                    {
+                                        items.Add(new Dictionary<string, string>() { { "tag", "data" } });
+                                        curItem++;
+                                    }
+                                    break;
+                                case ".var":
+                                    {
+                                        items.Add(new Dictionary<string, string>() { { "tag", "var" } });
+                                        curItem++;
+                                    }
+                                    break;
+                                case ".comment":
+                                    {
+                                        items.Add(new Dictionary<string, string>() { { "tag", "comment" } });
+                                        curItem++;
+                                    }
+                                    break;
+                                default:
+                                    if (curItem == -1)
+                                    {
+                                        break;
+                                    }
+                                    string[] opt = currentLine.Split(':');
+                                    if (opt.Length == 1)
+                                    {
+                                        if (items[curItem].ContainsKey("_c"))
+                                        {
+                                            items[curItem]["_c"] = currentLine;
+                                        }
+                                        else
+                                        {
+                                            items[curItem].Add("_c", currentLine);
+                                        }
+                                    }
+                                    else
+                                    {
+                                        if (items[curItem].ContainsKey(opt[0]))
+                                        {
+                                            items[curItem][opt[0]] = opt[1];
+                                        }
+                                        else
+                                        {
+                                            items[curItem].Add(opt[0], opt[1]);
+                                        }
+                                    }
+                                    break;
+                            }
+                        }
+                        foreach(Dictionary<string, string> currentItem in items)
+                        {
+                            switch (currentItem["tag"])
+                            {
+                                case "label":
+                                    {
                                         int offset = 0;
                                         string name = "";
-                                        List<string> cmtBuf = new List<string>();
+                                        string comment = "";
                                         bool offsetGood = false;
-                                        foreach (string x in buf)
+                                        foreach (KeyValuePair<string, string> kvp in currentItem)
                                         {
-                                            string[] opt = x.Split(':');
-                                            if (opt.Length == 1)
-                                            {
-                                                cmtBuf.Add(x);
-                                            }
-                                            switch (opt[0].ToLower())
+                                            switch(kvp.Key)
                                             {
                                                 case "_o":
                                                     {
-                                                        offsetGood = InputValidation.TryParseOffsetString(opt[1], out offset);
+                                                        offsetGood = InputValidation.TryParseOffsetString(kvp.Value, out offset);
                                                     }
 
                                                     break;
 
                                                 case "_n":
                                                     {
-                                                        name = opt[1];
+                                                        name = kvp.Value;
                                                     }
 
                                                     break;
 
                                                 case "_c":
                                                     {
-                                                        cmtBuf.Add(x.Substring(0, 3));
-                                                    }
-
-                                                    break;
-                                                default:
-                                                    {
-                                                        cmtBuf.Add(x);
+                                                        comment = kvp.Value;
                                                     }
 
                                                     break;
                                         }
                                         if (offsetGood)
                                         {
-                                            FunctionLabel fl = new FunctionLabel(offset, name, string.Join(Environment.NewLine, cmtBuf.ToArray()));
+                                            FunctionLabel fl = new FunctionLabel(offset, name, comment);
                                             AddFuncLabel(fl);
                                         }
                                         else
                                         {
-                                            tw.WriteLine("Unrecognized section:");
-                                            foreach (string x in buf)
-                                            {
-                                                tw.WriteLine(x);
-                                            }
+                                            tw.WriteLine("Label #" + items.IndexOf(currentItem) + " was unrecognized.");
                                         }
                                     }
+
                                     break;
 
-                                case ".data":
+                                case "data":
                                     {
                                         int offset = -1;
                                         int length = -1;
                                         int dataDiv = 0;
-                                        string name = String.Empty;
+                                        string name = "";
+                                        string comment = "";
                                         DataSectionType dst = DataSectionType.Data;
-                                        List<string> cmtBuf = new List<string>();
                                         GBPalette gbp = new GBPalette();
                                         bool offsetGood = false;
                                         bool lengthGood = false;
                                         bool dataDivGood = false;
-                                        foreach (string x in buf)
+                                        foreach (KeyValuePair<string, string> kvp in currentItem)
                                         {
-                                            string[] opt = x.Split(':');
-                                            if (opt.Length == 1)
-                                            {
-                                                cmtBuf.Add(x);
-                                            }
-                                            switch (opt[0].ToLower())
+                                            switch (kvp.Key)
                                             {
                                                 case "_o":
                                                     {
-                                                        offsetGood = InputValidation.TryParseOffsetString(opt[1], out offset);
+                                                        offsetGood = InputValidation.TryParseOffsetString(kvp.Value, out offset);
                                                     }
 
                                                     break;
 
                                                 case "_l":
                                                     {
-                                                        lengthGood = InputValidation.TryParseOffsetString(opt[1], out length);
-                                                    }
-                                                    break;
-
-                                                case "_d":
-                                                    {
-                                                        dataDivGood = InputValidation.TryParseOffsetString(opt[1], out dataDiv);
+                                                        lengthGood = InputValidation.TryParseOffsetString(kvp.Value, out length);
                                                     }
                                                     break;
 
                                                 case "_n":
-                                                    if (RegularValidation.IsWord(opt[1]))
                                                     {
-                                                        name = opt[1];
+                                                        if (RegularValidation.IsWord(kvp.Value))
+                                                        {
+                                                            name = kvp.Value;
+                                                        }
                                                     }
 
                                                     break;
-
-                                                case "_c":
+                                                case "_d":
                                                     {
-                                                        cmtBuf.Add(x.Substring(0, 3));
+                                                        dataDivGood = InputValidation.TryParseOffsetString(kvp.Value, out dataDiv);
                                                     }
-
                                                     break;
 
                                                 case "_p1":
                                                     {
                                                         dst = DataSectionType.Image;
-                                                        InputValidation.TryParseOffsetString(opt[1], out gbp.Col_1);
+                                                        InputValidation.TryParseOffsetString(kvp.Value, out gbp.Col_1);
                                                     }
 
                                                     break;
                                                 case "_p2":
                                                     {
                                                         dst = DataSectionType.Image;
-                                                        InputValidation.TryParseOffsetString(opt[1], out gbp.Col_2);
+                                                        InputValidation.TryParseOffsetString(kvp.Value, out gbp.Col_2);
                                                     }
 
                                                     break;
                                                 case "_p3":
                                                     {
                                                         dst = DataSectionType.Image;
-                                                        InputValidation.TryParseOffsetString(opt[1], out gbp.Col_3);
+                                                        InputValidation.TryParseOffsetString(kvp.Value, out gbp.Col_3);
                                                     }
 
                                                     break;
                                                 case "_p4":
                                                     {
                                                         dst = DataSectionType.Image;
-                                                        InputValidation.TryParseOffsetString(opt[1], out gbp.Col_4);
+                                                        InputValidation.TryParseOffsetString(kvp.Value, out gbp.Col_4);
                                                     }
 
                                                     break;
-                                                default:
+                                                case "_c":
                                                     {
-                                                        cmtBuf.Add(x);
+                                                        comment = kvp.Value;
                                                     }
+
                                                     break;
                                             }
                                         }
+
                                         if (offsetGood && lengthGood)
                                         {
-                                            DataLabel ds = new DataLabel(offset, length, name, dataDiv, string.Join(Environment.NewLine, cmtBuf.ToArray()), dst, gbp);
+                                            DataLabel ds = new DataLabel(offset, length, name, dataDiv, comment, dst, gbp);
                                             AddDataLabel(ds);
                                         }
                                         else
                                         {
-                                            tw.WriteLine("Unrecognized section:");
-                                            foreach (string x in buf)
-                                            {
-                                                tw.WriteLine(x);
-                                            }
+                                            tw.WriteLine("Label #" + items.IndexOf(currentItem) + " was unrecognized.");
                                         }
                                     }
 
                                     break;
 
-                                case ".var":
+                                case "var":
                                     {
                                         int variable = -1;
-                                        string name = String.Empty;
-                                        List<string> cmtBuf = new List<string>();
+                                        string name = "";
+                                        string comment = "";
                                         bool variableGood = false;
-                                        foreach (string x in buf)
+
+                                        foreach (KeyValuePair<string, string> kvp in currentItem)
                                         {
-                                            string code = x.Substring(0, 3);
-                                            string val = x.Substring(3, x.Length - 3);
-                                            switch (code[1])
+                                            switch (kvp.Key)
                                             {
-                                                case 'v':
-                                                    variableGood = InputValidation.TryParseOffsetString(val, out variable);
+                                                case "_v":
+                                                    {
+                                                        variableGood = InputValidation.TryParseOffsetString(kvp.Value, out variable);
+                                                    }
+
                                                     break;
 
-                                                case 'n':
-                                                    if (RegularValidation.IsWord(val))
-                                                        name = val;
+                                                case "_n":
+                                                    {
+                                                        if (RegularValidation.IsWord(kvp.Value))
+                                                        {
+                                                            name = kvp.Value;
+                                                        }
+                                                    }
+
                                                     break;
 
-                                                case 'c':
-                                                    cmtBuf.Add(val);
-                                                    break;
-                                                default:
+                                                case "_c":
+                                                    {
+                                                        comment = kvp.Value;
+                                                    }
+
                                                     break;
                                             }
                                         }
+
                                         if (variableGood)
                                         {
-                                            VarLabel vl = new VarLabel(variable, name, string.Join(Environment.NewLine, cmtBuf.ToArray()));
+                                            VarLabel vl = new VarLabel(variable, name, comment);
                                             AddVarLabel(vl);
                                         }
                                         else
                                         {
-                                            tw.WriteLine("Unrecognized section:");
-                                            foreach (string x in buf)
-                                            {
-                                                tw.WriteLine(x);
-                                            }
+                                            tw.WriteLine("Label #" + items.IndexOf(currentItem) + " was unrecognized.");
                                         }
                                     }
 
                                     break;
 
-                                case ".comment":
+                                case "comment":
                                     {
                                         int offset = 0;
                                         string name = String.Empty;
-                                        List<string> cmtBuf = new List<string>();
+                                        string comment = "";
                                         bool offsetGood = false;
-                                        foreach (string x in buf)
+
+                                        foreach (KeyValuePair<string, string> kvp in currentItem)
                                         {
-                                            string code = x.Substring(0, 3).ToLower();
-                                            string val = x.Substring(3, x.Length - 3);
-                                            switch (code)
+                                            switch (kvp.Key)
                                             {
-                                                case "_o:":
-                                                    offsetGood = InputValidation.TryParseOffsetString(val, out offset);
+                                                case "_o":
+                                                    {
+                                                        offsetGood = InputValidation.TryParseOffsetString(kvp.Value, out offset);
+                                                    }
+
                                                     break;
-                                                default:
-                                                    cmtBuf.Add(val);
+
+                                                case "_c":
+                                                    {
+                                                        comment = kvp.Value;
+                                                    }
+
                                                     break;
                                             }
                                         }
                                         if (offsetGood)
                                         {
-                                            AddComment(offset, string.Join(Environment.NewLine, cmtBuf.ToArray()));
+                                            AddComment(offset, comment);
                                         }
                                         else
                                         {
-                                            tw.WriteLine("Unrecognized section:");
-                                            foreach (string x in buf)
-                                            {
-                                                tw.WriteLine(x);
-                                            }
+                                            tw.WriteLine("Label #" + items.IndexOf(currentItem) + " was unrecognized.");
                                         }
                                     }
                                     break;
                                 default:
-                                    tw.WriteLine("Unrecognized section heading: " + currentLine);
+
                                     break;
                             }
                         }
             using (TextWriter functions = new StreamWriter(fileName, false, Encoding.UTF8))
             {
                 functions.WriteLine("gbr");
-                if (FuncList.Count != 0)
+                foreach (FunctionLabel s in FuncList)
                 {
-                    functions.WriteLine(FunctionListToSaveFileFormat());
+                    functions.WriteLine(".label");
+                    functions.WriteLine("_n:" + s.Name);
+                    functions.WriteLine("_o:" + s.Offset);
                 }
-                if (DataList.Count != 0)
+                foreach (DataLabel s in DataList)
                 {
-                    functions.WriteLine(DataListToSaveFileFormat());
+                    functions.WriteLine(".data");
+                    functions.WriteLine("_n:" + s.Name);
+                    functions.WriteLine("_o:" + s.Offset.ToString("X"));
+                    functions.WriteLine("_l:" + s.Length.ToString("X"));
+                    functions.WriteLine("_t:" + s.DSectionType);
+                    functions.WriteLine("_d:" + s.DataLineLength.ToString("X"));
+                    if (s.DSectionType == DataSectionType.Image)
+                    {
+                        functions.WriteLine("_p1:" + s.Palette.Col_1.ToString("X"));
+                        functions.WriteLine("_p2:" + s.Palette.Col_2.ToString("X"));
+                        functions.WriteLine("_p3:" + s.Palette.Col_3.ToString("X"));
+                        functions.WriteLine("_p4:" + s.Palette.Col_4.ToString("X"));
+                    }
                 }
-                if (VarList.Count != 0)
+                foreach (VarLabel s in VarList)
                 {
-                    functions.WriteLine(VarListToSaveFileFormat());
+                    functions.WriteLine(".var");
+                    functions.WriteLine("_n:" + s.Name);
+                    functions.WriteLine("_v:" + s.Variable.ToString("X"));
                 }
-                if (_commentList.Count != 0)
+                foreach (KeyValuePair<int, string> kvp in _commentList)
                 {
-                    functions.WriteLine(CommentListToSaveFileFormat());
+                    functions.WriteLine(".comment");
+                    functions.WriteLine("_o:" + kvp.Key.ToString("X"));
+                    functions.WriteLine("_c:" + kvp.Value);
                 }
                 functions.Close();
             }
         }
 
-        // TODO: Make sure everything saves and loads properly.
-
-        private string FunctionListToSaveFileFormat()
-        {
-            StringBuilder sb = new StringBuilder(String.Empty);
-            foreach (FunctionLabel s in FuncList)
-            {
-                sb.AppendLine(".label");
-                sb.AppendLine("_n:" + s.Name);
-                sb.AppendLine("_o:" + s.Offset);
-            }
-            return sb.ToString();
-        }
-
-        private string DataListToSaveFileFormat()
-        {
-            StringBuilder sb = new StringBuilder(String.Empty);
-            foreach (DataLabel s in DataList)
-            {
-                sb.AppendLine(".data");
-                sb.AppendLine("_n:" + s.Name);
-                sb.AppendLine("_o:" + s.Offset.ToString("X"));
-                sb.AppendLine("_l:" + s.Length.ToString("X"));
-                sb.AppendLine("_t:" + s.DSectionType);
-                sb.AppendLine("_d:" + s.DataLineLength.ToString("X"));
-                if (s.DSectionType == DataSectionType.Image)
-                {
-                    sb.AppendLine("_p1:" + s.Palette.Col_1.ToString("X"));
-                    sb.AppendLine("_p2:" + s.Palette.Col_2.ToString("X"));
-                    sb.AppendLine("_p3:" + s.Palette.Col_3.ToString("X"));
-                    sb.AppendLine("_p4:" + s.Palette.Col_4.ToString("X"));
-                }
-            }
-            return sb.ToString();
-        }
-
-        private string VarListToSaveFileFormat()
-        {
-            StringBuilder sb = new StringBuilder(String.Empty);
-            foreach (VarLabel s in VarList)
-            {
-                sb.AppendLine(".var");
-                sb.AppendLine("_n:" + s.Name);
-                sb.AppendLine("_v:" + s.Variable.ToString("X"));
-            }
-            return sb.ToString();
-        }
-
-        private string CommentListToSaveFileFormat()
-        {
-            if (_commentList.Count == 0)
-            {
-                return "";
-            }
-            StringBuilder ret = new StringBuilder();
-            foreach (KeyValuePair<int, string> kvp in _commentList)
-            {
-                ret.AppendLine(".comment");
-                ret.AppendLine("_o:" + kvp.Key.ToString("X"));
-                ret.AppendLine("_c:" + kvp.Value);
-            }
-            return ret.ToString();
-        }
-
         #endregion Loading and Saving Label Files
     }
 }

File gbread/Forms/AddCommentForm.Designer.cs

+namespace GBRead.Forms
+{
+    partial class AddCommentForm
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.label1 = new System.Windows.Forms.Label();
+            this.offsetBox = new System.Windows.Forms.TextBox();
+            this.textBox2 = new System.Windows.Forms.TextBox();
+            this.label2 = new System.Windows.Forms.Label();
+            this.button1 = new System.Windows.Forms.Button();
+            this.cancelButton = new System.Windows.Forms.Button();
+            this.fetchCommentButton = new System.Windows.Forms.Button();
+            this.SuspendLayout();
+            // 
+            // label1
+            // 
+            this.label1.AutoSize = true;
+            this.label1.Location = new System.Drawing.Point(12, 9);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(38, 13);
+            this.label1.TabIndex = 0;
+            this.label1.Text = "Offset:";
+            // 
+            // offsetBox
+            // 
+            this.offsetBox.Location = new System.Drawing.Point(53, 6);
+            this.offsetBox.Name = "offsetBox";
+            this.offsetBox.Size = new System.Drawing.Size(98, 20);
+            this.offsetBox.TabIndex = 1;
+            // 
+            // textBox2
+            // 
+            this.textBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.textBox2.Location = new System.Drawing.Point(15, 45);
+            this.textBox2.Multiline = true;
+            this.textBox2.Name = "textBox2";
+            this.textBox2.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+            this.textBox2.Size = new System.Drawing.Size(354, 243);
+            this.textBox2.TabIndex = 2;
+            // 
+            // label2
+            // 
+            this.label2.AutoSize = true;
+            this.label2.Location = new System.Drawing.Point(12, 29);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(54, 13);
+            this.label2.TabIndex = 3;
+            this.label2.Text = "Comment:";
+            // 
+            // button1
+            // 
+            this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+            this.button1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.button1.Location = new System.Drawing.Point(15, 294);
+            this.button1.Name = "button1";
+            this.button1.Size = new System.Drawing.Size(75, 23);
+            this.button1.TabIndex = 7;
+            this.button1.Text = "OK";
+            this.button1.UseVisualStyleBackColor = true;
+            this.button1.Click += new System.EventHandler(this.okButton_Click);
+            // 
+            // cancelButton
+            // 
+            this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+            this.cancelButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.cancelButton.Location = new System.Drawing.Point(288, 294);
+            this.cancelButton.Name = "cancelButton";
+            this.cancelButton.Size = new System.Drawing.Size(81, 23);
+            this.cancelButton.TabIndex = 8;
+            this.cancelButton.Text = "Cancel";
+            this.cancelButton.UseVisualStyleBackColor = true;
+            // 
+            // fetchCommentButton
+            // 
+            this.fetchCommentButton.AccessibleRole = System.Windows.Forms.AccessibleRole.None;
+            this.fetchCommentButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+            this.fetchCommentButton.Location = new System.Drawing.Point(157, 4);
+            this.fetchCommentButton.Name = "fetchCommentButton";
+            this.fetchCommentButton.Size = new System.Drawing.Size(135, 23);
+            this.fetchCommentButton.TabIndex = 9;
+            this.fetchCommentButton.Text = "Fetch Comment At Offset";
+            this.fetchCommentButton.UseVisualStyleBackColor = true;
+            this.fetchCommentButton.Click += new System.EventHandler(this.fetchCommentButton_Click);
+            // 
+            // AddCommentForm
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(384, 332);
+            this.Controls.Add(this.fetchCommentButton);
+            this.Controls.Add(this.cancelButton);
+            this.Controls.Add(this.button1);
+            this.Controls.Add(this.label2);
+            this.Controls.Add(this.textBox2);
+            this.Controls.Add(this.offsetBox);
+            this.Controls.Add(this.label1);
+            this.Name = "AddCommentForm";
+            this.Text = "AddCommentForm";
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.TextBox offsetBox;
+        private System.Windows.Forms.TextBox textBox2;
+        private System.Windows.Forms.Label label2;
+        private System.Windows.Forms.Button button1;
+        private System.Windows.Forms.Button cancelButton;
+        private System.Windows.Forms.Button fetchCommentButton;
+
+    }
+}

File gbread/Forms/AddCommentForm.cs

+namespace GBRead.Forms
+{
+    using System;
+    using System.Windows.Forms;
+    using GBRead.Base;
+
+    public partial class AddCommentForm : Form
+    {
+        private LabelContainer labelContainer;
+
+        public AddCommentForm(LabelContainer lblContainer, LabelEditMode editMode, int offset = -1)
+        {
+            InitializeComponent();
+            labelContainer = lblContainer;
+            if (editMode == LabelEditMode.Edit)
+            {
+                if (labelContainer.Comments.ContainsKey(offset))
+                {
+                    textBox2.Text = labelContainer.Comments[offset];
+                }
+                Text = "Edit Comment";
+            }
+        }
+
+        private void okButton_Click(object sender, EventArgs e)
+        {
+            int off = -1;
+            if (!InputValidation.TryParseOffsetString(offsetBox.Text, out off))
+            {
+                Error.ShowErrorMessage(ErrorMessage.OFFSET_IS_INVALID);
+            }
+            labelContainer.AddComment(off, textBox2.Text);
+            this.DialogResult = System.Windows.Forms.DialogResult.OK;
+        }
+
+        private void fetchCommentButton_Click(object sender, EventArgs e)
+        {
+            int off = -1;
+            if (InputValidation.TryParseOffsetString(offsetBox.Text, out off) && labelContainer.Comments.ContainsKey(off))
+            {
+                textBox2.Text = labelContainer.Comments[off];
+            }
+        }
+    }
+}

File gbread/Forms/AddCommentForm.resx

+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>