CpuStateText.java

1
package com.rox.debug.cpu;
2
3
import com.rox.cpu.mos6502.MOS6502Snapshot;
4
import com.rox.cpu.mos6502.assembler.DisassembledInstruction;
5
6
import java.util.List;
7
import java.util.Optional;
8
import java.util.Locale;
9
10
/**
11
 * One-line text renderings of CPU state, shared by every CPU view - the flagged-issue {@code state.txt}
12
 * and the live CPU state panel - so both read the same way.
13
 */
14
public final class CpuStateText {
15
    /** Where a line's state starts: wide enough for the longest instruction, {@code   $FFFF  ORA ($40),Y}. */
16
    private static final int STATE_COLUMN = 24;
17
18
    private CpuStateText(){
19
    }
20
21
    /** {@code PC: $8123  A: $01  X: $02  Y: $03  SP: $FD} */
22
    public static String registers(final MOS6502Snapshot cpu){
23 1 1. registers : replaced return value with "" for com/rox/debug/cpu/CpuStateText::registers → KILLED
        return String.format("PC: $%04X  A: $%02X  X: $%02X  Y: $%02X  SP: $%02X",
24
                cpu.pc(), cpu.a(), cpu.x(), cpu.y(), cpu.stackPointer());
25
    }
26
27
    /** {@code Nv-BdIzC} - the status register's {@code NV-BDIZC} bits, uppercase when set. */
28
    public static String flags(final MOS6502Snapshot cpu){
29 1 1. flags : replaced return value with "" for com/rox/debug/cpu/CpuStateText::flags → KILLED
        return new StringBuilder()
30 1 1. flags : negated conditional → KILLED
                .append(cpu.negative() ? 'N' : 'n')
31 1 1. flags : negated conditional → KILLED
                .append(cpu.signedOverflow() ? 'V' : 'v')
32
                .append('-')
33 1 1. flags : negated conditional → KILLED
                .append(cpu.breakFlag() ? 'B' : 'b')
34 1 1. flags : negated conditional → KILLED
                .append(cpu.decimal() ? 'D' : 'd')
35 1 1. flags : negated conditional → KILLED
                .append(cpu.interruptDisable() ? 'I' : 'i')
36 1 1. flags : negated conditional → KILLED
                .append(cpu.zero() ? 'Z' : 'z')
37 1 1. flags : negated conditional → KILLED
                .append(cpu.carry() ? 'C' : 'c')
38
                .toString();
39
    }
40
41
    /** {@code IRQ line: clear  NMI pending: yes} */
42
    public static String interruptLines(final MOS6502Snapshot cpu){
43 3 1. interruptLines : replaced return value with "" for com/rox/debug/cpu/CpuStateText::interruptLines → KILLED
2. interruptLines : negated conditional → KILLED
3. interruptLines : negated conditional → KILLED
        return "IRQ line: " + (cpu.irqLineAsserted() ? "asserted" : "clear") + "  NMI pending: " + (cpu.nmiPending() ? "yes" : "no");
44
    }
45
46
    /**
47
     * {@code > $E45B  JMP $E45B    A:B0 X:0F Y:26 SP:FF  Nv-bdIzc} - the instruction, then the state it started
48
     * with (no PC: that's the instruction's own address), plus {@code irq}/{@code nmi} when active.
49
     */
50
    public static String instruction(final DisassembledInstruction instruction, final boolean atPc, final MOS6502Snapshot before){
51 1 1. instruction : replaced return value with "" for com/rox/debug/cpu/CpuStateText::instruction → KILLED
        return String.format("%-" + STATE_COLUMN + "s%s", instruction(instruction, atPc), state(before));
52
    }
53
54
    /** {@code A:B0 X:0F Y:26 SP:FF  Nv-bdIzc}, plus {@code  irq}/{@code  nmi} when active. */
55
    public static String state(final MOS6502Snapshot cpu){
56 1 1. state : replaced return value with "" for com/rox/debug/cpu/CpuStateText::state → KILLED
        return String.format("A:%02X X:%02X Y:%02X SP:%02X  %s%s%s", cpu.a(), cpu.x(), cpu.y(), cpu.stackPointer(), flags(cpu),
57 2 1. state : negated conditional → KILLED
2. state : negated conditional → KILLED
                cpu.irqLineAsserted() ? "  irq" : "", cpu.nmiPending() ? "  nmi" : "");
58
    }
59
60
    /** {@code > $8123  JMP $9000} for the instruction at the PC, {@code   $8126  INX} otherwise. */
61
    public static String instruction(final DisassembledInstruction instruction, final boolean atPc){
62 2 1. instruction : replaced return value with "" for com/rox/debug/cpu/CpuStateText::instruction → KILLED
2. instruction : negated conditional → KILLED
        return (atPc ? "> " : "  ") + String.format("$%04X  %s", instruction.address(), instruction.formatted());
63
    }
64
65
    /**
66
     * {@code Clock: 1,789,773 Hz of 1,789,773 Hz (100.0%)} - below 100% while a game slows down means the
67
     * emulator can't keep up; near 100% means the slowdown is the game's own.
68
     */
69
    public static String clockRate(final double measuredHz, final long intendedHz){
70
        //Locale.ROOT: the same ',' grouping and '.' decimal point whatever the machine's locale
71 3 1. clockRate : Replaced double multiplication with division → KILLED
2. clockRate : replaced return value with "" for com/rox/debug/cpu/CpuStateText::clockRate → KILLED
3. clockRate : Replaced double division with multiplication → KILLED
        return String.format(Locale.ROOT, "Clock: %,.0f Hz of %,d Hz (%.1f%%)", measuredHz, intendedHz, 100.0 * measuredHz / intendedHz);
72
    }
73
74
    /**
75
     * The whole CPU state view as text: run state and clock rate, registers, flags, interrupt lines,
76
     * then the instructions on one time axis running bottom to top - the oldest recent instruction at
77
     * the bottom, the latest above it, then the current one ({@code >}) and the next
78
     * {@code upcomingCount - 1} above that, furthest ahead at the top.
79
     */
80
    public static String panel(final CpuDebugSource source, final int upcomingCount){
81 1 1. panel : replaced return value with "" for com/rox/debug/cpu/CpuStateText::panel → KILLED
        return panel(source.state(), source, upcomingCount);
82
    }
83
84
    /** As {@link #panel(CpuDebugSource, int)}, for a CPU state already read - so a caller can use the same read elsewhere. */
85
    public static String panel(final MOS6502Snapshot cpu, final CpuDebugSource source, final int upcomingCount){
86
        final StringBuilder out = new StringBuilder();
87 1 1. panel : negated conditional → KILLED
        out.append(source.isPaused() ? "PAUSED" : "Running").append("  ")
88
                .append(clockRate(source.measuredHz(), source.intendedHz())).append('\n');
89
        out.append(registers(cpu)).append('\n');
90
        out.append("Flags: ").append(flags(cpu)).append("  (NV-BDIZC)\n");
91
        out.append(interruptLines(cpu)).append("\n\n");
92
        out.append("--- Next (furthest ahead at top) ---\n");
93
        final List<DisassembledInstruction> upcoming = source.upcomingInstructions(upcomingCount);
94
        final Optional<MOS6502Snapshot> currentState = source.currentInstructionState();
95 3 1. panel : negated conditional → KILLED
2. panel : changed conditional boundary → KILLED
3. panel : Replaced integer subtraction with addition → KILLED
        for (int i = upcoming.size() - 1; i >= 0; i--){
96
            //only the current instruction has a state - the rest haven't run yet
97 2 1. panel : negated conditional → KILLED
2. panel : negated conditional → KILLED
            final String line = i == 0 && currentState.isPresent()
98
                    ? instruction(upcoming.get(i), true, currentState.get())
99 1 1. panel : negated conditional → KILLED
                    : instruction(upcoming.get(i), i == 0);
100
            out.append(line).append('\n');
101
        }
102
        out.append("--- Recent (latest at top) ---\n");
103
        final List<TracedInstruction> recent = source.recentInstructions();
104 3 1. panel : changed conditional boundary → KILLED
2. panel : negated conditional → KILLED
3. panel : Replaced integer subtraction with addition → KILLED
        for (int i = recent.size() - 1; i >= 0; i--){
105
            out.append(instruction(recent.get(i).instruction(), false, recent.get(i).before())).append('\n');
106
        }
107 1 1. panel : replaced return value with "" for com/rox/debug/cpu/CpuStateText::panel → KILLED
        return out.toString();
108
    }
109
}

Mutations

23

1.1
Location : registers
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:registersArePaddedHex()]
replaced return value with "" for com/rox/debug/cpu/CpuStateText::registers → KILLED

29

1.1
Location : flags
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:flagsAreUppercaseWhenSet()]
replaced return value with "" for com/rox/debug/cpu/CpuStateText::flags → KILLED

30

1.1
Location : flags
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:flagsAreUppercaseWhenSet()]
negated conditional → KILLED

31

1.1
Location : flags
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:flagsAreUppercaseWhenSet()]
negated conditional → KILLED

33

1.1
Location : flags
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:flagsAreUppercaseWhenSet()]
negated conditional → KILLED

34

1.1
Location : flags
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:flagsAreUppercaseWhenSet()]
negated conditional → KILLED

35

1.1
Location : flags
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:flagsAreUppercaseWhenSet()]
negated conditional → KILLED

36

1.1
Location : flags
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:flagsAreUppercaseWhenSet()]
negated conditional → KILLED

37

1.1
Location : flags
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:flagsAreUppercaseWhenSet()]
negated conditional → KILLED

43

1.1
Location : interruptLines
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:interruptLinesShowBothStates()]
replaced return value with "" for com/rox/debug/cpu/CpuStateText::interruptLines → KILLED

2.2
Location : interruptLines
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:interruptLinesShowBothStates()]
negated conditional → KILLED

3.3
Location : interruptLines
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:interruptLinesShowBothStates()]
negated conditional → KILLED

51

1.1
Location : instruction
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:instructionWithStateAlignsTheStateColumnEvenForTheLongestInstruction()]
replaced return value with "" for com/rox/debug/cpu/CpuStateText::instruction → KILLED

56

1.1
Location : state
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:stateShowsIrqAndNmiOnlyWhenActive()]
replaced return value with "" for com/rox/debug/cpu/CpuStateText::state → KILLED

57

1.1
Location : state
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:stateShowsIrqAndNmiOnlyWhenActive()]
negated conditional → KILLED

2.2
Location : state
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:stateShowsIrqAndNmiOnlyWhenActive()]
negated conditional → KILLED

62

1.1
Location : instruction
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:instructionMarksTheOneAtThePc()]
replaced return value with "" for com/rox/debug/cpu/CpuStateText::instruction → KILLED

2.2
Location : instruction
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:instructionMarksTheOneAtThePc()]
negated conditional → KILLED

71

1.1
Location : clockRate
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:clockRateShowsMeasuredIntendedAndPercentage()]
Replaced double multiplication with division → KILLED

2.2
Location : clockRate
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:clockRateShowsMeasuredIntendedAndPercentage()]
replaced return value with "" for com/rox/debug/cpu/CpuStateText::clockRate → KILLED

3.3
Location : clockRate
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:clockRateShowsMeasuredIntendedAndPercentage()]
Replaced double division with multiplication → KILLED

81

1.1
Location : panel
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:theCurrentInstructionHasNoStateBeforeAnythingHasBeenRecorded()]
replaced return value with "" for com/rox/debug/cpu/CpuStateText::panel → KILLED

87

1.1
Location : panel
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:panelShowsWhenPaused()]
negated conditional → KILLED

95

1.1
Location : panel
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:theCurrentInstructionHasNoStateBeforeAnythingHasBeenRecorded()]
negated conditional → KILLED

2.2
Location : panel
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:theCurrentInstructionHasNoStateBeforeAnythingHasBeenRecorded()]
changed conditional boundary → KILLED

3.3
Location : panel
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:theCurrentInstructionHasNoStateBeforeAnythingHasBeenRecorded()]
Replaced integer subtraction with addition → KILLED

97

1.1
Location : panel
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:panelRunsTimeBottomToTopWithTheCurrentInstructionBetweenNextAndRecent()]
negated conditional → KILLED

2.2
Location : panel
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:theCurrentInstructionHasNoStateBeforeAnythingHasBeenRecorded()]
negated conditional → KILLED

99

1.1
Location : panel
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:theCurrentInstructionHasNoStateBeforeAnythingHasBeenRecorded()]
negated conditional → KILLED

104

1.1
Location : panel
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:panelRunsTimeBottomToTopWithTheCurrentInstructionBetweenNextAndRecent()]
changed conditional boundary → KILLED

2.2
Location : panel
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:theCurrentInstructionHasNoStateBeforeAnythingHasBeenRecorded()]
negated conditional → KILLED

3.3
Location : panel
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:theCurrentInstructionHasNoStateBeforeAnythingHasBeenRecorded()]
Replaced integer subtraction with addition → KILLED

107

1.1
Location : panel
Killed by : com.rox.debug.cpu.CpuStateTextTest.[engine:junit-jupiter]/[class:com.rox.debug.cpu.CpuStateTextTest]/[method:theCurrentInstructionHasNoStateBeforeAnythingHasBeenRecorded()]
replaced return value with "" for com/rox/debug/cpu/CpuStateText::panel → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.1