1 using Mesen.GUI.Config;
2 using System;
3 using System.Collections.Generic;
4 using System.IO;
5 using System.Linq;
6 using System.Text;
7 using System.Text.RegularExpressions;
8 using System.Threading.Tasks;
9 
10 namespace Mesen.GUI.Debugger
11 {
12 	public class CodeLabel
13 	{
14 		public UInt32 Address;
15 		public AddressType AddressType;
16 		public string Label;
17 		public string Comment;
18 		public CodeLabelFlags Flags;
19 		public UInt32 Length = 1;
20 
ToString()21 		public override string ToString()
22 		{
23 			StringBuilder sb = new StringBuilder();
24 			switch(AddressType) {
25 				case AddressType.InternalRam: sb.Append("R:"); break;
26 				case AddressType.PrgRom: sb.Append("P:"); break;
27 				case AddressType.WorkRam: sb.Append("W:"); break;
28 				case AddressType.SaveRam: sb.Append("S:"); break;
29 				case AddressType.Register: sb.Append("G:"); break;
30 			}
31 			sb.Append(Address.ToString("X4"));
32 			if(Length > 1) {
33 				sb.Append("-" + (Address+Length-1).ToString("X4"));
34 			}
35 			sb.Append(":");
36 			sb.Append(Label);
37 			if(!string.IsNullOrWhiteSpace(Comment)) {
38 				sb.Append(":");
39 				sb.Append(Comment.Replace(Environment.NewLine, "\\n").Replace("\n", "\\n").Replace("\r", "\\n"));
40 			}
41 			return sb.ToString();
42 		}
43 
GetRelativeAddress()44 		public int GetRelativeAddress()
45 		{
46 			return InteropEmu.DebugGetRelativeAddress(this.Address, this.AddressType);
47 		}
48 
GetValue()49 		public byte GetValue()
50 		{
51 			return InteropEmu.DebugGetMemoryValue(AddressType.ToMemoryType(), Address);
52 		}
53 	}
54 
55 	public class LabelManager
56 	{
57 		public static Regex LabelRegex { get; } = new Regex("^[@_a-zA-Z]+[@_a-zA-Z0-9]*$", RegexOptions.Compiled);
58 
59 		private static Dictionary<UInt32, CodeLabel> _labelsByKey = new Dictionary<UInt32, CodeLabel>();
60 		private static HashSet<CodeLabel> _labels = new HashSet<CodeLabel>();
61 		private static Dictionary<string, CodeLabel> _reverseLookup = new Dictionary<string, CodeLabel>();
62 
63 		public static event EventHandler OnLabelUpdated;
64 
ResetLabels()65 		public static void ResetLabels()
66 		{
67 			InteropEmu.DebugDeleteLabels();
68 			_labels.Clear();
69 			_labelsByKey.Clear();
70 			_reverseLookup.Clear();
71 		}
72 
GetLabel(UInt32 address, AddressType type)73 		public static CodeLabel GetLabel(UInt32 address, AddressType type)
74 		{
75 			CodeLabel label;
76 			_labelsByKey.TryGetValue(GetKey(address, type), out label);
77 			return label;
78 		}
79 
GetLabel(UInt16 relativeAddress)80 		public static CodeLabel GetLabel(UInt16 relativeAddress)
81 		{
82 			AddressTypeInfo info = new AddressTypeInfo();
83 			InteropEmu.DebugGetAbsoluteAddressAndType((UInt32)relativeAddress, info);
84 			if(info.Address >= 0) {
85 				return GetLabel((UInt32)info.Address, info.Type);
86 			}
87 			return null;
88 		}
89 
GetLabel(string label)90 		public static CodeLabel GetLabel(string label)
91 		{
92 			return _reverseLookup.ContainsKey(label) ? _reverseLookup[label] : null;
93 		}
94 
SetLabels(IEnumerable<CodeLabel> labels, bool raiseEvents = true)95 		public static void SetLabels(IEnumerable<CodeLabel> labels, bool raiseEvents = true)
96 		{
97 			foreach(CodeLabel label in labels) {
98 				SetLabel(label.Address, label.AddressType, label.Label, label.Comment, false, label.Flags, label.Length);
99 			}
100 			if(raiseEvents) {
101 				OnLabelUpdated?.Invoke(null, null);
102 			}
103 		}
104 
GetLabels()105 		public static List<CodeLabel> GetLabels()
106 		{
107 			return _labels.ToList<CodeLabel>();
108 		}
109 
GetKey(UInt32 address, AddressType addressType)110 		private static UInt32 GetKey(UInt32 address, AddressType addressType)
111 		{
112 			switch(addressType) {
113 				case AddressType.InternalRam: return address | 0x80000000;
114 				case AddressType.PrgRom: return address | 0xE0000000;
115 				case AddressType.WorkRam: return address | 0xC0000000;
116 				case AddressType.SaveRam: return address | 0xA0000000;
117 				case AddressType.Register: return address | 0x60000000;
118 			}
119 			throw new Exception("Invalid type");
120 		}
121 
SetLabel(UInt32 address, AddressType type, string label, string comment, bool raiseEvent = true, CodeLabelFlags flags = CodeLabelFlags.None, UInt32 labelLength = 1)122 		public static bool SetLabel(UInt32 address, AddressType type, string label, string comment, bool raiseEvent = true, CodeLabelFlags flags = CodeLabelFlags.None, UInt32 labelLength = 1)
123 		{
124 			if(_reverseLookup.ContainsKey(label)) {
125 				//Another identical label exists, we need to remove it
126 				CodeLabel existingLabel = _reverseLookup[label];
127 				DeleteLabel(existingLabel, false);
128 			}
129 
130 			CodeLabel newLabel = new CodeLabel() { Address = address, AddressType = type, Label = label, Comment = comment, Flags = flags, Length = labelLength };
131 			for(UInt32 i = address; i < address + labelLength; i++) {
132 				UInt32 key = GetKey(i, type);
133 				CodeLabel existingLabel;
134 				if(_labelsByKey.TryGetValue(key, out existingLabel)) {
135 					_reverseLookup.Remove(existingLabel.Label);
136 				}
137 
138 				_labelsByKey[key] = newLabel;
139 
140 				if(labelLength == 1) {
141 					InteropEmu.DebugSetLabel(i, type, label, comment.Replace(Environment.NewLine, "\n"));
142 				} else {
143 					InteropEmu.DebugSetLabel(i, type, label + "+" + (i - address).ToString(), comment.Replace(Environment.NewLine, "\n"));
144 
145 					//Only set the comment on the first byte of multi-byte comments
146 					comment = "";
147 				}
148 			}
149 
150 			_labels.Add(newLabel);
151 			if(label.Length > 0) {
152 				_reverseLookup[label] = newLabel;
153 			}
154 
155 			if(raiseEvent) {
156 				OnLabelUpdated?.Invoke(null, null);
157 			}
158 
159 			return true;
160 		}
161 
DeleteLabel(CodeLabel label, bool raiseEvent)162 		public static void DeleteLabel(CodeLabel label, bool raiseEvent)
163 		{
164 			bool needEvent = false;
165 
166 			_labels.Remove(label);
167 			for(UInt32 i = label.Address; i < label.Address + label.Length; i++) {
168 				UInt32 key = GetKey(i, label.AddressType);
169 				if(_labelsByKey.ContainsKey(key)) {
170 					_reverseLookup.Remove(_labelsByKey[key].Label);
171 				}
172 
173 				if(_labelsByKey.Remove(key)) {
174 					InteropEmu.DebugSetLabel(i, label.AddressType, string.Empty, string.Empty);
175 					if(raiseEvent) {
176 						needEvent = true;
177 					}
178 				}
179 			}
180 
181 			if(needEvent) {
182 				OnLabelUpdated?.Invoke(null, null);
183 			}
184 		}
185 
CreateAutomaticJumpLabels()186 		public static void CreateAutomaticJumpLabels()
187 		{
188 			byte[] cdlData = InteropEmu.DebugGetPrgCdlData();
189 			List<CodeLabel> labelsToAdd = new List<CodeLabel>();
190 			for(int i = 0; i < cdlData.Length; i++) {
191 				if((cdlData[i] & (byte)CdlPrgFlags.JumpTarget) != 0 && LabelManager.GetLabel((uint)i, AddressType.PrgRom) == null) {
192 					labelsToAdd.Add(new CodeLabel() { Flags = CodeLabelFlags.AutoJumpLabel, Address = (uint)i, AddressType = AddressType.PrgRom, Label = "L" + i.ToString("X4"), Comment = "" });
193 				}
194 			}
195 			if(labelsToAdd.Count > 0) {
196 				LabelManager.SetLabels(labelsToAdd, true);
197 			}
198 		}
199 
RefreshLabels()200 		public static void RefreshLabels()
201 		{
202 			InteropEmu.DebugDeleteLabels();
203 			LabelManager.SetLabels(GetLabels(), true);
204 		}
205 
206 		private const int FdsMapperID = 65535;
207 		private const int NsfMapperID = 65534;
208 
SetDefaultLabels(int mapperId)209 		public static void SetDefaultLabels(int mapperId)
210 		{
211 			bool disableBuiltInWorkspace = false;
212 			bool disableBuiltInMapperWorkspace = false;
213 			string prefix = "DefaultLabels.";
214 			string defaultWorkspaceMlbFile = Path.Combine(ConfigManager.DebuggerFolder, prefix + "Global.mlb");
215 			if(File.Exists(defaultWorkspaceMlbFile)) {
216 				MesenLabelFile.Import(defaultWorkspaceMlbFile, true);
217 				disableBuiltInWorkspace = true;
218 			}
219 
220 			string mapperName = mapperId.ToString();
221 			if(mapperId == FdsMapperID) {
222 				mapperName = "FDS";
223 			} else if(mapperId == NsfMapperID) {
224 				mapperName = "NSF";
225 			}
226 
227 			string defaultWorkspaceMapperMlbFile = Path.Combine(ConfigManager.DebuggerFolder, prefix + mapperName + ".mlb");
228 			if(File.Exists(defaultWorkspaceMapperMlbFile)) {
229 				MesenLabelFile.Import(defaultWorkspaceMapperMlbFile, true);
230 				disableBuiltInMapperWorkspace = true;
231 			}
232 
233 			if(!disableBuiltInWorkspace) {
234 				LabelManager.SetLabel(0x2000, AddressType.Register, "PpuControl_2000", $"7  bit  0{Environment.NewLine}---- ----{Environment.NewLine}VPHB SINN{Environment.NewLine}|||| ||||{Environment.NewLine}|||| ||++- Base nametable address{Environment.NewLine}|||| ||    (0 = $2000; 1 = $2400; 2 = $2800; 3 = $2C00){Environment.NewLine}|||| |+--- VRAM address increment per CPU read/write of PPUDATA{Environment.NewLine}|||| |     (0: add 1, going across; 1: add 32, going down){Environment.NewLine}|||| +---- Sprite pattern table address for 8x8 sprites{Environment.NewLine}||||       (0: $0000; 1: $1000; ignored in 8x16 mode){Environment.NewLine}|||+------ Background pattern table address (0: $0000; 1: $1000){Environment.NewLine}||+------- Sprite size (0: 8x8; 1: 8x16){Environment.NewLine}|+-------- PPU master/slave select{Environment.NewLine}|          (0: read backdrop from EXT pins; 1: output color on EXT pins){Environment.NewLine}+--------- Generate an NMI at the start of the{Environment.NewLine}           vertical blanking interval (0: off; 1: on)", false);
235 				LabelManager.SetLabel(0x2001, AddressType.Register, "PpuMask_2001", $"7  bit  0{Environment.NewLine}---- ----{Environment.NewLine}BGRs bMmG{Environment.NewLine}|||| ||||{Environment.NewLine}|||| |||+- Display type: (0: color, 1: grayscale){Environment.NewLine}|||| ||+-- 1: Show background in leftmost 8 pixels of screen, 0: Hide{Environment.NewLine}|||| |+--- 1: Show sprites in leftmost 8 pixels of screen, 0: Hide{Environment.NewLine}|||| +---- 1: Show background{Environment.NewLine}|||+------ 1: Show sprites{Environment.NewLine}||+------- Emphasize red{Environment.NewLine}|+-------- Emphasize green{Environment.NewLine}+--------- Emphasize blue", false);
236 				LabelManager.SetLabel(0x2002, AddressType.Register, "PpuStatus_2002", $"7  bit  0{Environment.NewLine}---- ----{Environment.NewLine}VSO. ....{Environment.NewLine}|||| ||||{Environment.NewLine}|||+-++++- Least significant bits previously written into a PPU register{Environment.NewLine}|||        (due to register not being updated for this address){Environment.NewLine}||+------- Sprite overflow. The intent was for this flag to be set{Environment.NewLine}||         whenever more than eight sprites appear on a scanline, but a{Environment.NewLine}||         hardware bug causes the actual behavior to be more complicated{Environment.NewLine}||         and generate false positives as well as false negatives; see{Environment.NewLine}||         PPU sprite evaluation. This flag is set during sprite{Environment.NewLine}||         evaluation and cleared at dot 1 (the second dot) of the{Environment.NewLine}||         pre-render line.{Environment.NewLine}|+-------- Sprite 0 Hit.  Set when a nonzero pixel of sprite 0 overlaps{Environment.NewLine}|          a nonzero background pixel; cleared at dot 1 of the pre-render{Environment.NewLine}|          line.  Used for raster timing.{Environment.NewLine}+--------- Vertical blank has started (0: not in vblank; 1: in vblank).{Environment.NewLine}           Set at dot 1 of line 241 (the line *after* the post-render{Environment.NewLine}           line, false); cleared after reading $2002 and at dot 1 of the{Environment.NewLine}           pre-render line.", false);
237 				LabelManager.SetLabel(0x2003, AddressType.Register, "OamAddr_2003", "Set OAM address - Write only", false);
238 				LabelManager.SetLabel(0x2004, AddressType.Register, "OamData_2004", "Read/Write OAM data", false);
239 				LabelManager.SetLabel(0x2005, AddressType.Register, "PpuScroll_2005", "Set PPU scroll, write twice - Write only", false);
240 				LabelManager.SetLabel(0x2006, AddressType.Register, "PpuAddr_2006", "Set PPU address, write twice - Write only", false);
241 				LabelManager.SetLabel(0x2007, AddressType.Register, "PpuData_2007", "Read/Write VRAM", false);
242 
243 				LabelManager.SetLabel(0x4000, AddressType.Register, "Sq0Duty_4000", $"DDLC VVVV{Environment.NewLine}Duty (D), envelope loop / length counter halt (L), constant volume (C), volume/envelope (V)", false);
244 				LabelManager.SetLabel(0x4001, AddressType.Register, "Sq0Sweep_4001", $"EPPP NSSS{Environment.NewLine}Sweep unit: enabled (E), period (P), negate (N), shift (S)", false);
245 				LabelManager.SetLabel(0x4002, AddressType.Register, "Sq0Timer_4002", $"TTTT TTTT{Environment.NewLine}Timer low (T)", false);
246 				LabelManager.SetLabel(0x4003, AddressType.Register, "Sq0Length_4003", $"LLLL LTTT{Environment.NewLine}Length counter load (L), timer high (T)", false);
247 
248 				LabelManager.SetLabel(0x4004, AddressType.Register, "Sq1Duty_4004", $"DDLC VVVV{Environment.NewLine}Duty (D), envelope loop / length counter halt (L), constant volume (C), volume/envelope (V)", false);
249 				LabelManager.SetLabel(0x4005, AddressType.Register, "Sq1Sweep_4005", $"EPPP NSSS{Environment.NewLine}Sweep unit: enabled (E), period (P), negate (N), shift (S)", false);
250 				LabelManager.SetLabel(0x4006, AddressType.Register, "Sq1Timer_4006", $"TTTT TTTT{Environment.NewLine}Timer low (T)", false);
251 				LabelManager.SetLabel(0x4007, AddressType.Register, "Sq1Length_4007", $"LLLL LTTT{Environment.NewLine}Length counter load (L), timer high (T)", false);
252 
253 				LabelManager.SetLabel(0x4008, AddressType.Register, "TrgLinear_4008", $"CRRR RRRR{Environment.NewLine}Length counter halt / linear counter control (C), linear counter load (R)", false);
254 				LabelManager.SetLabel(0x400A, AddressType.Register, "TrgTimer_400A", $"TTTT TTTT{Environment.NewLine}Timer low (T)", false);
255 				LabelManager.SetLabel(0x400B, AddressType.Register, "TrgLength_400B", $"LLLL LTTT{Environment.NewLine}Length counter load (L), timer high (T)", false);
256 
257 				LabelManager.SetLabel(0x400C, AddressType.Register, "NoiseVolume_400C", $"--LC VVVV{Environment.NewLine}Envelope loop / length counter halt (L), constant volume (C), volume/envelope (V)", false);
258 				LabelManager.SetLabel(0x400E, AddressType.Register, "NoisePeriod_400E", $"L--- PPPP{Environment.NewLine}Loop noise (L), noise period (P)", false);
259 				LabelManager.SetLabel(0x400F, AddressType.Register, "NoiseLength_400F", $"LLLL L---{Environment.NewLine}Length counter load (L)", false);
260 
261 				LabelManager.SetLabel(0x4010, AddressType.Register, "DmcFreq_4010", $"IL-- RRRR{Environment.NewLine}IRQ enable (I), loop (L), frequency (R)", false);
262 				LabelManager.SetLabel(0x4011, AddressType.Register, "DmcCounter_4011", $"-DDD DDDD{Environment.NewLine}Load counter (D)", false);
263 				LabelManager.SetLabel(0x4012, AddressType.Register, "DmcAddress_4012", $"AAAA AAAA{Environment.NewLine}Sample address (A)", false);
264 				LabelManager.SetLabel(0x4013, AddressType.Register, "DmcLength_4013", $"LLLL LLLL{Environment.NewLine}Sample length (L)", false);
265 
266 				LabelManager.SetLabel(0x4014, AddressType.Register, "SpriteDma_4014", "Writing $XX will upload 256 bytes of data from CPU page $XX00-$XXFF to the internal PPU OAM.", false);
267 
268 				LabelManager.SetLabel(0x4015, AddressType.Register, "ApuStatus_4015", $"Read:{Environment.NewLine}IF-D NT21{Environment.NewLine}DMC interrupt (I), frame interrupt (F), DMC active (D), length counter > 0 (N/T/2/1){Environment.NewLine + Environment.NewLine}Write:{Environment.NewLine}---D NT21{Environment.NewLine}Enable DMC (D), noise (N), triangle (T), and pulse channels (2/1)", false);
269 
270 				LabelManager.SetLabel(0x4016, AddressType.Register, "Ctrl1_4016", $"Read (NES - input):{Environment.NewLine}---4 3210{Environment.NewLine}Read data from controller port #1.{Environment.NewLine}{Environment.NewLine}Write:{Environment.NewLine}---- ---A{Environment.NewLine}Output data (strobe) to both controllers.", false);
271 				LabelManager.SetLabel(0x4017, AddressType.Register, "Ctrl2_FrameCtr_4017", $"Read (NES - input):{Environment.NewLine}---4 3210{Environment.NewLine}Read data from controller port #2.{Environment.NewLine}{Environment.NewLine}Write (Frame counter): MI-- ----{Environment.NewLine}Mode (M, 0 = 4-step, 1 = 5-step), IRQ inhibit flag (I)", false);
272 			}
273 
274 			if(!disableBuiltInMapperWorkspace && mapperId == FdsMapperID) {
275 				LabelManager.SetLabel(0x01F8, AddressType.PrgRom, "LoadFiles", "Input: Pointer to Disk ID, Pointer to File List" + Environment.NewLine + "Output: A = error #, Y = # of files loaded" + Environment.NewLine + "Desc: Loads files specified by DiskID into memory from disk. Load addresses are decided by the file's header.", false);
276 				LabelManager.SetLabel(0x0237, AddressType.PrgRom, "AppendFile", "Input: Pointer to Disk ID, Pointer to File Header" + Environment.NewLine + "Output: A = error #" + Environment.NewLine + "Desc: Appends the file data given by DiskID to the disk. This means that the file is tacked onto the end of the disk, and the disk file count is incremented. The file is then read back to verify the write. If an error occurs during verification, the disk's file count is decremented (logically hiding the written file).", false);
277 				LabelManager.SetLabel(0x0239, AddressType.PrgRom, "WriteFile", "Input: Pointer to Disk ID, Pointer to File Header, A = file #" + Environment.NewLine + "Output: A = error #" + Environment.NewLine + "Desc: Same as \"Append File\", but instead of writing the file to the end of the disk, A specifies the sequential position on the disk to write the file (0 is the first). This also has the effect of setting the disk's file count to the A value, therefore logically hiding any other files that may reside after the written one.", false);
278 				LabelManager.SetLabel(0x02B7, AddressType.PrgRom, "CheckFileCount", "Input: Pointer to Disk ID, A = # to set file count to" + Environment.NewLine + "Output: A = error #" + Environment.NewLine + "Desc: Reads in disk's file count, compares it to A, then sets the disk's file count to A.", false);
279 				LabelManager.SetLabel(0x02BB, AddressType.PrgRom, "AdjustFileCount", "Input: Pointer to Disk ID, A = number to reduce current file count by" + Environment.NewLine + "Output: A = error #" + Environment.NewLine + "Desc: Reads in disk's file count, decrements it by A, then writes the new value back.", false);
280 				LabelManager.SetLabel(0x0301, AddressType.PrgRom, "SetFileCount1", "Input: Pointer to Disk ID, A = file count minus one = # of the last file" + Environment.NewLine + "Output: A = error #" + Environment.NewLine + "Desc: Set the file count to A + 1", false);
281 				LabelManager.SetLabel(0x0305, AddressType.PrgRom, "SetFileCount", "Input: Pointer to Disk ID, A = file count" + Environment.NewLine + "Output: A = error #" + Environment.NewLine + "Desc: Set the file count to A", false);
282 				LabelManager.SetLabel(0x032A, AddressType.PrgRom, "GetDiskInfo", "Input: Pointer to Disk Info" + Environment.NewLine + "Output: A = error #" + Environment.NewLine + "Desc: Fills DiskInfo up with data read off the current disk.", false);
283 
284 				LabelManager.SetLabel(0x0445, AddressType.PrgRom, "CheckDiskHeader", "Input: Pointer to 10 byte string at $00 " + Environment.NewLine + "Output:  " + Environment.NewLine + "Desc: Compares the first 10 bytes on the disk coming after the FDS string, to 10 bytes pointed to by Ptr($00). To bypass the checking of any byte, a -1 can be placed in the equivelant place in the compare string.  Otherwise, if the comparison fails, an appropriate error will be generated.", false);
285 				LabelManager.SetLabel(0x0484, AddressType.PrgRom, "GetNumFiles", "Input:  " + Environment.NewLine + "Output:  " + Environment.NewLine + "Desc: Reads number of files stored on disk, stores the result in $06", false);
286 				LabelManager.SetLabel(0x0492, AddressType.PrgRom, "SetNumFiles", "Input:  " + Environment.NewLine + "Output: A = number of files " + Environment.NewLine + "Desc: Writes new number of files to disk header.", false);
287 				LabelManager.SetLabel(0x04A0, AddressType.PrgRom, "FileMatchTest", "Input: Pointer to FileID list at $02 " + Environment.NewLine + "Output:   " + Environment.NewLine + "Desc: Uses a byte string pointed at by Ptr($02) to tell the disk system which files to load.  The file ID's number is searched for in the string.  If an exact match is found, [$09] is 0'd, and [$0E] is incremented.  If no matches are found after 20 bytes, or a -1 entry is encountered, [$09] is set to -1.  If the first byte in the string is -1, the BootID number is used for matching files (any FileID that is not greater than the BootID qualifies as a match).", false);
288 				LabelManager.SetLabel(0x04DA, AddressType.PrgRom, "SkipFiles", "Input: Number of files to skip in $06 " + Environment.NewLine + "Output:  " + Environment.NewLine + "Desc: Skips over specified number of files.", false);
289 
290 				LabelManager.SetLabel(0x0149, AddressType.PrgRom, "Delay132", "Input: " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: " + Environment.NewLine + "Desc: 132 clock cycle delay", false);
291 				LabelManager.SetLabel(0x0153, AddressType.PrgRom, "Delayms", "Input: " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: X, Y " + Environment.NewLine + "Desc: Delay routine, Y = delay in ms (approximate)", false);
292 				LabelManager.SetLabel(0x0161, AddressType.PrgRom, "DisPFObj", "Input: " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, $fe " + Environment.NewLine + "Desc: Disable sprites and background", false);
293 				LabelManager.SetLabel(0x016B, AddressType.PrgRom, "EnPFObj", "Input: " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, $fe " + Environment.NewLine + "Desc: Enable sprites and background", false);
294 				LabelManager.SetLabel(0x0171, AddressType.PrgRom, "DisObj", "Input: " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, $fe " + Environment.NewLine + "Desc: Disable sprites", false);
295 				LabelManager.SetLabel(0x0178, AddressType.PrgRom, "EnObj", "Input: " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, $fe " + Environment.NewLine + "Desc: Enable sprites", false);
296 				LabelManager.SetLabel(0x017E, AddressType.PrgRom, "DisPF", "Input: " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, $fe " + Environment.NewLine + "Desc: Disable background", false);
297 				LabelManager.SetLabel(0x0185, AddressType.PrgRom, "EnPF", "Input: " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, $fe " + Environment.NewLine + "Desc: Enable background", false);
298 				LabelManager.SetLabel(0x01B2, AddressType.PrgRom, "VINTWait", "Input: " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: $ff " + Environment.NewLine + "Desc: Wait until next VBlank NMI fires, and return (for programs that does it the \"everything in main\" way). NMI vector selection at $100 is preserved, but further VBlanks are disabled.", false);
299 				LabelManager.SetLabel(0x07BB, AddressType.PrgRom, "VRAMStructWrite", "Input: Pointer to VRAM buffer to be written " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, X, Y, $00, $01, $ff " + Environment.NewLine + "Desc: Set VRAM increment to 1 (clear [[PPUCTRL]]/$ff bit 2), and write a VRAM buffer to VRAM. Read below for information on the structure.", false);
300 				LabelManager.SetLabel(0x0844, AddressType.PrgRom, "FetchDirectPtr", "Input: " + Environment.NewLine + "Output: $00, $01 = pointer fetched " + Environment.NewLine + "Affects: A, X, Y, $05, $06 " + Environment.NewLine + "Desc: Fetch a direct pointer from the stack (the pointer should be placed after the return address of the routine that calls this one (see \"important notes\" above)), save the pointer at ($00) and fix the return address.", false);
301 				LabelManager.SetLabel(0x086A, AddressType.PrgRom, "WriteVRAMBuffer", "Input: " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, X, Y, $301, $302 " + Environment.NewLine + "Desc: Write the VRAM Buffer at $302 to VRAM. Read below for information on the structure.", false);
302 				LabelManager.SetLabel(0x08B3, AddressType.PrgRom, "ReadVRAMBuffer", "Input: X = start address of read buffer, Y = # of bytes to read " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, X, Y " + Environment.NewLine + "Desc: Read individual bytes from VRAM to the VRAMBuffer. (see notes below)", false);
303 				LabelManager.SetLabel(0x08D2, AddressType.PrgRom, "PrepareVRAMString", "Input: A = High VRAM address, X = Low VRAM address, Y = string length, Direct Pointer = data to be written to VRAM " + Environment.NewLine + "Output: A = $ff : no error, A = $01 : string didn't fit in buffer " + Environment.NewLine + "Affects: A, X, Y, $00, $01, $02, $03, $04, $05, $06 " + Environment.NewLine + "Desc: This routine copies pointed data into the VRAM buffer.", false);
304 				LabelManager.SetLabel(0x08E1, AddressType.PrgRom, "PrepareVRAMStrings", "Input: A = High VRAM address, X = Low VRAM address, Direct pointer = data to be written to VRAM " + Environment.NewLine + "Output: A = $ff : no error, A = $01 : data didn't fit in buffer " + Environment.NewLine + "Affects: A, X, Y, $00, $01, $02, $03, $04, $05, $06 " + Environment.NewLine + "Desc: This routine copies a 2D string into the VRAM buffer. The first byte of the data determines the width and height of the following string (in tiles): Upper nybble = height, lower nybble = width.", false);
305 				LabelManager.SetLabel(0x094F, AddressType.PrgRom, "GetVRAMBufferByte", "Input: X = starting index of read buffer, Y = # of address to compare (starting at 1), $00, $01 = address to read from " + Environment.NewLine + "Output: carry clear : a previously read byte was returned, carry set : no byte was read, should wait next call to ReadVRAMBuffer " + Environment.NewLine + "Affects: A, X, Y " + Environment.NewLine + "Desc: This routine was likely planned to be used in order to avoid useless latency on a VRAM reads (see notes below). It compares the VRAM address in ($00) with the Yth (starting at 1) address of the read buffer. If both addresses match, the corresponding data byte is returned exit with c clear. If the addresses are different, the buffer address is overwritten by the address in ($00) and the routine exit with c set.", false);
306 				LabelManager.SetLabel(0x097D, AddressType.PrgRom, "Pixel2NamConv", "Input: $02 = Pixel X cord, $03 = Pixel Y cord " + Environment.NewLine + "Output: $00 = High nametable address, $01 = Low nametable address " + Environment.NewLine + "Affects: A " + Environment.NewLine + "Desc: This routine convert pixel screen coordinates to corresponding nametable address (assumes no scrolling, and points to first nametable at $2000-$23ff).", false);
307 				LabelManager.SetLabel(0x0997, AddressType.PrgRom, "Nam2PixelConv", "Input: $00 = High nametable address, $01 = low nametable address " + Environment.NewLine + "Output: $02 = Pixel X cord, $03 = Pixel Y cord " + Environment.NewLine + "Affects: A " + Environment.NewLine + "Desc: This routine convert a nametable address to corresponding pixel coordinates (assume no scrolling).", false);
308 				LabelManager.SetLabel(0x09B1, AddressType.PrgRom, "Random", "Input: X = Zero Page address where the random bytes are placed, Y = # of shift register bytes (normally $02) " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, X, Y, $00 " + Environment.NewLine + "Desc: This is a shift-register based random number generator, normally takes 2 bytes (using more won't affect random sequence). On reset you are supposed to write some non-zero values here (BIOS uses writes $d0, $d0), and call this routine several times before the data is actually random. Each call of this routine will shift the bytes ''right''.", false);
309 				LabelManager.SetLabel(0x09C8, AddressType.PrgRom, "SpriteDMA", "Input: " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A " + Environment.NewLine + "Desc: This routine does sprite DMA from RAM $200-$2ff", false);
310 				LabelManager.SetLabel(0x09D3, AddressType.PrgRom, "CounterLogic", "Input: A, Y = end Zeropage address of counters, X = start zeropage address of counters " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, X, $00 " + Environment.NewLine + "Desc: This decrements several counters in Zeropage. The first counter is a decimal counter 9 -> 8 -> 7 -> ... -> 1 -> 0 -> 9 -> ... Counters 1...A are simply decremented and stays at 0. Counters A+1...Y are decremented when the first counter does a 0 -> 9 transition, and stays at 0.", false);
311 				LabelManager.SetLabel(0x09EB, AddressType.PrgRom, "ReadPads", "Input: " + Environment.NewLine + "Output: $f5 = Joypad #1 data, $f6 = Joypad #2 data " + Environment.NewLine + "Affects: A, X, $00, $01, " + Environment.NewLine + "Desc: This read hardwired famicom joypads.", false);
312 				LabelManager.SetLabel(0x0A1A, AddressType.PrgRom, "ReadDownPads", "Input: " + Environment.NewLine + "Output: $f5 = Joypad #1 up->down transitions, $f6 = Joypad #2 up->down transitions $f7 = Joypad #1 data, $f8 = Joypad #2 data " + Environment.NewLine + "Affects: A, X, $00, $01 " + Environment.NewLine + "Desc: This reads hardwired famicom joypads, and detect up->down button transitions", false);
313 				LabelManager.SetLabel(0x0A1F, AddressType.PrgRom, "ReadOrDownPads", "Input: " + Environment.NewLine + "Output: $f5 = Joypad #1 up->down transitions, $f6 = Joypad #2 up->down transitions $f7 = Joypad #1 data, $f8 = Joypad #2 data " + Environment.NewLine + "Affects: A, X, $00, $01 " + Environment.NewLine + "Desc: This read both hardwired famicom and expansion port joypads and detect up->down button transitions.", false);
314 				LabelManager.SetLabel(0x0A36, AddressType.PrgRom, "ReadDownVerifyPads", "Input: " + Environment.NewLine + "Output: $f5 = Joypad #1 up->down transitions, $f6 = Joypad #2 up->down transitions $f7 = Joypad #1 data, $f8 = Joypad #2 data " + Environment.NewLine + "Affects: A, X, $00, $01 " + Environment.NewLine + "Desc: This reads hardwired Famicom joypads, and detect up->down button transitions. Data is read until two consecutive read matches to work around the DMC reading glitches.", false);
315 				LabelManager.SetLabel(0x0A4C, AddressType.PrgRom, "ReadOrDownVerifyPads", "Input: " + Environment.NewLine + "Output: $f5 = Joypad #1 up->down transitions, $f6 = Joypad #2 up->down transitions $f7 = Joypad #1 data, $f8 = Joypad #2 data " + Environment.NewLine + "Affects: A, X, $00, $01 " + Environment.NewLine + "Desc: This read both hardwired famicom and expansion port joypads and detect up->down button transitions. Data is read until two consecutive read matches to work around the DMC reading glitches.", false);
316 				LabelManager.SetLabel(0x0A68, AddressType.PrgRom, "ReadDownExpPads", "Input: $f1-$f4 = up->down transitions, $f5-$f8 = Joypad data in the order : Pad1, Pad2, Expansion1, Expansion2 " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, X, $00, $01 " + Environment.NewLine + "Desc: This read both hardwired famicom and expansion port joypad, but stores their data separately instead of ORing them together like the other routines does. This routine is NOT DMC fortified.", false);
317 				LabelManager.SetLabel(0x0A84, AddressType.PrgRom, "VRAMFill", "Input: A = High VRAM Address (aka tile row #), X = Fill value, Y = # of tile rows OR attribute fill data " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, X, Y, $00, $01, $02 " + Environment.NewLine + "Desc: This routine does 2 things : If A < $20, it fills pattern table data with the value in X for 16 * Y tiles. If A >= $20, it fills the corresponding nametable with the value in X and attribute table with the value in Y.", false);
318 				LabelManager.SetLabel(0x0Ad2, AddressType.PrgRom, "MemFill", "Input: A = fill value, X = first page #, Y = last page # " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, X, Y, $00, $01 " + Environment.NewLine + "Desc: This routines fills RAM pages with specified value.", false);
319 				LabelManager.SetLabel(0x0AEA, AddressType.PrgRom, "SetScroll", "Input: " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A " + Environment.NewLine + "Desc: This routine set scroll registers according to values in $fc, $fd and $ff. Should typically be called in VBlank after VRAM updates", false);
320 				LabelManager.SetLabel(0x0AFD, AddressType.PrgRom, "JumpEngine", "Input: A = Jump table entry " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, X, Y, $00, $01 " + Environment.NewLine + "Desc: The instruction calling this is supposed to be followed by a jump table (16-bit pointers little endian, up to 128 pointers). A is the entry # to jump to, return address on stack is used to get jump table entries.", false);
321 				LabelManager.SetLabel(0x0B13, AddressType.PrgRom, "ReadKeyboard", "Input: " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: " + Environment.NewLine + "Desc: Read Family Basic Keyboard expansion (detail is under analysis)", false);
322 				LabelManager.SetLabel(0x0B66, AddressType.PrgRom, "LoadTileset", "Input: A = Low VRAM Address & Flags, Y = Hi VRAM Address, X = # of tiles to transfer to/from VRAM " + Environment.NewLine + "Output: " + Environment.NewLine + "Affects: A, X, Y, $00, $01, $02, $03, $04 " + Environment.NewLine + "Desc: This routine can read and write 2BP and 1BP tilesets to/from VRAM. See appendix below about the flags.", false);
323 				LabelManager.SetLabel(0x0C22, AddressType.PrgRom, "unk_EC22", "Some kind of logic that some games use. (detail is under analysis)", false);
324 			}
325 		}
326 	}
327 
328 	[Flags]
329 	public enum CodeLabelFlags
330 	{
331 		None = 0,
332 		AutoJumpLabel = 1
333 	}
334 }
335