kopia lustrzana https://github.com/maccasoft/z80-tools
Initial emulator implementation
rodzic
034c82c2a8
commit
28217319ac
|
@ -130,6 +130,7 @@ public class Application {
|
|||
|
||||
SerialTerminal terminal;
|
||||
DebugTerminal debugTerminal;
|
||||
Emulator emulator;
|
||||
|
||||
Z80 proc;
|
||||
MemIoOps memIoOps;
|
||||
|
@ -958,6 +959,21 @@ public class Application {
|
|||
|
||||
new MenuItem(menu, SWT.SEPARATOR);
|
||||
|
||||
item = new MenuItem(menu, SWT.PUSH);
|
||||
item.setText("Emulator\tCtrl+Shift+E");
|
||||
item.setAccelerator(SWT.MOD1 + SWT.MOD2 + 'E');
|
||||
item.addListener(SWT.Selection, new Listener() {
|
||||
|
||||
@Override
|
||||
public void handleEvent(Event e) {
|
||||
try {
|
||||
handleOpenEmulator();
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
item = new MenuItem(menu, SWT.PUSH);
|
||||
item.setText("Serial Terminal\tCtrl+Shift+T");
|
||||
item.setAccelerator(SWT.MOD1 + SWT.MOD2 + 'T');
|
||||
|
@ -2376,7 +2392,7 @@ public class Application {
|
|||
return;
|
||||
}
|
||||
|
||||
memIoOps = new MemIoOps() {
|
||||
memIoOps = new MemIoOps(65536, 256) {
|
||||
|
||||
public final static int SIOA_D = 0x81;
|
||||
public final static int SIOA_C = 0x80;
|
||||
|
@ -2876,6 +2892,21 @@ public class Application {
|
|||
|
||||
}
|
||||
|
||||
private void handleOpenEmulator() {
|
||||
if (emulator == null) {
|
||||
emulator = new Emulator();
|
||||
emulator.open();
|
||||
emulator.getShell().addDisposeListener(new DisposeListener() {
|
||||
|
||||
@Override
|
||||
public void widgetDisposed(DisposeEvent e) {
|
||||
emulator = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
emulator.setFocus();
|
||||
}
|
||||
|
||||
static {
|
||||
Display.setAppName(APP_TITLE);
|
||||
Display.setAppVersion(APP_VERSION);
|
||||
|
|
|
@ -0,0 +1,488 @@
|
|||
/*
|
||||
* Copyright (c) 2018-19 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.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.core.databinding.observable.Realm;
|
||||
import org.eclipse.jface.databinding.swt.DisplayRealm;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.DisposeEvent;
|
||||
import org.eclipse.swt.events.DisposeListener;
|
||||
import org.eclipse.swt.events.SelectionAdapter;
|
||||
import org.eclipse.swt.events.SelectionEvent;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
import org.eclipse.swt.graphics.Point;
|
||||
import org.eclipse.swt.graphics.Rectangle;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Button;
|
||||
import org.eclipse.swt.widgets.Combo;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.Event;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Listener;
|
||||
import org.eclipse.swt.widgets.Menu;
|
||||
import org.eclipse.swt.widgets.MenuItem;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
|
||||
import com.maccasoft.tools.internal.ImageRegistry;
|
||||
|
||||
import nl.grauw.glass.AssemblyException;
|
||||
import nl.grauw.glass.Source;
|
||||
import nl.grauw.glass.SourceBuilder;
|
||||
|
||||
public class Emulator {
|
||||
|
||||
Display display;
|
||||
Shell shell;
|
||||
Composite container;
|
||||
Terminal term;
|
||||
|
||||
Combo cursorKeys;
|
||||
|
||||
PipedOutputStream os;
|
||||
PipedInputStream is;
|
||||
|
||||
Machine machine;
|
||||
Preferences preferences;
|
||||
|
||||
public Emulator() {
|
||||
|
||||
}
|
||||
|
||||
public void open() {
|
||||
display = Display.getDefault();
|
||||
preferences = Preferences.getInstance();
|
||||
|
||||
shell = new Shell(display);
|
||||
shell.setText(Application.APP_TITLE);
|
||||
shell.setData(this);
|
||||
|
||||
Image[] images = new Image[] {
|
||||
ImageRegistry.getImageFromResources("app128.png"),
|
||||
ImageRegistry.getImageFromResources("app64.png"),
|
||||
ImageRegistry.getImageFromResources("app48.png"),
|
||||
ImageRegistry.getImageFromResources("app32.png"),
|
||||
ImageRegistry.getImageFromResources("app16.png"),
|
||||
};
|
||||
shell.setImages(images);
|
||||
|
||||
Menu menu = new Menu(shell, SWT.BAR);
|
||||
createFileMenu(menu);
|
||||
createEditMenu(menu);
|
||||
createHelpMenu(menu);
|
||||
shell.setMenuBar(menu);
|
||||
|
||||
GridLayout layout = new GridLayout(1, false);
|
||||
layout.marginWidth = layout.marginHeight = 0;
|
||||
shell.setLayout(layout);
|
||||
|
||||
Control control = createContents(shell);
|
||||
control.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
||||
|
||||
Rectangle screen = display.getClientArea();
|
||||
|
||||
Point size = control.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
|
||||
|
||||
Rectangle rect = shell.computeTrim(0, 0, size.x, size.y);
|
||||
rect.x = (screen.width - rect.width) / 2;
|
||||
rect.y = (screen.height - rect.height) / 2;
|
||||
if (rect.y < 0) {
|
||||
rect.height += rect.y * 2;
|
||||
rect.y = 0;
|
||||
}
|
||||
|
||||
shell.setLocation(rect.x, rect.y);
|
||||
shell.setSize(rect.width, rect.height);
|
||||
|
||||
shell.open();
|
||||
|
||||
machine = new Machine() {
|
||||
|
||||
@Override
|
||||
protected void run() {
|
||||
try {
|
||||
String s = preferences.getRomImage1();
|
||||
if (s != null && !"".equals(s)) {
|
||||
if (s.toUpperCase().endsWith(".ASM")) {
|
||||
byte[] rom = compile(new File(s));
|
||||
if (rom == null) {
|
||||
return;
|
||||
}
|
||||
machine.setRom(preferences.getRomAddress1(), rom);
|
||||
}
|
||||
else {
|
||||
machine.setRom(preferences.getRomAddress1(), new File(s));
|
||||
}
|
||||
}
|
||||
s = preferences.getRomImage2();
|
||||
if (s != null && !"".equals(s)) {
|
||||
if (s.toUpperCase().endsWith(".ASM")) {
|
||||
byte[] rom = compile(new File(s));
|
||||
if (rom == null) {
|
||||
return;
|
||||
}
|
||||
machine.setRom(preferences.getRomAddress2(), rom);
|
||||
}
|
||||
else {
|
||||
machine.setRom(preferences.getRomAddress2(), new File(s));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
super.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int inPort(int port) {
|
||||
switch (port & 0xFF) {
|
||||
case SIOA_C:
|
||||
try {
|
||||
if (is.available() > 0) {
|
||||
return 0x04 + 0x01;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0x04; // Always return TX buffer empty
|
||||
case SIOA_D:
|
||||
try {
|
||||
if (is.available() > 0) {
|
||||
return is.read();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
}
|
||||
return super.inPort(port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void outPort(int port, int value) {
|
||||
switch (port & 0xFF) {
|
||||
case SIOA_D:
|
||||
display.syncExec(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
term.write(value);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case SIOB_D:
|
||||
break;
|
||||
}
|
||||
super.outPort(port, value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
String s = preferences.getCompactFlashImage();
|
||||
if (s != null && !"".equals(s)) {
|
||||
machine.setCompactFlash(new File(s));
|
||||
}
|
||||
|
||||
machine.reset();
|
||||
machine.start();
|
||||
}
|
||||
|
||||
void createFileMenu(Menu parent) {
|
||||
final Menu menu = new Menu(parent.getParent(), SWT.DROP_DOWN);
|
||||
|
||||
MenuItem item = new MenuItem(parent, SWT.CASCADE);
|
||||
item.setText("&File");
|
||||
item.setMenu(menu);
|
||||
|
||||
item = new MenuItem(menu, SWT.PUSH);
|
||||
item.setText("Close");
|
||||
item.addListener(SWT.Selection, new Listener() {
|
||||
|
||||
@Override
|
||||
public void handleEvent(Event e) {
|
||||
shell.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void createEditMenu(Menu parent) {
|
||||
Menu menu = new Menu(parent.getParent(), SWT.DROP_DOWN);
|
||||
|
||||
MenuItem item = new MenuItem(parent, SWT.CASCADE);
|
||||
item.setText("&Edit");
|
||||
item.setMenu(menu);
|
||||
|
||||
item = new MenuItem(menu, SWT.PUSH);
|
||||
item.setText("Paste\tShift+Ins");
|
||||
item.setAccelerator(SWT.MOD2 + SWT.INSERT);
|
||||
item.addListener(SWT.Selection, new Listener() {
|
||||
|
||||
@Override
|
||||
public void handleEvent(Event e) {
|
||||
try {
|
||||
term.pasteFromClipboard();
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void createHelpMenu(Menu parent) {
|
||||
Menu menu = new Menu(parent.getParent(), SWT.DROP_DOWN);
|
||||
|
||||
MenuItem item = new MenuItem(parent, SWT.CASCADE);
|
||||
item.setText("&Help");
|
||||
item.setMenu(menu);
|
||||
|
||||
item = new MenuItem(menu, SWT.PUSH);
|
||||
item.setText("About " + Application.APP_TITLE);
|
||||
item.addListener(SWT.Selection, new Listener() {
|
||||
|
||||
@Override
|
||||
public void handleEvent(Event e) {
|
||||
AboutDialog dlg = new AboutDialog(shell);
|
||||
dlg.open();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected Control createContents(Composite parent) {
|
||||
container = new Composite(parent, SWT.NONE);
|
||||
GridLayout layout = new GridLayout(1, false);
|
||||
layout.marginWidth = layout.marginHeight = 0;
|
||||
container.setLayout(layout);
|
||||
container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
||||
|
||||
term = new Terminal(container) {
|
||||
|
||||
@Override
|
||||
protected void writeByte(byte b) {
|
||||
try {
|
||||
while (is.available() >= 16) {
|
||||
Thread.yield();
|
||||
}
|
||||
os.write(b);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
Rectangle rect = term.getBounds();
|
||||
GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
|
||||
gridData.widthHint = rect.width;
|
||||
gridData.heightHint = rect.height;
|
||||
term.setLayoutData(gridData);
|
||||
|
||||
createBottomControls(container);
|
||||
|
||||
try {
|
||||
is = new PipedInputStream();
|
||||
os = new PipedOutputStream(is);
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
|
||||
container.addDisposeListener(new DisposeListener() {
|
||||
|
||||
@Override
|
||||
public void widgetDisposed(DisposeEvent e) {
|
||||
if (machine != null) {
|
||||
machine.stop();
|
||||
}
|
||||
try {
|
||||
if (os != null) {
|
||||
os.close();
|
||||
}
|
||||
|
||||
} catch (Exception e1) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
void createBottomControls(Composite parent) {
|
||||
Composite container = new Composite(parent, SWT.NONE);
|
||||
GridLayout layout = new GridLayout(1, false);
|
||||
layout.marginBottom = layout.marginHeight;
|
||||
layout.marginLeft = layout.marginRight = layout.marginWidth;
|
||||
layout.marginWidth = layout.marginHeight = 0;
|
||||
container.setLayout(layout);
|
||||
container.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
|
||||
|
||||
Label label = new Label(container, SWT.NONE);
|
||||
label.setText("Keys");
|
||||
|
||||
cursorKeys = new Combo(container, SWT.DROP_DOWN | SWT.READ_ONLY);
|
||||
cursorKeys.setItems(new String[] {
|
||||
"VT-100",
|
||||
"WordStar"
|
||||
});
|
||||
cursorKeys.select(0);
|
||||
cursorKeys.addSelectionListener(new SelectionAdapter() {
|
||||
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
switch (cursorKeys.getSelectionIndex()) {
|
||||
case 0:
|
||||
term.setCursorKeys(Terminal.CURSORS_VT100);
|
||||
break;
|
||||
case 1:
|
||||
term.setCursorKeys(Terminal.CURSORS_WORDSTAR);
|
||||
break;
|
||||
}
|
||||
term.setFocus();
|
||||
}
|
||||
});
|
||||
|
||||
label = new Label(container, SWT.NONE);
|
||||
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
|
||||
|
||||
Button button = new Button(container, SWT.PUSH);
|
||||
button.setText("Reset");
|
||||
button.addSelectionListener(new SelectionAdapter() {
|
||||
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
try {
|
||||
while (is.available() > 0) {
|
||||
is.read();
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
machine.reset();
|
||||
term.setFocus();
|
||||
}
|
||||
});
|
||||
|
||||
layout.numColumns = container.getChildren().length;
|
||||
}
|
||||
|
||||
public void setFocus() {
|
||||
term.setFocus();
|
||||
}
|
||||
|
||||
byte[] compile(File file) {
|
||||
System.out.print("Compiling " + file.getName() + "...");
|
||||
|
||||
try {
|
||||
final List<File> includePaths = new ArrayList<File>();
|
||||
if (file != null) {
|
||||
includePaths.add(file.getParentFile());
|
||||
}
|
||||
|
||||
String[] includes = preferences.getIncludes();
|
||||
if (includes != null) {
|
||||
for (int i = 0; i < includes.length; i++) {
|
||||
includePaths.add(new File(includes[i]));
|
||||
}
|
||||
}
|
||||
|
||||
SourceBuilder builder = new SourceBuilder(includePaths) {
|
||||
|
||||
@Override
|
||||
public Source parse(File sourceFile) {
|
||||
System.out.print("\r\nCompiling " + sourceFile.getName() + "...");
|
||||
return super.parse(sourceFile);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Source source = builder.parse(new InputStreamReader(new FileInputStream(file)), file);
|
||||
source.register();
|
||||
source.expand();
|
||||
source.resolve();
|
||||
|
||||
System.out.println();
|
||||
|
||||
return new BinaryBuilder(source).build();
|
||||
|
||||
} catch (AssemblyException ex) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
Iterator<AssemblyException.Context> iter = ex.contexts.iterator();
|
||||
if (iter.hasNext()) {
|
||||
AssemblyException.Context context = iter.next();
|
||||
sb.append(context.file.getName());
|
||||
sb.append(":");
|
||||
sb.append(context.line + 1);
|
||||
if (context.column != -1) {
|
||||
sb.append(":");
|
||||
sb.append(context.column);
|
||||
}
|
||||
sb.append(": error: ");
|
||||
sb.append(ex.getPlainMessage());
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
System.err.println(sb.toString());
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Shell getShell() {
|
||||
return shell;
|
||||
}
|
||||
|
||||
static {
|
||||
System.setProperty("SWT_GTK3", "0");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
final Display display = new Display();
|
||||
|
||||
Realm.runWithDefault(DisplayRealm.getRealm(display), new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Emulator emulator = new Emulator();
|
||||
emulator.open();
|
||||
|
||||
while (display.getShells().length != 0) {
|
||||
if (!display.readAndDispatch()) {
|
||||
display.sleep();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
display.dispose();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* Copyright (c) 2018-19 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.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
import z80core.MemIoOps;
|
||||
import z80core.Z80;
|
||||
|
||||
public class Machine extends MemIoOps {
|
||||
|
||||
public final static int SIOA_C = 0x80;
|
||||
public final static int SIOA_D = 0x81;
|
||||
public final static int SIOB_C = 0x82;
|
||||
public final static int SIOB_D = 0x83;
|
||||
|
||||
public final static int CF_DATA = 0x10;
|
||||
public final static int CF_FEATURES = 0x11;
|
||||
public final static int CF_ERROR = 0x11;
|
||||
public final static int CF_SECCOUNT = 0x12;
|
||||
public final static int CF_SECTOR = 0x13;
|
||||
public final static int CF_CYL_LOW = 0x14;
|
||||
public final static int CF_CYL_HI = 0x15;
|
||||
public final static int CF_HEAD = 0x16;
|
||||
public final static int CF_STATUS = 0x17;
|
||||
public final static int CF_COMMAND = 0x17;
|
||||
public final static int CF_LBA0 = 0x13;
|
||||
public final static int CF_LBA1 = 0x14;
|
||||
public final static int CF_LBA2 = 0x15;
|
||||
public final static int CF_LBA3 = 0x16;
|
||||
|
||||
public final static int CF_READ_SEC = 0x20;
|
||||
public final static int CF_WRITE_SEC = 0x30;
|
||||
|
||||
boolean page;
|
||||
byte[] rom;
|
||||
byte[] ram;
|
||||
|
||||
byte cfCommand;
|
||||
byte[] cfLBA = new byte[4];
|
||||
File cfFile;
|
||||
RandomAccessFile cf;
|
||||
|
||||
Z80 proc;
|
||||
Thread thread;
|
||||
double clockNs;
|
||||
long tstates;
|
||||
|
||||
public Machine() {
|
||||
rom = new byte[16384];
|
||||
ram = new byte[65536];
|
||||
|
||||
clockNs = 1000.0 / 7.3728;
|
||||
|
||||
proc = new Z80(this, null);
|
||||
|
||||
thread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Machine.this.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setRom(int address, File file) throws IOException {
|
||||
InputStream is = new FileInputStream(file);
|
||||
is.read(rom, address, rom.length - address);
|
||||
is.close();
|
||||
}
|
||||
|
||||
public void setRom(int address, byte[] rom) throws IOException {
|
||||
System.arraycopy(rom, 0, this.rom, address, Math.min(this.rom.length - address, rom.length));
|
||||
}
|
||||
|
||||
public void setClock(double freq) {
|
||||
clockNs = 1000.0 / freq;
|
||||
}
|
||||
|
||||
public void setCompactFlash(File file) {
|
||||
this.cfFile = file;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
try {
|
||||
if (cfFile != null) {
|
||||
cf = new RandomAccessFile(cfFile, "rw");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
thread.start();
|
||||
}
|
||||
|
||||
protected void run() {
|
||||
long ns = System.nanoTime();
|
||||
|
||||
while (!Thread.interrupted()) {
|
||||
synchronized (proc) {
|
||||
long current = System.nanoTime();
|
||||
long tstates = getTstates() + (long) ((current - ns) / clockNs);
|
||||
while (getTstates() < tstates) {
|
||||
proc.execute();
|
||||
}
|
||||
ns = current;
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
synchronized (proc) {
|
||||
page = false;
|
||||
tstates = 0;
|
||||
proc.reset();
|
||||
super.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fetchOpcode(int address) {
|
||||
tstates += 4; // 3 clocks to fetch opcode from RAM and 1 execution clock
|
||||
if (!page && address < rom.length) {
|
||||
return rom[address & 0xFFFF] & 0xff;
|
||||
}
|
||||
return ram[address & 0xFFFF] & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int inPort(int port) {
|
||||
tstates += 4; // 4 clocks for read byte from bus
|
||||
|
||||
switch (port & 0xFF) {
|
||||
case CF_DATA:
|
||||
if (cfCommand == CF_READ_SEC) {
|
||||
try {
|
||||
if (cf != null) {
|
||||
return (byte) cf.read();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CF_STATUS:
|
||||
return 0x40; // CF ready
|
||||
case SIOA_C:
|
||||
return 0x04; // Always return TX buffer empty
|
||||
case SIOB_C:
|
||||
return 0x04; // Always return TX buffer empty
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void outPort(int port, int value) {
|
||||
tstates += 4; // 4 clocks for write byte to bus
|
||||
|
||||
switch (port & 0xFF) {
|
||||
case CF_DATA:
|
||||
if (cfCommand == CF_WRITE_SEC) {
|
||||
try {
|
||||
if (cf != null) {
|
||||
cf.write(value & 0xFF);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CF_COMMAND:
|
||||
cfCommand = (byte) value;
|
||||
if (cfCommand == CF_WRITE_SEC || cfCommand == CF_READ_SEC) {
|
||||
try {
|
||||
long addr = ((cfLBA[3] & 0x0F) << 24) | ((cfLBA[2] & 0xFF) << 16) | ((cfLBA[1] & 0xFF) << 8) | (cfLBA[0] & 0xFF);
|
||||
if (cf != null) {
|
||||
cf.seek(addr << 9);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CF_LBA0:
|
||||
cfLBA[0] = (byte) value;
|
||||
break;
|
||||
case CF_LBA1:
|
||||
cfLBA[1] = (byte) value;
|
||||
break;
|
||||
case CF_LBA2:
|
||||
cfLBA[2] = (byte) value;
|
||||
break;
|
||||
case CF_LBA3: {
|
||||
cfLBA[3] = (byte) value;
|
||||
break;
|
||||
}
|
||||
case 0x38: // ROM page
|
||||
page = !page;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int peek8(int address) {
|
||||
tstates += 3; // 3 clocks for read byte from RAM
|
||||
if (!page && address < rom.length) {
|
||||
return rom[address & 0xFFFF] & 0xff;
|
||||
}
|
||||
return ram[address & 0xFFFF] & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void poke8(int address, int value) {
|
||||
tstates += 3; // 3 clocks for write byte to RAM
|
||||
if (page || address >= rom.length) {
|
||||
ram[address & 0xFFFF] = (byte) value;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTstates() {
|
||||
return tstates;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
try {
|
||||
thread.interrupt();
|
||||
} catch (Exception e) {
|
||||
// Do nothing
|
||||
}
|
||||
try {
|
||||
if (cf != null) {
|
||||
cf.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Marco Maccaferri and others.
|
||||
* Copyright (c) 2018-19 Marco Maccaferri and others.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program and the accompanying materials are made available under
|
||||
|
@ -89,6 +89,12 @@ public class Preferences {
|
|||
String downloadCommand;
|
||||
String xmodemCommand;
|
||||
|
||||
String romImage1;
|
||||
int romAddress1;
|
||||
String romImage2;
|
||||
int romAddress2;
|
||||
String compactFlashImage;
|
||||
|
||||
int lastUploadType;
|
||||
String lastPath;
|
||||
List<String> lru;
|
||||
|
@ -368,6 +374,46 @@ public class Preferences {
|
|||
this.lastUploadType = lastUploadType;
|
||||
}
|
||||
|
||||
public String getRomImage1() {
|
||||
return romImage1;
|
||||
}
|
||||
|
||||
public void setRomImage1(String romImage) {
|
||||
this.romImage1 = romImage;
|
||||
}
|
||||
|
||||
public int getRomAddress1() {
|
||||
return romAddress1;
|
||||
}
|
||||
|
||||
public void setRomAddress1(int romAddress) {
|
||||
this.romAddress1 = romAddress;
|
||||
}
|
||||
|
||||
public String getRomImage2() {
|
||||
return romImage2;
|
||||
}
|
||||
|
||||
public void setRomImage2(String romImage) {
|
||||
this.romImage2 = romImage;
|
||||
}
|
||||
|
||||
public int getRomAddress2() {
|
||||
return romAddress2;
|
||||
}
|
||||
|
||||
public void setRomAddress2(int romAddress) {
|
||||
this.romAddress2 = romAddress;
|
||||
}
|
||||
|
||||
public String getCompactFlashImage() {
|
||||
return compactFlashImage;
|
||||
}
|
||||
|
||||
public void setCompactFlashImage(String compactFlashImage) {
|
||||
this.compactFlashImage = compactFlashImage;
|
||||
}
|
||||
|
||||
public void save() throws IOException {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Marco Maccaferri and others.
|
||||
* Copyright (c) 2018-19 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
|
||||
|
@ -27,6 +27,7 @@ import org.eclipse.swt.widgets.Combo;
|
|||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.swt.widgets.DirectoryDialog;
|
||||
import org.eclipse.swt.widgets.FileDialog;
|
||||
import org.eclipse.swt.widgets.FontDialog;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.List;
|
||||
|
@ -73,6 +74,12 @@ public class PreferencesDialog extends Dialog {
|
|||
Text downloadCommand;
|
||||
Text xmodemCommand;
|
||||
|
||||
Text romImage1;
|
||||
Text romAddress1;
|
||||
Text romImage2;
|
||||
Text romAddress2;
|
||||
Text compactFlashImage;
|
||||
|
||||
Preferences preferences;
|
||||
String defaultFont;
|
||||
|
||||
|
@ -96,7 +103,7 @@ public class PreferencesDialog extends Dialog {
|
|||
layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
|
||||
layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
|
||||
composite.setLayout(layout);
|
||||
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
||||
|
||||
if ("win32".equals(SWT.getPlatform())) {
|
||||
defaultFont = StringConverter.asString(new FontData("Courier New", 9, SWT.NONE));
|
||||
|
@ -119,6 +126,7 @@ public class PreferencesDialog extends Dialog {
|
|||
createGeneralPage(stack);
|
||||
createAssemblerPage(stack);
|
||||
createEditorPage(stack);
|
||||
createEmulatorPage(stack);
|
||||
createFormatterPage(stack);
|
||||
|
||||
stackLayout.topControl = stack.getChildren()[0];
|
||||
|
@ -146,7 +154,7 @@ public class PreferencesDialog extends Dialog {
|
|||
layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
|
||||
layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
|
||||
composite.setLayout(layout);
|
||||
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
||||
|
||||
pages.add("General");
|
||||
|
||||
|
@ -290,7 +298,7 @@ public class PreferencesDialog extends Dialog {
|
|||
|
||||
label = new Label(composite, SWT.NONE);
|
||||
label.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false, 2, 1));
|
||||
((GridData) label.getLayoutData()).heightHint = convertHeightInCharsToPixels(7);
|
||||
((GridData) label.getLayoutData()).heightHint = convertHeightInCharsToPixels(1);
|
||||
}
|
||||
|
||||
void updateRootDirectoryButtons() {
|
||||
|
@ -307,7 +315,7 @@ public class PreferencesDialog extends Dialog {
|
|||
layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
|
||||
layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
|
||||
composite.setLayout(layout);
|
||||
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
||||
|
||||
pages.add("Assembler");
|
||||
|
||||
|
@ -453,7 +461,7 @@ public class PreferencesDialog extends Dialog {
|
|||
layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
|
||||
layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
|
||||
composite.setLayout(layout);
|
||||
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
||||
|
||||
pages.add("Editor");
|
||||
|
||||
|
@ -514,7 +522,7 @@ public class PreferencesDialog extends Dialog {
|
|||
layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
|
||||
layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
|
||||
composite.setLayout(layout);
|
||||
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
||||
|
||||
pages.add("Formatter");
|
||||
|
||||
|
@ -567,6 +575,143 @@ public class PreferencesDialog extends Dialog {
|
|||
directivePrefix.select(preferences.getDirectivePrefix());
|
||||
}
|
||||
|
||||
void createEmulatorPage(Composite parent) {
|
||||
Composite composite = new Composite(parent, SWT.NONE);
|
||||
GridLayout layout = new GridLayout(2, false);
|
||||
layout.marginHeight = layout.marginWidth = 0;
|
||||
layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
|
||||
layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
|
||||
composite.setLayout(layout);
|
||||
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
|
||||
|
||||
pages.add("Emulator");
|
||||
|
||||
Label label = new Label(composite, SWT.NONE);
|
||||
label.setText("ROM images");
|
||||
Composite container = new Composite(composite, SWT.NONE);
|
||||
layout = new GridLayout(3, false);
|
||||
layout.marginWidth = layout.marginHeight = 0;
|
||||
container.setLayout(layout);
|
||||
container.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
|
||||
|
||||
romAddress1 = new Text(container, SWT.BORDER);
|
||||
romAddress1.setLayoutData(new GridData(convertWidthInCharsToPixels(5), SWT.DEFAULT));
|
||||
romAddress1.setText(String.format("%04X", preferences.getRomAddress1()));
|
||||
|
||||
romImage1 = new Text(container, SWT.BORDER);
|
||||
romImage1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
|
||||
if (preferences.getRomImage1() != null) {
|
||||
romImage1.setText(preferences.getRomImage1());
|
||||
}
|
||||
|
||||
Button button = new Button(container, SWT.PUSH);
|
||||
button.setText("Browse");
|
||||
button.addSelectionListener(new SelectionAdapter() {
|
||||
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
FileDialog dlg = new FileDialog(getShell(), SWT.OPEN);
|
||||
dlg.setText("Open ROM image");
|
||||
dlg.setFilterNames(new String[] {
|
||||
"All files",
|
||||
"Rom images",
|
||||
"Source files"
|
||||
});
|
||||
dlg.setFilterExtensions(new String[] {
|
||||
"*.*",
|
||||
"*.BIN;*.bin",
|
||||
"*.ASM;*.asm"
|
||||
});
|
||||
dlg.setFilterIndex(1);
|
||||
final String fileName = dlg.open();
|
||||
if (fileName != null) {
|
||||
romImage1.setText(fileName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
new Label(composite, SWT.NONE);
|
||||
container = new Composite(composite, SWT.NONE);
|
||||
layout = new GridLayout(3, false);
|
||||
layout.marginWidth = layout.marginHeight = 0;
|
||||
container.setLayout(layout);
|
||||
container.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
|
||||
|
||||
romAddress2 = new Text(container, SWT.BORDER);
|
||||
romAddress2.setLayoutData(new GridData(convertWidthInCharsToPixels(5), SWT.DEFAULT));
|
||||
romAddress2.setText(String.format("%04X", preferences.getRomAddress2()));
|
||||
|
||||
romImage2 = new Text(container, SWT.BORDER);
|
||||
romImage2.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
|
||||
if (preferences.getRomImage2() != null) {
|
||||
romImage2.setText(preferences.getRomImage2());
|
||||
}
|
||||
|
||||
button = new Button(container, SWT.PUSH);
|
||||
button.setText("Browse");
|
||||
button.addSelectionListener(new SelectionAdapter() {
|
||||
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
FileDialog dlg = new FileDialog(getShell(), SWT.OPEN);
|
||||
dlg.setText("Open ROM image");
|
||||
dlg.setFilterNames(new String[] {
|
||||
"All files",
|
||||
"Rom images",
|
||||
"Source files"
|
||||
});
|
||||
dlg.setFilterExtensions(new String[] {
|
||||
"*.*",
|
||||
"*.BIN;*.bin",
|
||||
"*.ASM;*.asm"
|
||||
});
|
||||
dlg.setFilterIndex(1);
|
||||
final String fileName = dlg.open();
|
||||
if (fileName != null) {
|
||||
romImage2.setText(fileName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
label = new Label(composite, SWT.NONE);
|
||||
label.setText("CF card image");
|
||||
container = new Composite(composite, SWT.NONE);
|
||||
layout = new GridLayout(2, false);
|
||||
layout.marginWidth = layout.marginHeight = 0;
|
||||
container.setLayout(layout);
|
||||
container.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
|
||||
|
||||
compactFlashImage = new Text(container, SWT.BORDER);
|
||||
compactFlashImage.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
|
||||
if (preferences.getCompactFlashImage() != null) {
|
||||
compactFlashImage.setText(preferences.getCompactFlashImage());
|
||||
}
|
||||
|
||||
button = new Button(container, SWT.PUSH);
|
||||
button.setText("Browse");
|
||||
button.addSelectionListener(new SelectionAdapter() {
|
||||
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
FileDialog dlg = new FileDialog(getShell(), SWT.OPEN);
|
||||
dlg.setText("Open Compact Flash image");
|
||||
dlg.setFilterNames(new String[] {
|
||||
"All files",
|
||||
"Compact Flash images"
|
||||
});
|
||||
dlg.setFilterExtensions(new String[] {
|
||||
"*.*",
|
||||
"*.IMG;*.img"
|
||||
});
|
||||
dlg.setFilterIndex(1);
|
||||
final String fileName = dlg.open();
|
||||
if (fileName != null) {
|
||||
compactFlashImage.setText(fileName);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void addSeparator(Composite parent) {
|
||||
Label label = new Label(parent, SWT.NONE);
|
||||
label.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false, ((GridLayout) parent.getLayout()).numColumns, 1));
|
||||
|
@ -598,6 +743,12 @@ public class PreferencesDialog extends Dialog {
|
|||
preferences.setDownloadCommand(downloadCommand.getText());
|
||||
preferences.setXmodemCommand(xmodemCommand.getText());
|
||||
|
||||
preferences.setRomAddress1(Integer.valueOf(romAddress1.getText(), 16));
|
||||
preferences.setRomImage1(romImage1.getText());
|
||||
preferences.setRomAddress2(Integer.valueOf(romAddress2.getText(), 16));
|
||||
preferences.setRomImage2(romImage2.getText());
|
||||
preferences.setCompactFlashImage(compactFlashImage.getText());
|
||||
|
||||
super.okPressed();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,13 +11,12 @@ package z80core;
|
|||
*/
|
||||
public class MemIoOps {
|
||||
|
||||
private byte z80Ram[] = null;
|
||||
private byte z80Ports[] = null;
|
||||
private byte[] z80Ram;
|
||||
private byte[] z80Ports;
|
||||
private long tstates = 0;
|
||||
|
||||
public MemIoOps() {
|
||||
z80Ram = new byte[0x10000];
|
||||
z80Ports = new byte[0x10000];
|
||||
|
||||
}
|
||||
|
||||
public MemIoOps(int ramSize, int portSize) {
|
||||
|
@ -42,11 +41,11 @@ public class MemIoOps {
|
|||
return z80Ram;
|
||||
}
|
||||
|
||||
public void setRam(byte ram[]) {
|
||||
public void setRam(byte[] ram) {
|
||||
z80Ram = ram;
|
||||
}
|
||||
|
||||
public void setPorts(byte ports[]) {
|
||||
public void setPorts(byte[] ports) {
|
||||
z80Ram = ports;
|
||||
}
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue