示例#1
0
        public Proc(Instruction instruction)
        {
            if (instruction.proc != null)
                throw new InvalidOperationException("Instruction already associated with another procedure");

            firstInstruction = instruction;
            firstInstruction.proc = this;
        }
示例#2
0
        public int Run(string[] args)
        {
            // Process command line
            if (!ProcessArgs(args))
                return 0;

            if (_inputFile == null)
            {
                ShowLogo();
                ShowHelp();
                return 7;
            }

            // Open input file
            var code = System.IO.File.ReadAllBytes(_inputFile);

            // Work out the available address space
            _addrSpaceStart = _baseAddr;
            _addrSpaceEnd = (ushort)(_baseAddr + (code.Length - _header));

            // Work out auto length
            if (_len == 0)
                _len = (ushort)(code.Length - _header);

            // Check specified address range
            CheckAddress(_start);
            CheckAddress(_start + _len);
            foreach (var addr in _entryPoints)
                CheckAddress(addr);

            // Setup disassembler parameters
            Disassembler.LabelledRangeLow = (ushort)_start;
            Disassembler.LabelledRangeHigh = (ushort)(_start + _len);
            Disassembler.LowerCase = _lowerCase;
            Disassembler.HtmlMode = _htmlMode;
            Disassembler.ShowRelativeOffsets = _reloffs;

            // Disassemble
            var instructions = new Dictionary<int, Instruction>();
            if (_entryPoints.Count > 0)
            {
                var pendingCodePaths = new List<int>();
                pendingCodePaths.AddRange(_entryPoints);

                while (pendingCodePaths.Count > 0)
                {
                    // Get a new address that needs disassembling
                    int addr = pendingCodePaths[0];
                    pendingCodePaths.RemoveAt(0);

                    // Disassemble
                    while (!instructions.ContainsKey(addr) && addr >= _start && addr < _start + _len)
                    {
                        // Disassemble this instruction
                        var i = Disassembler.Disassemble(code, (ushort)(addr - _baseAddr + _header), (ushort)addr);

                        // Possible address reference?
                        if (_markWordRefs && i.word_val.HasValue && (i.opCode.flags & (OpCodeFlags.Jumps | OpCodeFlags.RefAddr)) == 0)
                        {
                            i.Comment = "address or value?";
                        }

                        // Add it
                        instructions.Add(addr, i);

                        // If have a jump address, dump it
                        if (i.next_addr_2.HasValue)
                        {
                            pendingCodePaths.Add(i.next_addr_2.Value);
                        }

                        // Continue
                        if (i.next_addr_1.HasValue)
                            addr = i.next_addr_1.Value;
                        else
                            break;

                    }
                }
            }
            else
            {
                // Linear disassembly
                for (int addr = _start; addr < _start + _len; )
                {
                    // Disassemble this instruction
                    var i = Disassembler.Disassemble(code, (ushort)(addr - _baseAddr + _header), (ushort)addr);

                    // Add it
                    instructions.Add(addr, i);

                    // Update address
                    addr += i.bytes;
                }
            }

            // Sort all instructions
            var sorted = instructions.Values.OrderBy(x => x.addr).ToList();

            // Helper for generating DB directives
            Func<int, int, int, int> FillData = delegate(int from, int to, int index)
            {
                for (int j = from; j < to; j++)
                {
                    var data = code[j - _baseAddr + _header];

                    // Get the byte
                    var instruction = new Instruction();
                    if (data >= 0x20 && data <= 0x7f)
                        instruction.Comment = string.Format("'{0}'", (char)data);
                    instruction.addr = (ushort)j;
                    instruction.Asm = string.Format("DB {0}", Disassembler.FormatByte(data));
                    instruction.next_addr_1 = (ushort)(j + 1);
                    instruction.bytes = 1;

                    // Add to instruction map
                    instructions.Add(instruction.addr, instruction);

                    // Add to sorted list
                    sorted.Insert(index, instruction);
                    index++;
                }
                return index;
            };

            // Fill in all unpopulated areas with DBs
            int expectedNextAddress = _start;
            for (int i = 0; i < sorted.Count; i++)
            {
                var inst = sorted[i];
                if (inst.addr != expectedNextAddress)
                {
                    i = FillData(expectedNextAddress, inst.addr, i);
                }

                expectedNextAddress = sorted[i].addr + sorted[i].bytes;
            }
            FillData(expectedNextAddress, _start + _len, instructions.Count);

            // Mark all entry points
            foreach (var e in _entryPoints)
            {
                instructions[e].entryPoint = true;
            }

            // Resolve references
            foreach (var i in instructions)
            {
                ushort? ref_addr = i.Value.next_addr_2;
                if (!ref_addr.HasValue)
                {
                    if (i.Value.word_val.HasValue && (i.Value.opCode.flags & OpCodeFlags.RefAddr) != 0)
                        ref_addr = i.Value.word_val;
                }

                if (ref_addr.HasValue)
                {
                    for (int stepback = 0; stepback < 6; stepback++)
                    {
                        Instruction target;
                        if (instructions.TryGetValue(ref_addr.Value-stepback, out target))
                        {
                            if (target.referencedFrom == null)
                                target.referencedFrom = new List<Instruction>();
                            target.referencedFrom.Add(i.Value);

                            if (stepback != 0)
                            {
                                // patch the original instruction
                                i.Value.Asm = i.Value.Asm.Replace(Disassembler.FormatAddr(ref_addr.Value), Disassembler.FormatAddr(target.addr) + "+" + stepback.ToString());
                                i.Value.Comment = "reference not aligned to instruction";
                            }

                            break;
                        }
                    }
                }
            }

            TextWriter targetWriter = Console.Out;
            if (_outputFile != null)
            {
                targetWriter = new StreamWriter(_outputFile);
            }

            TextWriter w;

            if (_htmlMode)
            {
                w = targetWriter;
            }
            else
            {
                w = new TabbedTextWriter(targetWriter);
                if (_lst)
                {
                    ((TabbedTextWriter)w).TabStops = new int[] { 32, 40, 48, 56, 64 };
                }
                else
                {
                    ((TabbedTextWriter)w).TabStops = new int[] { 8, 16, 32 };
                }
            }

            if (_htmlMode)
            {
                w.WriteLine("<html>");
                w.WriteLine("<head>");
                w.WriteLine("</head>");
                w.WriteLine("<body>");
                w.WriteLine("<pre><code>");
            }

            // Analyse call graph
            var cg = new CallGraphAnalyzer(instructions, sorted);
            Dictionary<int, Proc> procs = cg.Analyse();

            // Write out the "ORG" directive
            if (sorted.Count > 0 && !_lst)
            {
                w.WriteLine("\n\tORG\t{0}\n", Disassembler.FormatWord(sorted[0].addr));
            }

            // List it
            Instruction prev = null;
            foreach (var i in sorted)
            {
                // Blank line after data blocks
                if (prev != null && prev.opCode == null && i.opCode != null)
                {
                    w.WriteLine();
                }

                if (_htmlMode)
                    w.Write("<a name=\"L{0:X4}\"></a>", i.addr);

                // Include cross references?
                if (_xref && i.referencedFrom != null)
                {
                    // Ensure a blank line before reference comments
                    if (prev != null && prev.next_addr_1.HasValue)
                    {
                        w.WriteLine();
                    }

                    if (_lst)
                        w.Write("{0}\t", new string(' ', 23));

                    w.WriteLine("\t; Referenced from {0}",
                            string.Join(", ", i.referencedFrom.Select(x => Disassembler.FormatAddr(x.addr, true, false)).ToList()));
                }

                if (i.entryPoint)
                {
                    if (_lst)
                        w.Write("{0}\t", new string(' ', 23));
                    w.WriteLine("\t; Entry Point");
                }

                if (procs.ContainsKey(i.addr))
                {
                    if (_lst)
                        w.Write("{0}\t", new string(' ', 23));
                    w.WriteLine("\t; --- START PROC {0} ---", Disassembler.FormatAddr(i.addr, false, true));
                }

                if (_lst)
                {
                    w.Write("{0:X4}:", i.addr);
                    for (int j = 0; j < i.bytes; j++)
                    {
                        var data = code[i.addr + j - _baseAddr + _header];
                        w.Write(" {0:X2}", data);
                    }

                    w.Write(new string(' ', 3 * (6 - i.bytes)));
                    w.Write("\t ");
                }

                // Work out label
                string label = "";
                if (i.entryPoint || i.referencedFrom != null || (prev != null && !prev.next_addr_1.HasValue))
                {
                    label = Disassembler.FormatAddr(i.addr, false);
                    label += ":";
                }

                // Write the disassembled instruction
                w.Write("{0}\t{1}", label, i.Asm.Replace(" ", "\t"));

                // Write out an optional comment
                if (i.Comment != null)
                    w.Write("\t; {0}", i.Comment);

                if (_htmlMode)
                    w.WriteLine("</a>");
                else
                    w.WriteLine();

                // If this instruction doesn't continue on, insert a blank line
                if (!i.next_addr_1.HasValue)
                {
                    w.WriteLine();
                }

                // Remember the previous instruction
                prev = i;
            }

            if (_lst)
            {
                // Build a list of all possible address references
                Dictionary<int, AddressInfo> addressInfos = new Dictionary<int, AddressInfo>();
                Dictionary<int, PortInfo> portInfos = new Dictionary<int, PortInfo>();
                foreach (var i in sorted)
                {
                    // Does this instruction reference a word value?
                    if (i.word_val.HasValue)
                    {
                        AddressInfo ai;
                        if (!addressInfos.TryGetValue(i.word_val.Value, out ai))
                        {
                            ai = new AddressInfo(i.word_val.Value);
                            addressInfos.Add(ai.addr, ai);
                        }

                        if ((i.opCode.flags & OpCodeFlags.RefAddr) != 0)
                        {
                            // Known referenced data address
                            ai.DataReferences.Add(i);
                        }

                        if ((i.opCode.flags & OpCodeFlags.Jumps) != 0)
                        {
                            // Known referenced code address
                            ai.CodeReferences.Add(i);
                        }

                        if ((i.opCode.flags & (OpCodeFlags.Jumps | OpCodeFlags.RefAddr)) == 0)
                        {
                            // Potential address
                            ai.PotentialReferences.Add(i);
                        }
                    }

                    // Is it a port reference?
                    if (i.opCode != null && (i.opCode.flags & OpCodeFlags.PortRef) != 0)
                    {
                        // Which port (-1, referenced through a register)
                        int port = -1;
                        if (i.byte_val.HasValue)
                            port = i.byte_val.Value;

                        // Get the port info
                        PortInfo pi;
                        if (!portInfos.TryGetValue(port, out pi))
                        {
                            pi = new PortInfo(port);
                            portInfos.Add(port, pi);
                        }

                        pi.References.Add(i);
                    }
                }

                if (w is TabbedTextWriter)
                    ((TabbedTextWriter)w).TabStops = new int[] { 8, 16, 24, 32 };

                // Build a list of all external references
                var extRefs = addressInfos.Values.Where(x => x.addr < _start || x.addr >= _start + _len).OrderBy(x => x.addr);
                foreach (var r in extRefs)
                {
                    if (r.DataReferences.Count > 0 || r.CodeReferences.Count > 0)
                    {
                        w.WriteLine("\nreferences to external address {0}:", Disassembler.FormatAddr((ushort)r.addr, true, false));

                        foreach (var i in (r.DataReferences.Concat(r.CodeReferences).Concat(r.PotentialReferences)).OrderBy(x => x.addr))
                        {
                            w.WriteLine("\t{0} {1}", Disassembler.FormatAddr(i.addr, true, false), i.Asm);
                        }

                    }
                }

                foreach (var r in addressInfos.Values.Where(x => (x.addr >= _start && x.addr < _start + _len) && x.PotentialReferences.Count > 0).OrderBy(x => x.addr))
                {
                    w.WriteLine("\npossible references to internal address {0}:", Disassembler.FormatAddr((ushort)r.addr, true, false));
                    ListPotentialAddresses(w, r);
                }

                foreach (var r in addressInfos.Values.Where(x => (x.addr < _start || x.addr >= _start + _len) && x.PotentialReferences.Count > 0).OrderBy(x => x.addr))
                {
                    w.WriteLine("\npossible references to external address {0}:", Disassembler.FormatAddr((ushort)r.addr, true, false));
                    ListPotentialAddresses(w, r);
                }

                foreach (var r in portInfos.Values.OrderBy(x => x.port))
                {
                    if (r.port == -1)
                    {
                        w.WriteLine("\nport references through a register:");
                    }
                    else
                    {
                        w.WriteLine("\nreferences to port {0}", Disassembler.FormatByte((byte)r.port));
                    }

                    foreach (var i in r.References.OrderBy(x => x.opCode.mnemonic[0]).ThenBy(x => x.addr))
                    {
                        w.WriteLine("\t{0} {1}", Disassembler.FormatAddr(i.addr, true, false), i.Asm);
                    }

                }

                // Dump all procs
                w.WriteLine("\nProcedures ({0}):", procs.Count);
                w.WriteLine("  Proc  Length  References Dependants");
                foreach (var p in procs.Values.OrderBy(x=>x.firstInstruction.addr))
                {
                    w.WriteLine("  {0}  {1:X4}   {2,10} {3,10}", Disassembler.FormatAddr(p.firstInstruction.addr, true, true), p.lengthInBytes, p.firstInstruction.referencedFrom == null ? 0 : p.firstInstruction.referencedFrom.Count, p.dependants.Count);
                }

                // Dump call graph
                w.WriteLine("\nCall Graph:");

                List<int> CallStack = new List<int>();
                Action<int> DumpCallGraph = null;
                DumpCallGraph = delegate(int addr)
                {
                    // Display the proc address, indented
                    w.Write("{0}{1}", new String(' ', CallStack.Count * 2), Disassembler.FormatAddr((ushort)addr, true, true));

                    if (CallStack.Count == 0)
                    {
                        w.Write(" - Entry Point");
                    }

                    // Is it a recursive call?
                    if (CallStack.Contains(addr))
                    {
                        w.WriteLine(" - Recursive");
                        return;
                    }

                    // Is it external?
                    Proc p;
                    if (!procs.TryGetValue(addr, out p))
                    {
                        w.WriteLine(" - External");
                        return;
                    }

                    // Dump dependants
                    w.WriteLine();
                    CallStack.Add(addr);
                    foreach (var d in p.dependants)
                    {
                        DumpCallGraph(d);
                    }
                    CallStack.RemoveAt(CallStack.Count - 1);
                };

                foreach (var ep in _entryPoints.OrderBy(x => x))
                {
                    // Get the proc's entry point
                    DumpCallGraph(ep);
                }

            }

            if (_htmlMode)
            {
                w.WriteLine("</code></pre>");
                w.WriteLine("</body>");
                w.WriteLine("</head>");
            }

            // Close file
            w.Close();
            if (_outputFile != null)
            {
                targetWriter.Close();
            }

            // Open the file for browsing?
            if (_autoOpen && _outputFile!=null)
            {
                Process.Start(_outputFile);
            }

            return 0;
        }
示例#3
0
        public static Instruction Disassemble(byte[] buffer, ushort offsetInBuffer, ushort addr)
        {
            Func<byte> readByte = () =>
            {
                addr++;
                return (byte)(offsetInBuffer<buffer.Length ? buffer[offsetInBuffer++] : 0);
            };

            var i = new Instruction();
            i.addr = addr;

            var start_addr = addr;
            byte opc = readByte();
            byte disp_u = 0;

            ushort jump_addr = 0;
            bool have_jump_addr = false;

            OpCode dasm = null;
            bool have_disp = false;

            switch(opc)
            {
                case 0xDD:
                case 0xFD:
                    byte next = readByte();
                    if((next | 0x20) == 0xFD || next == 0xED)
                    {
                        i.Asm = "NOP*";
                        i.t_states=4;
                        dasm=null;
                    }
                    else if(next == 0xCB)
                    {
                        disp_u = readByte();
                        next = readByte();

                        dasm = (opc==0xDD)? OpCodes.dasm_ddcb[next]: OpCodes.dasm_fdcb[next];
                        have_disp=true;
                    }
                    else
                    {
                        dasm = (opc==0xDD)? OpCodes.dasm_dd[next]: OpCodes.dasm_fd[next];
                        if(dasm.mnemonic == null) //mirrored instructions
                        {
                            dasm = OpCodes.dasm_base[next];
                            i.t_states=4;
                            i.t_states2=4;
                        }
                    }
                    break;

                case 0xED:
                    next = readByte();
                    dasm = OpCodes.dasm_ed[next];
                    if(dasm.mnemonic == null)
                    {
                        i.Asm = "NOP*";
                        i.t_states=8;
                        dasm=null;
                    }
                    break;

                case 0xCB:
                    next = readByte();
                    dasm = OpCodes.dasm_cb[next];
                    break;

                default:
                    dasm = OpCodes.dasm_base[opc];
                    break;
            }

            if (dasm != null)
            {
                i.opCode = dasm;

                var sb = new StringBuilder();

                foreach (var ch in LowerCase ? dasm.mnemonic.ToLower() : dasm.mnemonic)
                {
                    switch (ch)
                    {

                        case '@':
                            {
                                var lo = readByte();
                                var hi = readByte();
                                ushort val = (ushort)(lo + hi * 0x100);

                                if ((dasm.flags & (OpCodeFlags.RefAddr | OpCodeFlags.Jumps)) != 0)
                                    sb.Append(FormatAddr(val));
                                else
                                    sb.Append(FormatWord(val));

                                i.word_val = val;

                                jump_addr = val;
                                have_jump_addr = true;
                                break;
                            }

                        case '$':
                        case '%':
                            {
                                if (!have_disp)
                                    disp_u = readByte();
                                var disp = (disp_u & 0x80) != 0 ? -(((~disp_u) & 0x7f) + 1) : disp_u;

                                if (ShowRelativeOffsets)
                                {
                                    if (disp > 0)
                                    {
                                        i.Comment = string.Format("+{0}", disp);
                                    }
                                    else
                                    {
                                        i.Comment = string.Format("{0}", disp);
                                    }
                                }

                                if (ch == '$')
                                    sb.Append(FormatByte((byte)disp));
                                else
                                {
                                    jump_addr = (ushort)(addr + disp);
                                    have_jump_addr = true;
                                    sb.Append(FormatAddr(jump_addr));
                                }

                                break;
                            }

                        case '#':
                            {
                                var lo = readByte();
                                sb.Append(FormatByte(lo));
                                if (lo>=0x20 && lo<=0x7f)
                                    i.Comment = string.Format("'{0}'", (char)lo);
                                i.byte_val = lo;
                                break;
                            }

                        default:
                            sb.Append(ch);
                            break;
                    }

                }

                i.Asm = sb.ToString();

                i.t_states += dasm.t_states;
                i.t_states2 += dasm.t_states2;

                // Return continue address
                if ((dasm.flags & OpCodeFlags.Continues) != 0)
                    i.next_addr_1 = addr;

                // Return jump target address (if have it)
                if ((dasm.flags & OpCodeFlags.Jumps) != 0 && have_jump_addr)
                {
                    i.next_addr_2 = jump_addr;
                }
            }
            else
            {
                i.next_addr_1 = addr;
            }

            if (i.t_states == i.t_states2)
                i.t_states2 = 0;

            i.bytes = (ushort)(addr - start_addr);
            return i;
        }