/// <summary> /// List all PLA entries that trigger on a specific opcode /// If the opcode # (hex) was not given, dump that information for all opcodes (0-FF) /// </summary> private void MatchPLA(string arg) { int op = -1; if (!string.IsNullOrEmpty(arg)) { op = ScanNumber(arg, 16); if (op < 0) { return; } } for (int x = 0; x < 256; x++) { if (op >= 0 && x != op) { continue; } ClassLog.Log(String.Format("Opcode: {0:X02} ", x)); Byte opcode = Convert.ToByte(x); List <string> m = pla.TableMatch(modifier, opcode); foreach (var s in m) { ClassLog.Log(s); } } }
/// <summary> /// Dump opcode table in various ways. /// Returns a "selected" list of opcode numbers, that is, opcodes which were tagged by /// the optional input PLA table number given in arg parameter. /// </summary> public List <int> Table(ClassPlaEntry.Modifier modifier, int testNum, int arg) { ClassLog.Log(new string('-', 242)); List <int> tagged = new List <int>(); for (int y = 0; y < 16; y++) { string line = string.Format("{0:X} ", y); for (int x = 0; x < 16; x++) { char prefix = ' '; byte opcode = Convert.ToByte(y * 16 + x); List <string> match = TableMatch(modifier, opcode); foreach (string oneMatch in match.Where(oneMatch => Convert.ToInt32(oneMatch.Substring(1, oneMatch.LastIndexOf(']') - 1)) == arg)) { tagged.Add(y * 16 + x); prefix = '*'; } string entry = ""; //=============================================================================== // Table 0 - Show the number of PLA entries that match each opcode //=============================================================================== if (testNum == 0) { entry = string.Join(",", match); if (match.Count == 0) { entry = "."; } if (match.Count > 1) { entry = "[" + match.Count + "]"; } } //=============================================================================== // Table 1 - For each opcode, show all PLA entries that trigger //=============================================================================== if (testNum == 1) { foreach (string oneMatch in match) { string n = oneMatch.Substring(1, oneMatch.LastIndexOf(']') - 1); entry += n + ","; } entry = entry.TrimEnd(','); } // ------------------------------------------- if (entry.Length > 12) { entry = entry.Substring(0, 12); } line += string.Format(" |{0}{1,-12}", prefix, entry); } ClassLog.Log(line); } return(tagged); }
/// <summary> /// This is the main program's startup function /// </summary> public void OnStart() { ClassLog.Log("PLA Checker Tool Copyright (C) 2014 Goran Devic"); ClassLog.Log("This program comes with ABSOLUTELY NO WARRANTY"); ClassLog.Log("This is free software and you are welcome to redistribute it under certain conditions; for details see GPLv3 license."); ClassLog.Log("---------------------------------------------------------------------------------------------------------------------"); // Load the PLA table from a text file. String plaFile = Properties.Settings.Default.plaFileName; if (!pla.Load(plaFile)) { ClassLog.Log("*** Error loading the master input PLA source table ***"); ClassLog.Log("Click on File -> Load PLA table... and select a resource file z80-pla.txt"); return; } // Load opcode tables from a previously selected directory // We use only IX variation tables since IY's are the same String opFile = Properties.Settings.Default.opcodeDir; char p = Path.DirectorySeparatorChar; tableXX__.Load(opFile + p + "opcodes-xx.txt", 0); tableCBXX.Load(opFile + p + "opcodes-cb-xx.txt", 2); tableEDXX.Load(opFile + p + "opcodes-ed-xx.txt", 2); tableDDXX.Load(opFile + p + "opcodes-dd-xx.txt", 2); tableDDCB.Load(opFile + p + "opcodes-dd-cb.txt", 7); ClassLog.Log(Command("?")); }
/// <summary> /// Dumps the opcode table in a table format /// The opcode numbers in the list t will be tagged (marked with *) /// </summary> public void Dump(List <int> t) { ClassLog.Log(new string('-', 242)); for (int y = 0; y < 16; y++) { string line = string.Format("{0:X} ", y); for (int x = 0; x < 16; x++) { string opcode = ""; if (op.ContainsKey(y * 16 + x)) { opcode = op[y * 16 + x]; } if (opcode.Length > 12) { opcode = opcode.Substring(0, 12); } char tag = ' '; if (t.Contains(y * 16 + x)) { tag = '*'; } line += string.Format(" |{0}{1,-12}", tag, opcode); } ClassLog.Log(line); } }
/// <summary> /// Dumps the content of the entire PLA table /// </summary> public void Dump() { ClassLog.Log("Content of the PLA table:"); foreach (var p in pla.Where(p => !p.IsDuplicate())) { ClassLog.Log(p.Raw); } }
/// <summary> /// User clicked on the Redo button: repeat the command /// </summary> private void BtRedoClick(object sender, EventArgs e) { ClassLog.Log(string.Format("{0}>>> {1}", commands.Count, commands[commands.Count - 1])); string response = Command(commands[commands.Count - 1]); if (!string.IsNullOrEmpty(response)) { ClassLog.Log(response); } }
/// <summary> /// Generates a Verilog module with the PLA logic /// </summary> public void GenVerilogPla() { string max = (pla.Count() - 1).ToString(); string module = ""; module += @"//=====================================================================================" + Environment.NewLine; module += @"// This file is automatically generated by the z80_pla_checker tool. Do not edit! " + Environment.NewLine; module += @"//=====================================================================================" + Environment.NewLine; module += @"module pla_decode" + Environment.NewLine; module += @"(" + Environment.NewLine; module += @" input wire [6:0] prefix," + Environment.NewLine; module += @" input wire [7:0] opcode," + Environment.NewLine; module += @" output wire [" + max + ":0] pla" + Environment.NewLine; module += @");" + Environment.NewLine; module += @"" + Environment.NewLine; foreach (var p in pla) { if (p.IsDuplicate() || NotUsedPla.Contains(p.N)) { continue; } String bitstream = p.GetBitstream(); module += string.Format(@"assign pla[{0,3}] = (({{prefix[6:0], opcode[7:0]}} & 15'b{1}) == 15'b{2}) ? 1'b1 : 1'b0; // {3}", p.N, bitstream.Replace('0', '1').Replace('X', '0'), // Create "AND" mask bitstream.Replace('X', '0'), // Create a value to compare to p.Comment) + Environment.NewLine; } // List all PLA entries that are not used module += @"" + Environment.NewLine; module += @"// Entries not used by our timing matrix" + Environment.NewLine; foreach (var n in NotUsedPla) { module += string.Format(@"assign pla[{0,3}] = 1'b0; // {1}", n, pla[n].Comment) + Environment.NewLine; } // List all PLA entries that are ignored module += @"" + Environment.NewLine; module += @"// Duplicate entries" + Environment.NewLine; foreach (var p in pla) { if (p.IsDuplicate()) { module += string.Format(@"assign pla[{0,3}] = 1'b0; // {1}", p.N, p.Comment) + Environment.NewLine; } } module += @"" + Environment.NewLine; module += @"endmodule" + Environment.NewLine; ClassLog.Log(module); }
private static int ScanNumber(string arg, int baseValue) { try { return(Convert.ToInt32(arg, baseValue)); } catch (Exception ex) { ClassLog.Log(ex.Message + ": " + arg); return(-1); } }
/// <summary> /// Update button state after the internal flag state change /// </summary> private void UpdateButtons() { btIX0.Checked = (modifier & ClassPlaEntry.Modifier.IXY0) != 0; btIX1.Checked = (modifier & ClassPlaEntry.Modifier.IXY1) != 0; btHALT.Checked = (modifier & ClassPlaEntry.Modifier.NHALT) != 0; btALU.Checked = (modifier & ClassPlaEntry.Modifier.ALU) != 0; btXX.Checked = (modifier & ClassPlaEntry.Modifier.XX) != 0; btCB.Checked = (modifier & ClassPlaEntry.Modifier.CB) != 0; btED.Checked = (modifier & ClassPlaEntry.Modifier.ED) != 0; ClassLog.Log("Set modifier to " + modifier); }
/// <summary> /// Query PLA table string given as a vector of 0's and 1's /// This vector is coped from a ModelSim simulation run. The function will decode PLA string /// into a set of PLA entries that are being triggered ("1") /// </summary> public void QueryPla(String bits) { int max = pla.Count(); if (bits.Count() != max) { ClassLog.Log("Invalid PLA length - the bit array should be " + max + " and it is " + bits.Count()); return; } for (int i = 0; i < max; i++) { if (bits[max - i - 1] == '1') { ClassLog.Log(string.Format(@"pla[{0,3}] = 1; // {1}", pla[i].N, pla[i].Comment)); } } }
public bool Ignored = false; // This entry can optionally be ignored /// <summary> /// PLA entry class constructor /// Accepts the init string which should contain a line from the PLA master table. /// Various fields from that line are read into this class. /// </summary> public bool Init(string init) { try { Raw = init; char[] delimiterChars = { '\t' }; string[] w = init.Split(delimiterChars); // Example of an input line: // w[0] w[1] w[2] w[3] w[4] // ....1.. 1.1........1.11. D 63 00xxx110 ld r,* // Mark a duplicate duplicate = w[1].Contains("D"); // Read the 7 bits of the prefix for (int i = 0; i < 7; i++) { if (w[0][6 - i] == '1') { prefix |= (1 << i); } } // Read 16 bits of the opcode mask for (int i = 0; i < 16; i++) { if (w[0][23 - i] == '1') { opcode |= (1 << i); } } N = Convert.ToInt32(w[2]); Comment = w[4]; return(true); } catch (Exception ex) { ClassLog.Log("ClassPlaEntry: Can't parse line: " + init); ClassLog.Log(ex.Message); } return(false); }
/// <summary> /// List all opcodes that trigger on a given PLA table index /// </summary> private void MatchOpcodes(ClassPlaEntry.Modifier modifier, string arg) { int index = ScanNumber(arg, 10); if (index < 0) { return; } List <string> m = pla.MatchPLA(modifier, index); if (m.Count == 0) { return; } ClassLog.Log(String.Format("PLA Entry: {0} Modifier: {1}", index, modifier)); foreach (var s in m) { ClassLog.Log(s); } }
/// <summary> /// Implements a simple command history /// </summary> private void TextOpKeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { // Mark the handled flag so this key won't be processed. e.Handled = true; string cmd = textOp.Text.Trim(); if (cmd.Length > 0) { commands.Add(cmd); btRedo.Enabled = true; ClassLog.Log(string.Format("{0}>>> {1}", commands.Count, cmd)); string response = Command(textOp.Text); if (!string.IsNullOrEmpty(response)) { ClassLog.Log(response); } commandsBrowseIndex = commands.Count; textOp.Text = ""; } textOp.Focus(); } if (e.KeyCode == Keys.PageUp && commandsBrowseIndex > 0) { commandsBrowseIndex--; textOp.Text = commands[commandsBrowseIndex]; e.Handled = true; } if (e.KeyCode == Keys.PageDown && commandsBrowseIndex < commands.Count - 1) { commandsBrowseIndex++; textOp.Text = commands[commandsBrowseIndex]; e.Handled = true; } if (e.KeyCode == Keys.Escape) { textOp.Text = ""; e.Handled = true; } }
/// <summary> /// Loads an opcode table from a text file /// </summary> public void Load(string filename, int xxindex) { ClassLog.Log("Loading opcode table: " + filename); try { string[] lines = File.ReadAllLines(filename); op.Clear(); foreach (string line in lines) { string hex = line.Substring(xxindex, 2); string instr = line.Substring(12); int xx = Convert.ToInt32(hex, 16); op[xx] = instr; } } catch (Exception ex) { ClassLog.Log(ex.Message); } }
/// <summary> /// Given the PLA ID, return a list of all opcodes that trigger it /// </summary> public List <string> MatchPLA(ClassPlaEntry.Modifier modifier, int id) { var m = new List <string>(); // Find the pla with a given index foreach (ClassPlaEntry p in pla) { if (p.N == id) { // For each possible opcode... for (int i = 0; i < 256; i++) { String match = p.Match(modifier, Convert.ToByte(i)); if (!string.IsNullOrEmpty(match)) { m.Add(string.Format("{0:X02} => {1}", i, match)); } } return(m); } } ClassLog.Log("Non-existent PLA index"); return(m); }
/// <summary> /// Read the master PLA table from a text file /// </summary> public bool Load(string filename) { // Read each line of the file into a string array. Each element // of the array is one line of the file. ClassLog.Log("Loading PLA: " + filename); try { string[] lines = File.ReadAllLines(filename); pla.Clear(); foreach (string line in lines) { if (line[0] == '#') { continue; } var p = new ClassPlaEntry(); if (p.Init(line)) { pla.Add(p); } } } catch (Exception ex) { ClassLog.Log(ex.Message); return(false); } ClassLog.Log(string.Format("Total {0} PLA lines", pla.Count())); ////============================================================ //// Ignore duplicate PLA entries //IgnoredPla.Add(98); // Duplicate of 37 //IgnoredPla.Add(94); // Duplicate of 12 and 18 //IgnoredPla.Add(93); // Duplicate of 11 and 19 //IgnoredPla.Add(90); // Duplicate of 26 //IgnoredPla.Add(87); // Duplicate of 83 //IgnoredPla.Add(71); // Duplicate of 25 //IgnoredPla.Add(63); // Duplicate of 17 //IgnoredPla.Add(60); // Duplicate of 15 //IgnoredPla.Add(41); // Duplicate of 3 //IgnoredPla.Add(36); // Duplicate of 8 //IgnoredPla.Add(32); // Duplicate of 4 //IgnoredPla.Add(19); // Duplicate of 11 and 93 //IgnoredPla.Add(18); // Duplicate of 12 and 94 ////============================================================ //// Special signals (not instructions) //IgnoredPla.Add(91); // This signal goes along block IN/OUT instructions. //IgnoredPla.Add(75); // This signal specifies a decrement operation for PLA 53, 66 and 105. Otherwise, it is an increment. //IgnoredPla.Add(55); // This signal specifies (HL) addressing for all CB-table instructions, PLA entries 70, 72, 73, 74. //IgnoredPla.Add(44); // This signal specifies a regular CB opcode (ignoring IX/IY). //IgnoredPla.Add(33); // This signal specifies whether the register is being loaded or stored to memory for PLA entry 31. //IgnoredPla.Add(28); // This signal specifies the OUT operation for PLA 37. Otherwise, it is operation. //IgnoredPla.Add(27); // This signal goes along individual IN/OUT instructions in the ED table. //IgnoredPla.Add(16); // This signal specifies a PUSH operation for PLA23. Otherwise, it is a POP operation. //IgnoredPla.Add(13); // This signal specifies whether the value is being loaded or stored for PLA entries 8, 30 and 38. //IgnoredPla.Add(0); // This signal specifies *not* to repeat block instructions. ////============================================================ //// Ignore our own reserved entries //IgnoredPla.Add(107); //IgnoredPla.Add(106); //============================================================ // Remove op-bits so we the output is more readable IgnoredPla.Add(99); IgnoredPla.Add(100); IgnoredPla.Add(101); IgnoredPla.Add(102); IgnoredPla.Add(103); IgnoredPla.Add(104); // Remove ALU operation entries so the output is more readable IgnoredPla.Add(88); IgnoredPla.Add(86); IgnoredPla.Add(85); IgnoredPla.Add(84); IgnoredPla.Add(80); IgnoredPla.Add(79); IgnoredPla.Add(78); IgnoredPla.Add(76); //============================================================ // Signals not used in the Timings spreadsheet. For those, PLA table entries are not generated. // This list is used only when generating the PLA table. NotUsedPla.Add(67); // This signal defines a specific in(), but we use in/out pla[27] + pla[34] NotUsedPla.Add(62); // This signal is issued for all CB opcodes NotUsedPla.Add(54); // This signal specifies every CB with IX/IY NotUsedPla.Add(22); // This signal specifies CB prefix w/o IX/IY NotUsedPla.Add(14); // This signal specifies a decrement operation for PLA 9. Otherwise, it is an increment. NotUsedPla.Add(4); // This signal goes along instructions that access I and R register (PLA 57 and 83). //============================================================ // Mark all PLA entries we decided to ignore foreach (var p in pla) { if (IgnoredPla.Contains <int>(p.N)) { p.Ignored = true; } } return(true); }
/// <summary> /// Execute a command /// </summary> private string Command(string cmd) { try { string[] tokens = cmd.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); if (tokens.Length == 0) { return(""); } switch (tokens[0]) { case "?": case "h": return(Environment.NewLine + "p - Dump the content of the PLA table" + Environment.NewLine + "p [#] - For a given PLA entry # (dec) show opcodes that trigger it" + Environment.NewLine + "m [#] - Match opcode # (hex) with a PLA entry (or match 0-FF)" + Environment.NewLine + "g - Generate a Verilog PLA module" + Environment.NewLine + "t [#] <#> - Show opcode table in various ways" + Environment.NewLine + " 0 - Display number of PLA entries that trigger on each opcode" + Environment.NewLine + " 1 - For each opcode, display all PLA entry numbers that trigger" + Environment.NewLine + " <#> - Add a * to opcodes for which the specified PLA entry triggers" + Environment.NewLine + "q 101000... Query PLA table string" + Environment.NewLine + "c - Clear the screen"); case "p": if (tokens.Length > 1) { MatchOpcodes(modifier, tokens[1]); } else { pla.Dump(); } break; case "m": MatchPLA(tokens.Length > 1 ? tokens[1] : ""); break; case "g": pla.GenVerilogPla(); break; case "c": BtClearClick(null, null); break; case "t": { int arg1 = 0, arg2 = -1; if (tokens.Length > 1) { arg1 = ScanNumber(tokens[1], 10); } if (tokens.Length > 2) { arg2 = ScanNumber(tokens[2], 10); } if (arg1 == 0 || arg1 == 1) { List <int> tagged = pla.Table(modifier, arg1, arg2); DumpOpcodeTable(tagged); } else { ClassLog.Log("Invalid table number!"); } } break; case "q": pla.QueryPla(tokens[1].Trim()); break; default: return("?"); } } catch (Exception ex) { ClassLog.Log("Error: " + ex.Message); } return(string.Empty); }