kopia lustrzana https://github.com/maccasoft/z80-tools
Debugger moved to its own class
rodzic
66cafbaf1a
commit
1a107dcb97
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Marco Maccaferri and others.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program and the accompanying materials are made available under
|
||||
* the terms of the Eclipse Public License v1.0 which accompanies this
|
||||
* distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
|
||||
package com.maccasoft.tools;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import nl.grauw.glass.Source;
|
||||
import nl.grauw.glass.SourceBuilder;
|
||||
|
||||
public class DebuggerTest extends TestCase {
|
||||
|
||||
public void testSetStartAddress() throws Exception {
|
||||
Debugger debugger = new Debugger(System.out);
|
||||
debugger.setSource(assemble(
|
||||
" org 100h",
|
||||
" ld a,23",
|
||||
" ret"));
|
||||
|
||||
assertEquals(0x0100, debugger.proc.getRegPC());
|
||||
}
|
||||
|
||||
public void testStepInto() throws Exception {
|
||||
Debugger debugger = new Debugger(System.out);
|
||||
debugger.setSource(assemble(
|
||||
" ld a,23",
|
||||
" ret"));
|
||||
|
||||
debugger.stepInto();
|
||||
|
||||
assertEquals(0x0002, debugger.proc.getRegPC());
|
||||
}
|
||||
|
||||
public void testStepIntoCall() throws Exception {
|
||||
Debugger debugger = new Debugger(System.out);
|
||||
debugger.setSource(assemble(
|
||||
" call label",
|
||||
" ret",
|
||||
"label:",
|
||||
" ld a,23",
|
||||
" ret"));
|
||||
|
||||
debugger.stepInto();
|
||||
|
||||
assertEquals(0x0004, debugger.proc.getRegPC());
|
||||
}
|
||||
|
||||
public void testStepOver() throws Exception {
|
||||
Debugger debugger = new Debugger(System.out);
|
||||
debugger.setSource(assemble(
|
||||
" ld a,23",
|
||||
" ret"));
|
||||
|
||||
debugger.stepOver();
|
||||
|
||||
assertEquals(0x0002, debugger.proc.getRegPC());
|
||||
}
|
||||
|
||||
public void testStepOverCall() throws Exception {
|
||||
Debugger debugger = new Debugger(System.out);
|
||||
debugger.setSource(assemble(
|
||||
" call label",
|
||||
" ret",
|
||||
"label:",
|
||||
" ld a,23",
|
||||
" ret"));
|
||||
|
||||
debugger.stepOver();
|
||||
|
||||
assertEquals(0x0003, debugger.proc.getRegPC());
|
||||
}
|
||||
|
||||
public void testStepIntoRepeatInstructions() throws Exception {
|
||||
Debugger debugger = new Debugger(System.out);
|
||||
debugger.setSource(assemble(
|
||||
" ldir",
|
||||
" ret"));
|
||||
|
||||
debugger.proc.setRegBC(2);
|
||||
debugger.proc.setRegDE(0x1000);
|
||||
debugger.proc.setRegHL(0x2000);
|
||||
|
||||
debugger.stepInto();
|
||||
assertEquals(0x0000, debugger.proc.getRegPC());
|
||||
assertEquals(1, debugger.proc.getRegBC());
|
||||
}
|
||||
|
||||
public void testStepOverRepeatInstructions() throws Exception {
|
||||
Debugger debugger = new Debugger(System.out);
|
||||
debugger.setSource(assemble(
|
||||
" ldir",
|
||||
" ret"));
|
||||
|
||||
debugger.proc.setRegBC(2);
|
||||
debugger.proc.setRegDE(0x1000);
|
||||
debugger.proc.setRegHL(0x2000);
|
||||
|
||||
debugger.stepOver();
|
||||
|
||||
assertEquals(0x0002, debugger.proc.getRegPC());
|
||||
assertEquals(0, debugger.proc.getRegBC());
|
||||
}
|
||||
|
||||
public void testRunToAddress() throws Exception {
|
||||
Debugger debugger = new Debugger(System.out);
|
||||
debugger.setSource(assemble(
|
||||
" nop",
|
||||
" ld a,23",
|
||||
" ret"));
|
||||
|
||||
debugger.runToAddress(0x0003);
|
||||
|
||||
assertEquals(0x0003, debugger.proc.getRegPC());
|
||||
}
|
||||
|
||||
private Source assemble(String... sourceLines) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String lineText : sourceLines) {
|
||||
builder.append(lineText).append("\n");
|
||||
}
|
||||
SourceBuilder sourceBuilder = new SourceBuilder(new ArrayList<File>());
|
||||
Source source = sourceBuilder.parse(new StringReader(builder.toString()), null);
|
||||
try {
|
||||
source.assemble(new ByteArrayOutputStream());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
|
@ -99,9 +99,6 @@ import nl.grauw.glass.Source;
|
|||
import nl.grauw.glass.SourceBuilder;
|
||||
import nl.grauw.glass.directives.Directive;
|
||||
import nl.grauw.glass.directives.Include;
|
||||
import z80core.MemIoOps;
|
||||
import z80core.NotifyOps;
|
||||
import z80core.Z80;
|
||||
|
||||
public class Application {
|
||||
|
||||
|
@ -131,16 +128,10 @@ public class Application {
|
|||
DebugTerminal debugTerminal;
|
||||
Emulator emulator;
|
||||
|
||||
TMS9918 tms9918;
|
||||
DebugTMS9918 debugTMS9918;
|
||||
|
||||
Z80 proc;
|
||||
MemIoOps memIoOps;
|
||||
SourceMap sourceMap;
|
||||
|
||||
int stepOverPC1;
|
||||
int stepOverPC2;
|
||||
int stepOverSP;
|
||||
Debugger debugger;
|
||||
Thread debuggerThread;
|
||||
|
||||
Preferences preferences;
|
||||
|
||||
|
@ -215,74 +206,6 @@ public class Application {
|
|||
|
||||
};
|
||||
|
||||
final Runnable stepOverRunnable = new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (stepOverPC1 == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
int currentPC = proc.getRegPC();
|
||||
proc.execute();
|
||||
|
||||
if (viewer.isBreakpoint(proc.getRegPC())) {
|
||||
handleStop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (proc.getRegPC() == stepOverPC1 || proc.getRegPC() == stepOverPC2 || (proc.getRegPC() != currentPC && proc.getRegSP() == stepOverSP)) {
|
||||
handleStop();
|
||||
return;
|
||||
}
|
||||
|
||||
display.asyncExec(this);
|
||||
}
|
||||
};
|
||||
|
||||
final Runnable runToLineRunnable = new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (stepOverPC1 == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
proc.execute();
|
||||
|
||||
if (viewer.isBreakpoint(proc.getRegPC())) {
|
||||
handleStop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (proc.getRegPC() == stepOverPC1) {
|
||||
handleStop();
|
||||
return;
|
||||
}
|
||||
|
||||
display.asyncExec(this);
|
||||
}
|
||||
};
|
||||
|
||||
final Runnable runCodeRunnable = new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (stepOverPC1 == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
proc.execute();
|
||||
|
||||
if (viewer.isBreakpoint(proc.getRegPC())) {
|
||||
handleStop();
|
||||
return;
|
||||
}
|
||||
|
||||
display.asyncExec(this);
|
||||
}
|
||||
};
|
||||
|
||||
public Application() {
|
||||
|
||||
}
|
||||
|
@ -354,6 +277,7 @@ public class Application {
|
|||
|
||||
@Override
|
||||
public void widgetDisposed(DisposeEvent e) {
|
||||
debugger.doStop();
|
||||
if (terminal != null) {
|
||||
terminal.dispose();
|
||||
terminal = null;
|
||||
|
@ -2435,209 +2359,50 @@ public class Application {
|
|||
return;
|
||||
}
|
||||
|
||||
tms9918 = new TMS9918();
|
||||
if (debugTMS9918 != null) {
|
||||
debugTMS9918.setTMS9918(tms9918);
|
||||
debugTMS9918.redraw();
|
||||
}
|
||||
|
||||
memIoOps = new MemIoOps(65536) {
|
||||
|
||||
public final static int SIOA_D = 0x81;
|
||||
public final static int SIOA_C = 0x80;
|
||||
public final static int SIOB_D = 0x83;
|
||||
public final static int SIOB_C = 0x82;
|
||||
debugger = new Debugger(out) {
|
||||
|
||||
@Override
|
||||
public void poke8(int address, int value) {
|
||||
memory.poke(address, value);
|
||||
display.syncExec(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
memory.poke(address, value);
|
||||
}
|
||||
});
|
||||
super.poke8(address, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int inPort(int port) {
|
||||
switch (port & 0xFF) {
|
||||
case SIOA_C: {
|
||||
int result = 0b00101100; // TX Buffer Empty, DCD and CTS
|
||||
try {
|
||||
if (debugTerminal != null && debugTerminal.getInputStream().available() > 0) {
|
||||
result |= 0x01; // RX Char Available
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case SIOA_D: {
|
||||
try {
|
||||
if (debugTerminal != null && debugTerminal.getInputStream().available() > 0) {
|
||||
return debugTerminal.getInputStream().read();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
case SIOB_C:
|
||||
return 0b00101100; // TX Buffer Empty, DCD and CTS
|
||||
case SIOB_D:
|
||||
return 0x00;
|
||||
}
|
||||
protected void doUpdateDebuggerState() {
|
||||
super.doUpdateDebuggerState();
|
||||
display.syncExec(new Runnable() {
|
||||
|
||||
if ((port & 0xFF) == preferences.getTms9918Ram()) {
|
||||
return tms9918.inRam();
|
||||
}
|
||||
if ((port & 0xFF) == preferences.getTms9918Register()) {
|
||||
return tms9918.inReg();
|
||||
}
|
||||
|
||||
return super.inPort(port);
|
||||
@Override
|
||||
public void run() {
|
||||
updateDebuggerState();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void outPort(int port, int value) {
|
||||
switch (port & 0xFF) {
|
||||
case SIOA_D:
|
||||
if (debugTerminal != null) {
|
||||
debugTerminal.write(value);
|
||||
}
|
||||
else {
|
||||
out.write(value);
|
||||
}
|
||||
break;
|
||||
case SIOB_D:
|
||||
out.write(value);
|
||||
break;
|
||||
}
|
||||
if ((port & 0xFF) == preferences.getTms9918Ram()) {
|
||||
tms9918.outRam(value);
|
||||
}
|
||||
if ((port & 0xFF) == preferences.getTms9918Register()) {
|
||||
tms9918.outReg(value);
|
||||
}
|
||||
super.outPort(port, value);
|
||||
protected boolean isBreakpoint(int address) {
|
||||
return viewer.isBreakpoint(address);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
memIoOps.getRam()[0] = (byte) 0xC3;
|
||||
memIoOps.getRam()[1] = 0x00;
|
||||
memIoOps.getRam()[2] = 0x01; // JP 0x100 CP/M TPA
|
||||
memIoOps.getRam()[5] = (byte) 0xC9; // Return from BDOS call
|
||||
|
||||
memIoOps.getRam()[8] = (byte) 0xD3; // RST08
|
||||
memIoOps.getRam()[9] = (byte) 0x81;
|
||||
memIoOps.getRam()[10] = (byte) 0xC9;
|
||||
|
||||
proc = new Z80(memIoOps, new NotifyOps() {
|
||||
|
||||
@Override
|
||||
public int breakpoint(int address, int opcode) {
|
||||
|
||||
// Emulate CP/M Syscall at address 5
|
||||
if (address == 0x0005) {
|
||||
switch (proc.getRegC()) {
|
||||
case 0x00: // BDOS 0 System Reset
|
||||
out.println("Z80 reset after " + memIoOps.getTstates() + " t-states");
|
||||
break;
|
||||
case 0x01: // BDOS 1 console char input
|
||||
try {
|
||||
if (debugTerminal != null && debugTerminal.getInputStream().available() > 0) {
|
||||
proc.setRegA(debugTerminal.getInputStream().read());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
case 0x02: // BDOS 2 console char output
|
||||
if (debugTerminal != null) {
|
||||
debugTerminal.write(proc.getRegE());
|
||||
}
|
||||
else {
|
||||
out.write(proc.getRegE());
|
||||
}
|
||||
break;
|
||||
case 0x04: // BDOS 4 punch output
|
||||
case 0x05: // BDOS 2 list output
|
||||
out.write(proc.getRegE());
|
||||
break;
|
||||
case 0x06: { // BDOS 6 direct console I/O
|
||||
if (proc.getRegE() == 0xFF) {
|
||||
try {
|
||||
if (debugTerminal != null && debugTerminal.getInputStream().available() > 0) {
|
||||
proc.setRegA(debugTerminal.getInputStream().read());
|
||||
}
|
||||
else {
|
||||
proc.setRegA(0);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (debugTerminal != null) {
|
||||
debugTerminal.write(proc.getRegE());
|
||||
}
|
||||
else {
|
||||
out.write(proc.getRegE());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x09: { // BDOS 9 console string output (string terminated by "$")
|
||||
int strAddr = proc.getRegDE();
|
||||
if (debugTerminal != null) {
|
||||
while (memIoOps.peek8(strAddr) != '$') {
|
||||
debugTerminal.write(memIoOps.peek8(strAddr++));
|
||||
}
|
||||
}
|
||||
else {
|
||||
while (memIoOps.peek8(strAddr) != '$') {
|
||||
out.write(memIoOps.peek8(strAddr++));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x0B: // BDOS 11 console status
|
||||
try {
|
||||
if (debugTerminal != null && debugTerminal.getInputStream().available() > 0) {
|
||||
proc.setRegA(0xFF);
|
||||
}
|
||||
else {
|
||||
proc.setRegA(0x00);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
out.println("BDOS Call " + proc.getRegC());
|
||||
break;
|
||||
}
|
||||
|
||||
return opcode;
|
||||
}
|
||||
|
||||
return opcode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execDone() {
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
proc.setBreakpoint(0x0005, true);
|
||||
|
||||
sourceMap = new SourceMap(source, memIoOps);
|
||||
sourceMap.build();
|
||||
debugger.tms9918Ram = preferences.getTms9918Ram();
|
||||
debugger.tms9918Reg = preferences.getTms9918Register();
|
||||
|
||||
display.syncExec(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
memory.setData(memIoOps.getRam());
|
||||
viewer.setSourceMap(sourceMap);
|
||||
debugger.setSource(source);
|
||||
|
||||
memory.setData(debugger.getRam());
|
||||
memory.setSelection(debugger.proc.getRegPC());
|
||||
viewer.setSourceMap(debugger.getSourceMap());
|
||||
handleReset();
|
||||
|
||||
reparentControls();
|
||||
|
@ -2670,8 +2435,6 @@ public class Application {
|
|||
|
||||
reparentControls();
|
||||
|
||||
stepOverPC1 = -1;
|
||||
|
||||
stackLayout.topControl = stack.getChildren()[0];
|
||||
stack.layout(true, true);
|
||||
|
||||
|
@ -2694,7 +2457,9 @@ public class Application {
|
|||
}
|
||||
|
||||
private void handleReset() {
|
||||
stepOverPC1 = stepOverPC2 = -1;
|
||||
if (debuggerThread != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (debugTerminal != null) {
|
||||
try {
|
||||
|
@ -2706,104 +2471,84 @@ public class Application {
|
|||
}
|
||||
}
|
||||
|
||||
tms9918.reset();
|
||||
|
||||
memIoOps.reset();
|
||||
memory.clearUpdates();
|
||||
viewer.getControl().setFocus();
|
||||
|
||||
proc.setPinReset();
|
||||
proc.reset();
|
||||
proc.setRegPC(sourceMap.getEntryAddress());
|
||||
|
||||
memory.setSelection(sourceMap.getEntryAddress());
|
||||
debugger.reset();
|
||||
|
||||
updateDebuggerState();
|
||||
}
|
||||
|
||||
private void handleStep() {
|
||||
stepOverPC1 = stepOverPC2 = -1;
|
||||
if (debuggerThread != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
memory.clearUpdates();
|
||||
viewer.getControl().setFocus();
|
||||
viewer.setHighlighCurrentLine(false);
|
||||
|
||||
LineEntry lineEntry = sourceMap.getLineAtAddress(proc.getRegPC());
|
||||
if (lineEntry != null) {
|
||||
stepOverPC1 = proc.getRegPC() + lineEntry.code.length;
|
||||
stepOverPC2 = memIoOps.peek16(proc.getRegSP());
|
||||
stepOverSP = proc.getRegSP();
|
||||
boolean repeat = isRepeatInstruction();
|
||||
debuggerThread = new Thread(new Runnable() {
|
||||
|
||||
proc.execute();
|
||||
|
||||
if (repeat || sourceMap.getLineAtAddress(proc.getRegPC()) == null) {
|
||||
display.asyncExec(stepOverRunnable);
|
||||
return;
|
||||
@Override
|
||||
public void run() {
|
||||
debugger.stepInto();
|
||||
debuggerThread = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
proc.execute();
|
||||
}
|
||||
|
||||
updateDebuggerState();
|
||||
}
|
||||
|
||||
boolean isRepeatInstruction() {
|
||||
int pc = proc.getRegPC();
|
||||
byte[] ram = memIoOps.getRam();
|
||||
if ((ram[pc] & 0xFF) == 0xED) {
|
||||
switch (ram[(pc + 1) & 0xFFFF]) {
|
||||
case (byte) 0xB0: /* LDIR */
|
||||
case (byte) 0xB1: /* CPIR */
|
||||
case (byte) 0xB2: /* INIR */
|
||||
case (byte) 0xB3: /* OTIR */
|
||||
case (byte) 0xB8: /* LDDR */
|
||||
case (byte) 0xB9: /* CPDR */
|
||||
case (byte) 0xBA: /* INDR */
|
||||
case (byte) 0xBB: /* OTDR */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
debuggerThread.start();
|
||||
}
|
||||
|
||||
private void handleStepOver() {
|
||||
stepOverPC1 = stepOverPC2 = -1;
|
||||
if (debuggerThread != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
memory.clearUpdates();
|
||||
viewer.getControl().setFocus();
|
||||
viewer.setHighlighCurrentLine(false);
|
||||
|
||||
LineEntry lineEntry = sourceMap.getLineAtAddress(proc.getRegPC());
|
||||
if (lineEntry != null) {
|
||||
stepOverPC1 = proc.getRegPC() + lineEntry.code.length;
|
||||
stepOverPC2 = memIoOps.peek16(proc.getRegSP());
|
||||
stepOverSP = proc.getRegSP();
|
||||
display.asyncExec(stepOverRunnable);
|
||||
}
|
||||
else {
|
||||
proc.execute();
|
||||
updateDebuggerState();
|
||||
}
|
||||
debuggerThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
debugger.stepOver();
|
||||
debuggerThread = null;
|
||||
}
|
||||
|
||||
});
|
||||
debuggerThread.start();
|
||||
}
|
||||
|
||||
private void handleRunToLine() {
|
||||
stepOverPC1 = stepOverPC2 = -1;
|
||||
if (debuggerThread != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
memory.clearUpdates();
|
||||
viewer.getControl().setFocus();
|
||||
viewer.setHighlighCurrentLine(false);
|
||||
|
||||
int caretOffset = viewer.getStyledText().getCaretOffset();
|
||||
int lineAtOffset = viewer.getStyledText().getLineAtOffset(caretOffset);
|
||||
|
||||
LineEntry lineEntry = viewer.getSourceMap().getLines().get(lineAtOffset);
|
||||
final LineEntry lineEntry = viewer.getSourceMap().getLines().get(lineAtOffset);
|
||||
|
||||
if (lineEntry.address != proc.getRegPC()) {
|
||||
stepOverPC1 = lineEntry.address;
|
||||
display.asyncExec(runToLineRunnable);
|
||||
}
|
||||
debuggerThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
debugger.runToAddress(lineEntry.address);
|
||||
debuggerThread = null;
|
||||
}
|
||||
|
||||
});
|
||||
debuggerThread.start();
|
||||
}
|
||||
|
||||
private void handleStop() {
|
||||
stepOverPC1 = stepOverPC2 = -1;
|
||||
memory.clearUpdates();
|
||||
updateDebuggerState();
|
||||
debugger.doStop();
|
||||
}
|
||||
|
||||
private void handleToggleBreakpoint() {
|
||||
|
@ -2819,30 +2564,42 @@ public class Application {
|
|||
|
||||
private void handleClearBreakpoints() {
|
||||
viewer.resetBreakpoints();
|
||||
proc.resetBreakpoints();
|
||||
debugger.resetBreakpoints();
|
||||
}
|
||||
|
||||
private void handleRun() {
|
||||
stepOverPC1 = 0;
|
||||
stepOverPC2 = -1;
|
||||
if (debuggerThread != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
memory.clearUpdates();
|
||||
viewer.getControl().setFocus();
|
||||
viewer.setHighlighCurrentLine(false);
|
||||
|
||||
display.asyncExec(runCodeRunnable);
|
||||
debuggerThread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
debugger.run();
|
||||
debuggerThread = null;
|
||||
}
|
||||
|
||||
});
|
||||
debuggerThread.start();
|
||||
}
|
||||
|
||||
void updateDebuggerState() {
|
||||
memory.update();
|
||||
registers.updateRegisters(proc);
|
||||
registers.updateRegisters(debugger.proc);
|
||||
|
||||
tms9918.redrawFrame();
|
||||
if (debugTMS9918 != null) {
|
||||
debugTMS9918.redraw();
|
||||
}
|
||||
|
||||
LineEntry lineEntry = sourceMap.getLineAtAddress(proc.getRegPC());
|
||||
LineEntry lineEntry = debugger.getSourceMap().getLineAtAddress(debugger.proc.getRegPC());
|
||||
if (lineEntry != null) {
|
||||
viewer.gotToLineColumn(lineEntry.lineNumber, 0);
|
||||
viewer.setHighlighCurrentLine(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2864,7 +2621,7 @@ public class Application {
|
|||
private void handleOpenTMS9918Window() {
|
||||
if (debugTMS9918 == null) {
|
||||
debugTMS9918 = new DebugTMS9918();
|
||||
debugTMS9918.setTMS9918(tms9918);
|
||||
debugTMS9918.setTMS9918(debugger.tms9918);
|
||||
debugTMS9918.open();
|
||||
debugTMS9918.getShell().addDisposeListener(new DisposeListener() {
|
||||
|
||||
|
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Marco Maccaferri and others.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program and the accompanying materials are made available under
|
||||
* the terms of the Eclipse Public License v1.0 which accompanies this
|
||||
* distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
|
||||
package com.maccasoft.tools;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
import com.maccasoft.tools.SourceMap.LineEntry;
|
||||
|
||||
import nl.grauw.glass.Source;
|
||||
import z80core.MemIoOps;
|
||||
import z80core.NotifyOps;
|
||||
import z80core.Z80;
|
||||
|
||||
public class Debugger extends MemIoOps implements NotifyOps {
|
||||
|
||||
Z80 proc;
|
||||
SourceMap sourceMap;
|
||||
|
||||
DebugTerminal debugTerminal;
|
||||
|
||||
int tms9918Ram;
|
||||
int tms9918Reg;
|
||||
TMS9918 tms9918;
|
||||
|
||||
boolean stop;
|
||||
|
||||
final PrintStream out;
|
||||
|
||||
public Debugger(PrintStream out) {
|
||||
super(65536);
|
||||
|
||||
this.out = out;
|
||||
|
||||
z80Ram[0] = (byte) 0xC3;
|
||||
z80Ram[1] = 0x00;
|
||||
z80Ram[2] = 0x01; // JP 0x100 CP/M TPA
|
||||
z80Ram[5] = (byte) 0xC9; // Return from BDOS call
|
||||
|
||||
z80Ram[8] = (byte) 0xD3; // RST08
|
||||
z80Ram[9] = (byte) 0x81;
|
||||
z80Ram[10] = (byte) 0xC9;
|
||||
|
||||
tms9918Ram = 0x40;
|
||||
tms9918Reg = 0x41;
|
||||
tms9918 = new TMS9918();
|
||||
|
||||
proc = new Z80(this, this);
|
||||
proc.setBreakpoint(0x0005, true);
|
||||
}
|
||||
|
||||
public void setSource(Source source) {
|
||||
sourceMap = new SourceMap(source, this);
|
||||
sourceMap.build();
|
||||
|
||||
proc.setPinReset();
|
||||
proc.reset();
|
||||
proc.setRegPC(sourceMap.getEntryAddress());
|
||||
}
|
||||
|
||||
public SourceMap getSourceMap() {
|
||||
return sourceMap;
|
||||
}
|
||||
|
||||
public void setDebugTerminal(DebugTerminal debugTerminal) {
|
||||
this.debugTerminal = debugTerminal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
tms9918.reset();
|
||||
|
||||
proc.setPinReset();
|
||||
proc.reset();
|
||||
proc.setRegPC(sourceMap.getEntryAddress());
|
||||
|
||||
super.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int inPort(int port) {
|
||||
tstates += 4; // 4 clocks for read byte from bus
|
||||
|
||||
port &= 0xFF;
|
||||
|
||||
switch (port) {
|
||||
case Machine.SIOA_C: {
|
||||
int result = 0b00101100; // TX Buffer Empty, DCD and CTS
|
||||
try {
|
||||
if (debugTerminal != null && debugTerminal.getInputStream().available() > 0) {
|
||||
result |= 0x01; // RX Char Available
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case Machine.SIOA_D: {
|
||||
try {
|
||||
if (debugTerminal != null && debugTerminal.getInputStream().available() > 0) {
|
||||
return debugTerminal.getInputStream().read();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
case Machine.SIOB_C:
|
||||
return 0b00101100; // TX Buffer Empty, DCD and CTS
|
||||
case Machine.SIOB_D:
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
if (port == tms9918Ram) {
|
||||
return tms9918.inRam();
|
||||
}
|
||||
if (port == tms9918Reg) {
|
||||
return tms9918.inReg();
|
||||
}
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void outPort(int port, int value) {
|
||||
tstates += 4; // 4 clocks for write byte to bus
|
||||
|
||||
port &= 0xFF;
|
||||
value &= 0xFF;
|
||||
|
||||
switch (port) {
|
||||
case Machine.SIOA_D:
|
||||
if (debugTerminal != null) {
|
||||
debugTerminal.write(value);
|
||||
}
|
||||
else {
|
||||
out.write(value);
|
||||
}
|
||||
break;
|
||||
case Machine.SIOB_D:
|
||||
out.write(value);
|
||||
break;
|
||||
}
|
||||
|
||||
if (port == tms9918Ram) {
|
||||
tms9918.outRam(value);
|
||||
}
|
||||
if (port == tms9918Reg) {
|
||||
tms9918.outReg(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int breakpoint(int address, int opcode) {
|
||||
|
||||
// Emulate CP/M Syscall at address 5
|
||||
if (address == 0x0005) {
|
||||
if (z80Ram[5] != (byte) 0xC9) {
|
||||
return opcode;
|
||||
}
|
||||
doCPMEmulation();
|
||||
}
|
||||
|
||||
return opcode;
|
||||
}
|
||||
|
||||
protected void doCPMEmulation() {
|
||||
switch (proc.getRegC()) {
|
||||
case 0x00: // BDOS 0 System Reset
|
||||
stop = true;
|
||||
out.println("Z80 reset after " + getTstates() + " t-states");
|
||||
break;
|
||||
case 0x01: // BDOS 1 console char input
|
||||
try {
|
||||
if (debugTerminal != null && debugTerminal.getInputStream().available() > 0) {
|
||||
proc.setRegA(debugTerminal.getInputStream().read());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
case 0x02: // BDOS 2 console char output
|
||||
if (debugTerminal != null) {
|
||||
debugTerminal.write(proc.getRegE());
|
||||
}
|
||||
else {
|
||||
out.write(proc.getRegE());
|
||||
}
|
||||
break;
|
||||
case 0x04: // BDOS 4 punch output
|
||||
case 0x05: // BDOS 2 list output
|
||||
out.write(proc.getRegE());
|
||||
break;
|
||||
case 0x06: { // BDOS 6 direct console I/O
|
||||
if (proc.getRegE() == 0xFF) {
|
||||
try {
|
||||
if (debugTerminal != null && debugTerminal.getInputStream().available() > 0) {
|
||||
proc.setRegA(debugTerminal.getInputStream().read());
|
||||
}
|
||||
else {
|
||||
proc.setRegA(0);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (debugTerminal != null) {
|
||||
debugTerminal.write(proc.getRegE());
|
||||
}
|
||||
else {
|
||||
out.write(proc.getRegE());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x09: { // BDOS 9 console string output (string terminated by "$")
|
||||
int strAddr = proc.getRegDE();
|
||||
if (debugTerminal != null) {
|
||||
while (peek8(strAddr) != '$') {
|
||||
debugTerminal.write(peek8(strAddr++));
|
||||
}
|
||||
}
|
||||
else {
|
||||
while (peek8(strAddr) != '$') {
|
||||
out.write(peek8(strAddr++));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x0B: // BDOS 11 console status
|
||||
try {
|
||||
if (debugTerminal != null && debugTerminal.getInputStream().available() > 0) {
|
||||
proc.setRegA(0xFF);
|
||||
}
|
||||
else {
|
||||
proc.setRegA(0x00);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
out.println("BDOS Call " + proc.getRegC());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execDone() {
|
||||
}
|
||||
|
||||
public void doStop() {
|
||||
stop = true;
|
||||
}
|
||||
|
||||
public void stepOver() {
|
||||
LineEntry lineEntry = sourceMap.getLineAtAddress(proc.getRegPC());
|
||||
if (lineEntry != null) {
|
||||
int stepOverPC1 = proc.getRegPC() + lineEntry.code.length;
|
||||
int stepOverPC2 = peek16(proc.getRegSP());
|
||||
int stepOverSP = proc.getRegSP();
|
||||
|
||||
stop = false;
|
||||
do {
|
||||
int currentPC = proc.getRegPC();
|
||||
proc.execute();
|
||||
|
||||
if (isBreakpoint(proc.getRegPC())) {
|
||||
break;
|
||||
}
|
||||
if (proc.getRegPC() == stepOverPC1 || proc.getRegPC() == stepOverPC2 || (proc.getRegPC() != currentPC && proc.getRegSP() == stepOverSP)) {
|
||||
break;
|
||||
}
|
||||
} while (!stop);
|
||||
|
||||
}
|
||||
else {
|
||||
proc.execute();
|
||||
}
|
||||
doUpdateDebuggerState();
|
||||
}
|
||||
|
||||
public void stepInto() {
|
||||
LineEntry lineEntry = sourceMap.getLineAtAddress(proc.getRegPC());
|
||||
if (lineEntry != null) {
|
||||
int stepOverPC1 = proc.getRegPC() + lineEntry.code.length;
|
||||
int stepOverPC2 = peek16(proc.getRegSP());
|
||||
int stepOverSP = proc.getRegSP();
|
||||
|
||||
proc.execute();
|
||||
|
||||
if (sourceMap.getLineAtAddress(proc.getRegPC()) == null) {
|
||||
stop = false;
|
||||
do {
|
||||
int currentPC = proc.getRegPC();
|
||||
proc.execute();
|
||||
|
||||
if (isBreakpoint(proc.getRegPC())) {
|
||||
break;
|
||||
}
|
||||
if (proc.getRegPC() == stepOverPC1 || proc.getRegPC() == stepOverPC2 || (proc.getRegPC() != currentPC && proc.getRegSP() == stepOverSP)) {
|
||||
break;
|
||||
}
|
||||
} while (!stop);
|
||||
}
|
||||
}
|
||||
else {
|
||||
proc.execute();
|
||||
}
|
||||
doUpdateDebuggerState();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
stop = false;
|
||||
|
||||
int count = 0;
|
||||
do {
|
||||
proc.execute();
|
||||
|
||||
if (isBreakpoint(proc.getRegPC())) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (count++ >= 16) {
|
||||
doUpdateDebuggerState();
|
||||
count = 0;
|
||||
}
|
||||
} while (!stop);
|
||||
|
||||
doUpdateDebuggerState();
|
||||
}
|
||||
|
||||
public void runToAddress(int addr) {
|
||||
stop = false;
|
||||
do {
|
||||
proc.execute();
|
||||
|
||||
if (isBreakpoint(proc.getRegPC())) {
|
||||
break;
|
||||
}
|
||||
if (proc.getRegPC() == addr) {
|
||||
break;
|
||||
}
|
||||
} while (!stop);
|
||||
|
||||
doUpdateDebuggerState();
|
||||
}
|
||||
|
||||
protected boolean isBreakpoint(int address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void doUpdateDebuggerState() {
|
||||
tms9918.redrawFrame();
|
||||
}
|
||||
|
||||
public void resetBreakpoints() {
|
||||
proc.resetBreakpoints();
|
||||
proc.setBreakpoint(0x0005, true);
|
||||
}
|
||||
}
|
|
@ -63,6 +63,7 @@ public class SourceViewer {
|
|||
SourceMap sourceMap;
|
||||
|
||||
boolean showLineNumbers = true;
|
||||
boolean highlighCurrentLine = true;
|
||||
|
||||
private final CaretListener caretListener = new CaretListener() {
|
||||
|
||||
|
@ -74,7 +75,7 @@ public class SourceViewer {
|
|||
if (currentLine >= 0 && currentLine < text.getLineCount()) {
|
||||
text.setLineBackground(currentLine, 1, null);
|
||||
}
|
||||
text.setLineBackground(line, 1, currentLineBackground);
|
||||
text.setLineBackground(line, 1, highlighCurrentLine ? currentLineBackground : null);
|
||||
currentLine = line;
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +123,7 @@ public class SourceViewer {
|
|||
|
||||
currentLine = 0;
|
||||
currentLineBackground = new Color(Display.getDefault(), 232, 242, 254);
|
||||
text.setLineBackground(currentLine, 1, currentLineBackground);
|
||||
text.setLineBackground(currentLine, 1, highlighCurrentLine ? currentLineBackground : null);
|
||||
|
||||
updateTokenStyles();
|
||||
|
||||
|
@ -199,7 +200,7 @@ public class SourceViewer {
|
|||
|
||||
currentLine = 0;
|
||||
this.text.setText(text);
|
||||
this.text.setLineBackground(currentLine, 1, currentLineBackground);
|
||||
this.text.setLineBackground(currentLine, 1, highlighCurrentLine ? currentLineBackground : null);
|
||||
tokenMarker.refreshMultilineComments(text);
|
||||
}
|
||||
|
||||
|
@ -430,4 +431,9 @@ public class SourceViewer {
|
|||
codeRuler.redraw();
|
||||
}
|
||||
|
||||
public void setHighlighCurrentLine(boolean highlighCurrentLine) {
|
||||
this.highlighCurrentLine = highlighCurrentLine;
|
||||
text.setLineBackground(currentLine, 1, highlighCurrentLine ? currentLineBackground : null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ package z80core;
|
|||
*/
|
||||
public class MemIoOps {
|
||||
|
||||
private byte[] z80Ram;
|
||||
protected byte[] z80Ram;
|
||||
|
||||
protected long tstates = 0;
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue