From 47b136373858a74121ce70ac9f9828a823ebbe76 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Thu, 11 Apr 2019 16:23:02 -0700 Subject: [PATCH] Add more detail to cross references In the cross-reference table we now indicate whether the reference source is doing a read, write, read-modify-write, branch, subroutine call, is just referencing the address, or is part of the data. --- Asm65/OpDef.cs | 256 ++++++++++++++++++------ SourceGen/AppForms/ProjectView.cs | 32 ++- SourceGen/AsmGen/AsmMerlin32.cs | 7 +- SourceGen/CodeAnalysis.cs | 2 +- SourceGen/DataAnalysis.cs | 11 +- SourceGen/DisasmProject.cs | 35 ++-- SourceGen/RuntimeData/Help/codegen.html | 10 +- SourceGen/RuntimeData/Help/mainwin.html | 18 +- SourceGen/SymbolTable.cs | 4 +- SourceGen/XrefSet.cs | 23 ++- 10 files changed, 303 insertions(+), 95 deletions(-) diff --git a/Asm65/OpDef.cs b/Asm65/OpDef.cs index 4edd2cd..37a1f38 100644 --- a/Asm65/OpDef.cs +++ b/Asm65/OpDef.cs @@ -33,7 +33,18 @@ namespace Asm65 { NoCont, // RTS, BRK, ... (jump to new address, not specified in operand) CallSubroutine, // JSR, JSL (jump to new address, and also continue to next) ConditionalBranch // BCC, BEQ, ... (jump to new address and/or continue to next) - }; + } + + /// + /// Effect of executing an instruction on memory. + /// + public enum MemoryEffect { + Unknown = 0, + None, // e.g. TAX, PEA addr, LDA #imm + Read, // e.g. LDA addr + Write, // e.g. STA addr + ReadModifyWrite // e.g. LSR addr + } /// /// Addressing mode. This uses the same distinctions as Eyes & Lichty, which for @@ -160,6 +171,22 @@ namespace Asm65 { /// public FlowEffect Effect { get; private set; } + /// + /// Effect this instruction has on memory. + /// + public MemoryEffect MemEffect { + get { + // If we do this a lot, we should probably just go through and set the + // mem effect to "none" in all the immediate-mode op definitions. + if (IsImmediate) { + return MemoryEffect.None; + } else { + return BaseMemEffect; + } + } + } + private MemoryEffect BaseMemEffect { get; set; } + /// /// Cycles required. The low 8 bits hold the base cycle count, the remaining bits /// are defined by the CycleMod enum. @@ -221,6 +248,7 @@ namespace Asm65 { this.Mnemonic = src.Mnemonic; this.FlagsAffected = src.FlagsAffected; this.Effect = src.Effect; + this.BaseMemEffect = src.BaseMemEffect; this.CycDef = src.CycDef; this.IsOperandWidthUnambiguous = src.IsOperandWidthUnambiguous; this.StatusFlagUpdater = src.StatusFlagUpdater; @@ -247,10 +275,10 @@ namespace Asm65 { /// - /// True if this operation is a branch instruction (conditional branch, + /// True if this operation is any type of branch instruction (conditional branch, /// unconditional branch/jump, subroutine call). /// - public bool IsBranch { + public bool IsBranchOrSubCall { get { return Effect == FlowEffect.Branch || Effect == FlowEffect.ConditionalBranch || @@ -258,6 +286,15 @@ namespace Asm65 { } } + /// + /// True if this operation is a subroutine call. + /// + public bool IsSubroutineCall { + get { + return Effect == FlowEffect.CallSubroutine; + } + } + /// /// True if the operand is an immediate value, which should be prefixed with '#'. /// @@ -270,11 +307,11 @@ namespace Asm65 { } /// - /// True if the operand is an "extended immediate" value, which includes PEA - /// as well as Imm. + /// True if the operand is an "extended immediate" value, which includes PEA in + /// addition to Imm/ImmLongA/ImmLongXY. /// public bool IsExtendedImmediate { - get { // should this include PER as well? + get { return IsImmediate || AddrMode == AddressMode.StackAbs; } } @@ -748,24 +785,28 @@ namespace Asm65 { private static OpDef OpADC = new OpDef() { Mnemonic = OpName.ADC, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Read, FlagsAffected = FlagsAffected_NVZC, StatusFlagUpdater = FlagUpdater_NVZC }; private static OpDef OpAND = new OpDef() { Mnemonic = OpName.AND, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Read, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ // special handling for imm }; private static OpDef OpASL = new OpDef() { Mnemonic = OpName.ASL, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.ReadModifyWrite, FlagsAffected = FlagsAffected_NZC, StatusFlagUpdater = FlagUpdater_NZC }; private static OpDef OpBCC = new OpDef() { Mnemonic = OpName.BCC, Effect = FlowEffect.ConditionalBranch, + BaseMemEffect = MemoryEffect.None, StatusFlagUpdater = delegate(StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { condBranchTakenFlags.C = 0; @@ -776,6 +817,7 @@ namespace Asm65 { private static OpDef OpBCS = new OpDef() { Mnemonic = OpName.BCS, Effect = FlowEffect.ConditionalBranch, + BaseMemEffect = MemoryEffect.None, StatusFlagUpdater = delegate (StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { condBranchTakenFlags.C = 1; @@ -786,6 +828,7 @@ namespace Asm65 { private static OpDef OpBEQ = new OpDef() { Mnemonic = OpName.BEQ, Effect = FlowEffect.ConditionalBranch, + BaseMemEffect = MemoryEffect.None, StatusFlagUpdater = delegate (StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { condBranchTakenFlags.Z = 1; @@ -796,12 +839,14 @@ namespace Asm65 { private static OpDef OpBIT = new OpDef() { Mnemonic = OpName.BIT, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZC, // special handling for imm StatusFlagUpdater = FlagUpdater_NZC // special handling for imm }; private static OpDef OpBMI = new OpDef() { Mnemonic = OpName.BMI, Effect = FlowEffect.ConditionalBranch, + BaseMemEffect = MemoryEffect.None, StatusFlagUpdater = delegate (StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { condBranchTakenFlags.N = 1; @@ -812,6 +857,7 @@ namespace Asm65 { private static OpDef OpBNE = new OpDef() { Mnemonic = OpName.BNE, Effect = FlowEffect.ConditionalBranch, + BaseMemEffect = MemoryEffect.None, StatusFlagUpdater = delegate (StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { condBranchTakenFlags.Z = 0; @@ -822,6 +868,7 @@ namespace Asm65 { private static OpDef OpBPL = new OpDef() { Mnemonic = OpName.BPL, Effect = FlowEffect.ConditionalBranch, + BaseMemEffect = MemoryEffect.None, StatusFlagUpdater = delegate (StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { condBranchTakenFlags.N = 0; @@ -831,19 +878,23 @@ namespace Asm65 { }; private static OpDef OpBRA = new OpDef() { Mnemonic = OpName.BRA, - Effect = FlowEffect.Branch + Effect = FlowEffect.Branch, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpBRK = new OpDef() { Mnemonic = OpName.BRK, - Effect = FlowEffect.NoCont + Effect = FlowEffect.NoCont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpBRL = new OpDef() { Mnemonic = OpName.BRL, - Effect = FlowEffect.Branch + Effect = FlowEffect.Branch, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpBVC = new OpDef() { Mnemonic = OpName.BVC, Effect = FlowEffect.ConditionalBranch, + BaseMemEffect = MemoryEffect.None, StatusFlagUpdater = delegate (StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { condBranchTakenFlags.V = 0; @@ -854,6 +905,7 @@ namespace Asm65 { private static OpDef OpBVS = new OpDef() { Mnemonic = OpName.BVS, Effect = FlowEffect.ConditionalBranch, + BaseMemEffect = MemoryEffect.None, StatusFlagUpdater = delegate (StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { condBranchTakenFlags.V = 1; @@ -864,6 +916,7 @@ namespace Asm65 { private static OpDef OpCLC = new OpDef() { Mnemonic = OpName.CLC, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_C, StatusFlagUpdater = delegate (StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { @@ -874,6 +927,7 @@ namespace Asm65 { private static OpDef OpCLD = new OpDef() { Mnemonic = OpName.CLD, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_D, StatusFlagUpdater = delegate (StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { @@ -884,6 +938,7 @@ namespace Asm65 { private static OpDef OpCLI = new OpDef() { Mnemonic = OpName.CLI, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_I, StatusFlagUpdater = delegate (StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { @@ -894,6 +949,7 @@ namespace Asm65 { private static OpDef OpCLV = new OpDef() { Mnemonic = OpName.CLV, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_V, StatusFlagUpdater = delegate (StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { @@ -904,246 +960,293 @@ namespace Asm65 { private static OpDef OpCMP = new OpDef() { Mnemonic = OpName.CMP, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Read, FlagsAffected = FlagsAffected_NZC, StatusFlagUpdater = FlagUpdater_NZC }; private static OpDef OpCOP = new OpDef() { Mnemonic = OpName.COP, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpCPX = new OpDef() { Mnemonic = OpName.CPX, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Read, FlagsAffected = FlagsAffected_NZC, StatusFlagUpdater = FlagUpdater_NZC }; private static OpDef OpCPY = new OpDef() { Mnemonic = OpName.CPY, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Read, FlagsAffected = FlagsAffected_NZC, StatusFlagUpdater = FlagUpdater_NZC }; private static OpDef OpDEC = new OpDef() { Mnemonic = OpName.DEC, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.ReadModifyWrite, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpDEX = new OpDef() { Mnemonic = OpName.DEX, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpDEY = new OpDef() { Mnemonic = OpName.DEY, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpEOR = new OpDef() { Mnemonic = OpName.EOR, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Read, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpINC = new OpDef() { Mnemonic = OpName.INC, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.ReadModifyWrite, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpINX = new OpDef() { Mnemonic = OpName.INX, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpINY = new OpDef() { Mnemonic = OpName.INY, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpJML = new OpDef() { Mnemonic = OpName.JML, // technically JMP with long operand, but JML is convention Effect = FlowEffect.Branch, + BaseMemEffect = MemoryEffect.None, IsOperandWidthUnambiguous = true, }; private static OpDef OpJMP = new OpDef() { Mnemonic = OpName.JMP, Effect = FlowEffect.Branch, + BaseMemEffect = MemoryEffect.None, IsOperandWidthUnambiguous = true }; private static OpDef OpJSL = new OpDef() { Mnemonic = OpName.JSL, // technically JSR with long operand, but JSL is convention Effect = FlowEffect.CallSubroutine, + BaseMemEffect = MemoryEffect.None, IsOperandWidthUnambiguous = true, StatusFlagUpdater = FlagUpdater_Subroutine }; private static OpDef OpJSR = new OpDef() { Mnemonic = OpName.JSR, Effect = FlowEffect.CallSubroutine, + BaseMemEffect = MemoryEffect.None, IsOperandWidthUnambiguous = true, StatusFlagUpdater = FlagUpdater_Subroutine }; private static OpDef OpLDA = new OpDef() { Mnemonic = OpName.LDA, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Read, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ // special handling for imm }; private static OpDef OpLDX = new OpDef() { Mnemonic = OpName.LDX, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Read, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ // special handling for imm }; private static OpDef OpLDY = new OpDef() { Mnemonic = OpName.LDY, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Read, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ // special handling for imm }; private static OpDef OpLSR = new OpDef() { Mnemonic = OpName.LSR, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.ReadModifyWrite, FlagsAffected = FlagsAffected_NZC, StatusFlagUpdater = FlagUpdater_NZC }; private static OpDef OpMVN = new OpDef() { Mnemonic = OpName.MVN, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None // not quite right, but what is? }; private static OpDef OpMVP = new OpDef() { Mnemonic = OpName.MVP, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpNOP = new OpDef() { Mnemonic = OpName.NOP, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpORA = new OpDef() { Mnemonic = OpName.ORA, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Read, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ // special handling for imm }; private static OpDef OpPEA = new OpDef() { Mnemonic = OpName.PEA, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpPEI = new OpDef() { Mnemonic = OpName.PEI, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Read }; private static OpDef OpPER = new OpDef() { Mnemonic = OpName.PER, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpPHA = new OpDef() { Mnemonic = OpName.PHA, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpPHB = new OpDef() { Mnemonic = OpName.PHB, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpPHD = new OpDef() { Mnemonic = OpName.PHD, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpPHK = new OpDef() { Mnemonic = OpName.PHK, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpPHP = new OpDef() { Mnemonic = OpName.PHP, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpPHX = new OpDef() { Mnemonic = OpName.PHX, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpPHY = new OpDef() { Mnemonic = OpName.PHY, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpPLA = new OpDef() { Mnemonic = OpName.PLA, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpPLB = new OpDef() { Mnemonic = OpName.PLB, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpPLD = new OpDef() { Mnemonic = OpName.PLD, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpPLP = new OpDef() { Mnemonic = OpName.PLP, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_All, StatusFlagUpdater = FlagUpdater_PLP }; private static OpDef OpPLX = new OpDef() { Mnemonic = OpName.PLX, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpPLY = new OpDef() { Mnemonic = OpName.PLY, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpREP = new OpDef() { Mnemonic = OpName.REP, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_All, StatusFlagUpdater = FlagUpdater_REP }; private static OpDef OpROL = new OpDef() { Mnemonic = OpName.ROL, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.ReadModifyWrite, FlagsAffected = FlagsAffected_NZC, StatusFlagUpdater = FlagUpdater_ROL }; private static OpDef OpROR = new OpDef() { Mnemonic = OpName.ROR, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.ReadModifyWrite, FlagsAffected = FlagsAffected_NZC, StatusFlagUpdater = FlagUpdater_ROR }; private static OpDef OpRTI = new OpDef() { Mnemonic = OpName.RTI, - Effect = FlowEffect.NoCont + Effect = FlowEffect.NoCont, + BaseMemEffect = MemoryEffect.None, }; private static OpDef OpRTL = new OpDef() { Mnemonic = OpName.RTL, - Effect = FlowEffect.NoCont + Effect = FlowEffect.NoCont, + BaseMemEffect = MemoryEffect.None, }; private static OpDef OpRTS = new OpDef() { Mnemonic = OpName.RTS, - Effect = FlowEffect.NoCont + Effect = FlowEffect.NoCont, + BaseMemEffect = MemoryEffect.None, }; private static OpDef OpSBC = new OpDef() { Mnemonic = OpName.SBC, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Read, FlagsAffected = FlagsAffected_NVZC, StatusFlagUpdater = FlagUpdater_NVZC }; private static OpDef OpSEC = new OpDef() { Mnemonic = OpName.SEC, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_C, StatusFlagUpdater = delegate (StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { @@ -1154,6 +1257,7 @@ namespace Asm65 { private static OpDef OpSED = new OpDef() { Mnemonic = OpName.SED, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_D, StatusFlagUpdater = delegate (StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { @@ -1164,6 +1268,7 @@ namespace Asm65 { private static OpDef OpSEI = new OpDef() { Mnemonic = OpName.SEI, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_I, StatusFlagUpdater = delegate (StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags) { @@ -1174,126 +1279,150 @@ namespace Asm65 { private static OpDef OpSEP = new OpDef() { Mnemonic = OpName.SEP, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_All, StatusFlagUpdater = FlagUpdater_SEP }; private static OpDef OpSTA = new OpDef() { Mnemonic = OpName.STA, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Write, }; private static OpDef OpSTP = new OpDef() { Mnemonic = OpName.STP, - Effect = FlowEffect.NoCont + Effect = FlowEffect.NoCont, + BaseMemEffect = MemoryEffect.None, }; private static OpDef OpSTX = new OpDef() { Mnemonic = OpName.STX, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Write, }; private static OpDef OpSTY = new OpDef() { Mnemonic = OpName.STY, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Write, }; private static OpDef OpSTZ = new OpDef() { Mnemonic = OpName.STZ, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Write, }; private static OpDef OpTAX = new OpDef() { Mnemonic = OpName.TAX, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpTAY = new OpDef() { Mnemonic = OpName.TAY, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpTCD = new OpDef() { Mnemonic = OpName.TCD, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpTCS = new OpDef() { Mnemonic = OpName.TCS, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpTDC = new OpDef() { Mnemonic = OpName.TDC, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpTRB = new OpDef() { Mnemonic = OpName.TRB, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.ReadModifyWrite, FlagsAffected = FlagsAffected_Z, StatusFlagUpdater = FlagUpdater_Z }; private static OpDef OpTSB = new OpDef() { Mnemonic = OpName.TSB, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.ReadModifyWrite, FlagsAffected = FlagsAffected_Z, StatusFlagUpdater = FlagUpdater_Z }; private static OpDef OpTSC = new OpDef() { Mnemonic = OpName.TSC, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpTSX = new OpDef() { Mnemonic = OpName.TSX, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpTXA = new OpDef() { Mnemonic = OpName.TXA, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpTXS = new OpDef() { Mnemonic = OpName.TXS, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpTXY = new OpDef() { Mnemonic = OpName.TXY, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpTYA = new OpDef() { Mnemonic = OpName.TYA, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpTYX = new OpDef() { Mnemonic = OpName.TYX, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpWAI = new OpDef() { Mnemonic = OpName.WAI, - Effect = FlowEffect.Cont // when I=1 (interrupts disabled), continues on interrupt + Effect = FlowEffect.Cont, // when I=1 (interrupts disabled), continues on interrupt + BaseMemEffect = MemoryEffect.None }; private static OpDef OpWDM = new OpDef() { Mnemonic = OpName.WDM, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpXBA = new OpDef() { Mnemonic = OpName.XBA, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; private static OpDef OpXCE = new OpDef() { Mnemonic = OpName.XCE, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_C, StatusFlagUpdater = FlagUpdater_XCE }; @@ -2698,18 +2827,21 @@ namespace Asm65 { IsUndocumented = true, Mnemonic = OpName.ANC, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZC, StatusFlagUpdater = FlagUpdater_NZC }; private static OpDef OpANE = new OpDef() { IsUndocumented = true, Mnemonic = OpName.ANE, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpALR = new OpDef() { IsUndocumented = true, Mnemonic = OpName.ALR, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZC, StatusFlagUpdater = FlagUpdater_NZC }; @@ -2717,6 +2849,7 @@ namespace Asm65 { IsUndocumented = true, Mnemonic = OpName.ARR, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NVZC, StatusFlagUpdater = FlagUpdater_NVZC }; @@ -2724,23 +2857,27 @@ namespace Asm65 { IsUndocumented = true, Mnemonic = OpName.DCP, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.ReadModifyWrite, FlagsAffected = FlagsAffected_C, StatusFlagUpdater = FlagUpdater_C }; - private static OpDef OpDOP = new OpDef() { + private static OpDef OpDOP = new OpDef() { // double-byte NOP IsUndocumented = true, Mnemonic = OpName.DOP, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpJAM = new OpDef() { IsUndocumented = true, Mnemonic = OpName.JAM, - Effect = FlowEffect.NoCont + Effect = FlowEffect.NoCont, + BaseMemEffect = MemoryEffect.None }; private static OpDef OpISC = new OpDef() { IsUndocumented = true, Mnemonic = OpName.ISC, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.ReadModifyWrite, FlagsAffected = FlagsAffected_NVZC, StatusFlagUpdater = FlagUpdater_NVZC }; @@ -2748,6 +2885,7 @@ namespace Asm65 { IsUndocumented = true, Mnemonic = OpName.LAS, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Read, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; @@ -2755,20 +2893,15 @@ namespace Asm65 { IsUndocumented = true, Mnemonic = OpName.LAX, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Read, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; - //private static OpDef OpLXA = new OpDef() { - // IsUndocumented = true, - // Mnemonic = OpName.LXA, - // Effect = FlowEffect.Cont, - // FlagsAffected = FlagsAffected_NZ, - // StatusFlagUpdater = FlagUpdater_NZ - //}; private static OpDef OpRLA = new OpDef() { IsUndocumented = true, Mnemonic = OpName.RLA, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.ReadModifyWrite, FlagsAffected = FlagsAffected_NZC, StatusFlagUpdater = FlagUpdater_NZC }; @@ -2776,6 +2909,7 @@ namespace Asm65 { IsUndocumented = true, Mnemonic = OpName.RRA, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.ReadModifyWrite, FlagsAffected = FlagsAffected_NVZC, StatusFlagUpdater = FlagUpdater_NVZC }; @@ -2783,6 +2917,7 @@ namespace Asm65 { IsUndocumented = true, Mnemonic = OpName.SAX, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Write, FlagsAffected = FlagsAffected_NZ, StatusFlagUpdater = FlagUpdater_NZ }; @@ -2790,33 +2925,33 @@ namespace Asm65 { IsUndocumented = true, Mnemonic = OpName.SBX, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None, FlagsAffected = FlagsAffected_NZC, StatusFlagUpdater = FlagUpdater_NZC }; private static OpDef OpSHA = new OpDef() { IsUndocumented = true, Mnemonic = OpName.SHA, - Effect = FlowEffect.Cont - }; - private static OpDef OpTAS = new OpDef() { - IsUndocumented = true, - Mnemonic = OpName.TAS, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Write }; private static OpDef OpSHX = new OpDef() { IsUndocumented = true, Mnemonic = OpName.SHX, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Write }; private static OpDef OpSHY = new OpDef() { IsUndocumented = true, Mnemonic = OpName.SHY, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Write }; private static OpDef OpSLO = new OpDef() { IsUndocumented = true, Mnemonic = OpName.SLO, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.ReadModifyWrite, FlagsAffected = FlagsAffected_NZC, StatusFlagUpdater = FlagUpdater_NZC }; @@ -2824,14 +2959,23 @@ namespace Asm65 { IsUndocumented = true, Mnemonic = OpName.SRE, Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.ReadModifyWrite, FlagsAffected = FlagsAffected_NZC, StatusFlagUpdater = FlagUpdater_NZC }; - private static OpDef OpTOP = new OpDef() { + private static OpDef OpTAS = new OpDef() { + IsUndocumented = true, + Mnemonic = OpName.TAS, + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.Write + }; + private static OpDef OpTOP = new OpDef() { // triple-byte NOP IsUndocumented = true, Mnemonic = OpName.TOP, - Effect = FlowEffect.Cont + Effect = FlowEffect.Cont, + BaseMemEffect = MemoryEffect.None }; + public static readonly OpDef OpSLO_DPIndexXInd = new OpDef(OpSLO) { Opcode = 0x03, AddrMode = AddressMode.DPIndexXInd, diff --git a/SourceGen/AppForms/ProjectView.cs b/SourceGen/AppForms/ProjectView.cs index a1d22d8..3ddb606 100644 --- a/SourceGen/AppForms/ProjectView.cs +++ b/SourceGen/AppForms/ProjectView.cs @@ -4003,6 +4003,7 @@ namespace SourceGen.AppForms { return; } + // TODO(someday): localization Asm65.Formatter formatter = mOutputFormatter; bool showBank = !mProject.CpuDef.HasAddr16; for (int i = 0; i < xrefs.Count; i++) { @@ -4011,15 +4012,36 @@ namespace SourceGen.AppForms { string typeStr; switch (xr.Type) { - case XrefSet.XrefType.BranchOperand: + case XrefSet.XrefType.SubCallOp: + typeStr = "call "; + break; + case XrefSet.XrefType.BranchOp: typeStr = "branch "; break; - case XrefSet.XrefType.InstrOperand: - typeStr = "instr "; - break; - case XrefSet.XrefType.DataOperand: + case XrefSet.XrefType.RefFromData: typeStr = "data "; break; + case XrefSet.XrefType.MemAccessOp: + switch (xr.AccType) { + case OpDef.MemoryEffect.Read: + typeStr = "read "; + break; + case OpDef.MemoryEffect.Write: + typeStr = "write "; + break; + case OpDef.MemoryEffect.ReadModifyWrite: + typeStr = "rmw "; + break; + case OpDef.MemoryEffect.None: // e.g. LDA # + /// Given a reference from srcOffset to targetOffset, check to see if there's a + /// nearby location that we'd prefer to refer to. For example, if targetOffset points + /// into the middle of an instruction, we'd rather have it refer to the first byte. + /// + /// Reference source. + /// Reference target. + /// New value for targetOffset, or original value if nothing better was + /// found. private int FindAlternateTarget(int srcOffset, int targetOffset) { int origTargetOffset = targetOffset; @@ -393,7 +402,7 @@ namespace SourceGen { // Source is an instruction, so we have an instruction referencing an instruction. // Could be a branch, an address push, or self-modifying code. OpDef op = mProject.CpuDef.GetOpDef(mProject.FileData[srcOffset]); - if (op.IsBranch) { + if (op.IsBranchOrSubCall) { // Don't mess with jumps and branches -- always go directly to the // target address. } else if (op == OpDef.OpPEA_StackAbs || op == OpDef.OpPER_StackPCRelLong) { diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs index b30df98..338531f 100644 --- a/SourceGen/DisasmProject.cs +++ b/SourceGen/DisasmProject.cs @@ -478,7 +478,8 @@ namespace SourceGen { reanalysisTimer.EndTask("GenerateXrefs"); reanalysisTimer.StartTask("GenerateActiveDefSymbolList"); - // Generate the list of project/platform symbols that are being used. + // Generate the list of project/platform symbols that are being used. This forms + // the list of EQUates at the top of the file. GenerateActiveDefSymbolList(); reanalysisTimer.EndTask("GenerateActiveDefSymbolList"); @@ -792,15 +793,19 @@ namespace SourceGen { Anattrib attr = mAnattribs[offset]; XrefSet.XrefType xrefType = XrefSet.XrefType.Unknown; + OpDef.MemoryEffect accType = OpDef.MemoryEffect.Unknown; if (attr.IsInstruction) { OpDef op = CpuDef.GetOpDef(FileData[offset]); - if (op.IsBranch) { - xrefType = XrefSet.XrefType.BranchOperand; + if (op.IsSubroutineCall) { + xrefType = XrefSet.XrefType.SubCallOp; + } else if (op.IsBranchOrSubCall) { + xrefType = XrefSet.XrefType.BranchOp; } else { - xrefType = XrefSet.XrefType.InstrOperand; + xrefType = XrefSet.XrefType.MemAccessOp; + accType = op.MemEffect; } } else if (attr.IsData || attr.IsInlineData) { - xrefType = XrefSet.XrefType.DataOperand; + xrefType = XrefSet.XrefType.RefFromData; } bool hasZeroOffsetSym = false; @@ -825,7 +830,8 @@ namespace SourceGen { mAnattribs[operandOffset].Address; } - AddXref(symOffset, new XrefSet.Xref(offset, true, xrefType, adj)); + AddXref(symOffset, + new XrefSet.Xref(offset, true, xrefType, accType, adj)); if (adj == 0) { hasZeroOffsetSym = true; } @@ -838,7 +844,8 @@ namespace SourceGen { if (operandOffset >= 0) { adj = defSym.Value - operandOffset; } - defSym.Xrefs.Add(new XrefSet.Xref(offset, true, xrefType, adj)); + defSym.Xrefs.Add( + new XrefSet.Xref(offset, true, xrefType, accType, adj)); } else { Debug.WriteLine("NOTE: not xrefing '" + sym.Label + "'"); Debug.Assert(false); // not possible? @@ -849,7 +856,8 @@ namespace SourceGen { Debug.Assert(attr.IsData || attr.IsInlineData); int operandOffset = RawData.GetWord(mFileData, offset, dfd.Length, dfd.FormatType == FormatDescriptor.Type.NumericBE); - AddXref(operandOffset, new XrefSet.Xref(offset, false, xrefType, 0)); + AddXref(operandOffset, + new XrefSet.Xref(offset, false, xrefType, accType, 0)); } // Look for instruction offset references. We skip this if we've already @@ -857,7 +865,8 @@ namespace SourceGen { // just leave a duplicate entry. (The symbolic ref wins because we need // it for the label localizer and possibly the label refactorer.) if (!hasZeroOffsetSym && attr.IsInstructionStart && attr.OperandOffset >= 0) { - AddXref(attr.OperandOffset, new XrefSet.Xref(offset, false, xrefType, 0)); + AddXref(attr.OperandOffset, + new XrefSet.Xref(offset, false, xrefType, accType, 0)); } } @@ -1492,13 +1501,15 @@ namespace SourceGen { } /// - /// Updates all symbolic references to the old label. + /// Updates all symbolic references to the old label. Call this after replacing + /// mAnattribs[labelOffset].Symbol. /// /// Offset with the just-renamed label. /// Previous value. private void RefactorLabel(int labelOffset, string oldLabel) { if (!mXrefs.TryGetValue(labelOffset, out XrefSet xrefs)) { - // This can happen if you add a label in the middle of nowhere and rename it. + // This can happen if you add a label in a file section that nothing references, + // and then rename it. Debug.WriteLine("RefactorLabel: no references to " + oldLabel); return; } @@ -1548,7 +1559,7 @@ namespace SourceGen { continue; } - Debug.WriteLine("Replacing symbol at +" + xr.Offset.ToString("x6") + + Debug.WriteLine("Replacing OpFor symbol at +" + xr.Offset.ToString("x6") + " with " + newLabel); OperandFormats[xr.Offset] = FormatDescriptor.Create( dfd.Length, new WeakSymbolRef(newLabel, dfd.SymbolRef.ValuePart), diff --git a/SourceGen/RuntimeData/Help/codegen.html b/SourceGen/RuntimeData/Help/codegen.html index 68aacf3..56b11f0 100644 --- a/SourceGen/RuntimeData/Help/codegen.html +++ b/SourceGen/RuntimeData/Help/codegen.html @@ -127,7 +127,7 @@ code, but also needs to know how to handle the corner cases.

Bugs:

@@ -166,7 +166,6 @@ code, but also needs to know how to handle the corner cases.

  • PC relative branches don't wrap around at bank boundaries.
  • BRK <arg> is assembled to opcode $05 rather than $00.
  • WDM is not supported.
  • -
  • Source file names may not have spaces in them on Windows.
  • Quirks:

    @@ -178,8 +177,8 @@ code, but also needs to know how to handle the corner cases.

    more common interpretation would be label >> (8 - 16). (This is actually somewhat convenient, since none of the expressions SourceGen currently generates require parenthesis.) -
  • Undocumented opcodes: SBX ($cb) uses the mnemonic AXS. All other - opcodes match up with the "unintended opcodes" document.
  • +
  • Undocumented opcode SBX ($cb) uses the mnemonic AXS. All + other opcodes match up with the "unintended opcodes" document.
  • ca65 is implemented as a single-pass assembler, so label widths can't always be known in time. For example, if you use some zero-page labels, but they're defined via .ORG $0000 after the point where the @@ -224,7 +223,8 @@ code, but also needs to know how to handle the corner cases.

  • Values loaded into registers are implicitly mod 256 or 65536. There is no need to explicitly mask an expression.
  • The assembler tracks register widths when it sees SEP/REP instructions, - but doesn't attempt to track the emulation flag. So if you issue a REP + but doesn't attempt to track the emulation flag. So if you issue a + REP #$20 while in emulation mode, the assembler will incorrectly assume long registers. (Really I just want to be able to turn the width-tracking off, but there's no way to do that.)
  • diff --git a/SourceGen/RuntimeData/Help/mainwin.html b/SourceGen/RuntimeData/Help/mainwin.html index 9893541..c5dd301 100644 --- a/SourceGen/RuntimeData/Help/mainwin.html +++ b/SourceGen/RuntimeData/Help/mainwin.html @@ -216,10 +216,20 @@ For each reference, the file offset, address, and some details about the type of reference will be shown.

    The reference type indicates whether the origin is an instruction or -data operand. Branch instructions are called out separately. In -addition, it will be identified as a numeric or symbolic reference. -Symbolic references may be offset from the actual operand value; if this -is the case, the adjustment will be shown as well.

    +data operand, and provides an indication of the nature of the reference:

    + +

    In addition, the source will be identified as a symbolic ("Sym") or +numeric ("Num") reference. Symbolic references may be offset from the +actual operand value; if this is the case, the adjustment will be shown +as well.

    Double-clicking on a reference moves the code list selection to that reference, and adds the previous selection to the navigation stack.

    diff --git a/SourceGen/SymbolTable.cs b/SourceGen/SymbolTable.cs index d847ed9..1d4926f 100644 --- a/SourceGen/SymbolTable.cs +++ b/SourceGen/SymbolTable.cs @@ -215,7 +215,7 @@ namespace SourceGen { /// /// Generates a unique address symbol. Does not add the symbol to the list. /// - /// Address label will be applied to + /// Address that label will be applied to. /// Symbol table. /// Prefix to use; must start with a letter. /// Newly-created, unique symbol. @@ -239,7 +239,7 @@ namespace SourceGen { sb.Append(index); label = sb.ToString(); } while (index <= MAX_RENAME && symbols.TryGetValue(label, out unused)); - if (index == MAX_RENAME) { + if (index > MAX_RENAME) { // I give up throw new Exception("Too many identical symbols"); } diff --git a/SourceGen/XrefSet.cs b/SourceGen/XrefSet.cs index dc399e9..3fa602e 100644 --- a/SourceGen/XrefSet.cs +++ b/SourceGen/XrefSet.cs @@ -28,12 +28,16 @@ namespace SourceGen { public class XrefSet : IEnumerable { /// /// Reference type. This is mostly useful for display to the user. + /// + /// The enum is in priority order, i.e. the lowest-valued + /// item "wins" in situations where only one value is used. /// public enum XrefType { Unknown = 0, - InstrOperand, // generic instruction operand - BranchOperand, // branch instruction - DataOperand // e.g. ".dd2
    " + SubCallOp, // subroutine call + BranchOp, // branch instruction + RefFromData, // reference in data area, e.g. ".dd2
    " + MemAccessOp, // instruction that accesses memory, or refers to an address } /// @@ -41,7 +45,7 @@ namespace SourceGen { /// public class Xref { /// - /// Offset of start of instruction or data with the reference. + /// Offset of start of instruction or data that refers to the target offset. /// public int Offset { get; private set; } @@ -55,22 +59,29 @@ namespace SourceGen { /// public XrefType Type { get; private set; } + /// + /// For Type==MemAccessOp, what type of memory access is performed. + /// + public Asm65.OpDef.MemoryEffect AccType { get; private set; } + /// /// Adjustment to symbol. For example, "LDA label+2" adds an xref entry to /// "label", with an adjustment of +2. /// public int Adjustment { get; private set; } - public Xref(int offset, bool isSymbolic, XrefType type, int adjustment) { + public Xref(int offset, bool isSymbolic, XrefType type, + Asm65.OpDef.MemoryEffect accType, int adjustment) { Offset = offset; IsSymbolic = isSymbolic; Type = type; + AccType = accType; Adjustment = adjustment; } public override string ToString() { return "Xref off=+" + Offset.ToString("x6") + " sym=" + IsSymbolic + - " type=" + Type + " adj=" + Adjustment; + " type=" + Type + " accType= " + AccType + " adj=" + Adjustment; } }