Implemented binary upload protocols

master
Marco Maccaferri 2018-12-26 08:31:44 +01:00
rodzic 8551fa2c23
commit 295974618b
5 zmienionych plików z 620 dodań i 89 usunięć

Wyświetl plik

@ -797,6 +797,36 @@ public class Application {
new MenuItem(menu, SWT.SEPARATOR);
item = new MenuItem(menu, SWT.PUSH);
item.setText("Upload Packed CP/M binary");
item.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event e) {
try {
handleCompileAndUploadBinary();
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Upload XModem binary");
item.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event e) {
try {
handleCompileAndUploadXModem();
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
new MenuItem(menu, SWT.SEPARATOR);
item = new MenuItem(menu, SWT.PUSH);
item.setText("Serial Terminal\tCtrl+Shift+T");
item.setAccelerator(SWT.MOD1 + SWT.MOD2 + 'T');
@ -850,20 +880,7 @@ public class Application {
}
});
toolItem = new ToolItem(toolBar, SWT.PUSH);
toolItem.setImage(ImageRegistry.getImageFromResources("application_go.png"));
toolItem.setToolTipText("Upload Intel HEX");
toolItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
try {
handleCompileAndUploadIntelHex();
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
createUploadToolItem(toolBar);
new ToolItem(toolBar, SWT.SEPARATOR);
@ -932,6 +949,98 @@ public class Application {
return toolBar;
}
void createUploadToolItem(ToolBar toolBar) {
final ToolItem toolItem = new ToolItem(toolBar, SWT.DROP_DOWN);
toolItem.setImage(ImageRegistry.getImageFromResources("application_go.png"));
final Menu menu = new Menu(shell, SWT.POP_UP);
MenuItem menuItem = new MenuItem(menu, SWT.RADIO);
menuItem.setText("Upload Intel HEX");
menuItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
try {
if (((MenuItem) e.widget).getSelection()) {
toolItem.setToolTipText(((MenuItem) e.widget).getText());
preferences.setLastUploadType(menu.indexOf((MenuItem) e.widget));
handleCompileAndUploadIntelHex();
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
menuItem = new MenuItem(menu, SWT.RADIO);
menuItem.setText("Upload Packed CP/M binary");
menuItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
try {
if (((MenuItem) e.widget).getSelection()) {
toolItem.setToolTipText(((MenuItem) e.widget).getText());
preferences.setLastUploadType(menu.indexOf((MenuItem) e.widget));
handleCompileAndUploadBinary();
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
menuItem = new MenuItem(menu, SWT.RADIO);
menuItem.setText("Upload XModem binary");
menuItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
try {
if (((MenuItem) e.widget).getSelection()) {
toolItem.setToolTipText(((MenuItem) e.widget).getText());
preferences.setLastUploadType(menu.indexOf((MenuItem) e.widget));
handleCompileAndUploadXModem();
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
int selection = preferences.getLastUploadType();
if (selection < 0 || selection >= menu.getItemCount()) {
selection = 0;
}
menu.getItem(selection).setSelection(true);
toolItem.setToolTipText(menu.getItem(selection).getText());
toolItem.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event event) {
if (event.detail == SWT.ARROW) {
Rectangle rect = toolItem.getBounds();
Point pt = new Point(rect.x, rect.y + rect.height);
pt = toolBar.toDisplay(pt);
menu.setLocation(pt.x, pt.y);
menu.setVisible(true);
}
else {
MenuItem[] menuItem = menu.getItems();
for (int i = 0; i < menuItem.length; i++) {
if (menuItem[i].getSelection()) {
menuItem[i].notifyListeners(SWT.Selection, new Event());
break;
}
}
}
}
});
}
protected Control createContents(Composite parent) {
GC gc = new GC(parent);
FontMetrics fontMetrics = gc.getFontMetrics();
@ -1605,16 +1714,6 @@ public class Application {
private void handleOpenTerminal() {
if (terminal == null) {
terminal = new SerialTerminal();
terminal.setPort(preferences.getSerialPort());
terminal.setBaud(preferences.getSerialBaud());
terminal.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
preferences.setSerialPort(terminal.getPort());
preferences.setSerialBaud(terminal.getBaud());
}
});
terminal.open();
terminal.getShell().addDisposeListener(new DisposeListener() {
@ -1661,7 +1760,7 @@ public class Application {
return;
}
display.asyncExec(new Runnable() {
display.syncExec(new Runnable() {
@Override
public void run() {
@ -1718,6 +1817,148 @@ public class Application {
}
}
private void handleCompileAndUploadBinary() throws Exception {
CTabItem tabItem = tabFolder.getSelection();
if (tabItem == null) {
return;
}
SourceEditorTab tab = (SourceEditorTab) tabItem.getData();
console.clear();
final List<File> includePaths = new ArrayList<File>();
if (tab.getFile() != null) {
includePaths.add(tab.getFile().getParentFile());
}
final StringReader reader = new StringReader(tab.getEditor().getText());
final String name = tab.getText();
final File file = tab.getFile();
new Thread(new Runnable() {
PrintStream out;
IProgressMonitor monitor;
SerialPort serialPort;
@Override
public void run() {
out = new PrintStream(console.getOutputStream());
monitor = statusLine.getProgressMonitor();
monitor.beginTask("Compile", IProgressMonitor.UNKNOWN);
try {
String baseName = name;
if (baseName.indexOf('.') != -1) {
baseName = baseName.substring(0, baseName.lastIndexOf('.'));
}
Source source = compile(reader, name, file, includePaths);
if (source == null) {
return;
}
display.syncExec(new Runnable() {
@Override
public void run() {
handleOpenTerminal();
}
});
serialPort = terminal.getSerialPort();
byte[] data = source.generateObjectCode();
monitor.beginTask("Upload", (data.length + 127) / 128);
out.println("Sending to serial port " + serialPort.getPortName() + " ...");
terminal.uploadPackedBinary(baseName + ".COM", data, monitor);
} catch (Exception e) {
e.printStackTrace();
}
out.println("Done");
monitor.done();
}
}).start();
}
private void handleCompileAndUploadXModem() throws Exception {
CTabItem tabItem = tabFolder.getSelection();
if (tabItem == null) {
return;
}
SourceEditorTab tab = (SourceEditorTab) tabItem.getData();
console.clear();
final List<File> includePaths = new ArrayList<File>();
if (tab.getFile() != null) {
includePaths.add(tab.getFile().getParentFile());
}
final StringReader reader = new StringReader(tab.getEditor().getText());
final String name = tab.getText();
final File file = tab.getFile();
new Thread(new Runnable() {
PrintStream out;
IProgressMonitor monitor;
SerialPort serialPort;
@Override
public void run() {
out = new PrintStream(console.getOutputStream());
monitor = statusLine.getProgressMonitor();
monitor.beginTask("Compile", IProgressMonitor.UNKNOWN);
try {
String baseName = name;
if (baseName.indexOf('.') != -1) {
baseName = baseName.substring(0, baseName.lastIndexOf('.'));
}
Source source = compile(reader, name, file, includePaths);
if (source == null) {
return;
}
display.syncExec(new Runnable() {
@Override
public void run() {
handleOpenTerminal();
}
});
serialPort = terminal.getSerialPort();
byte[] data = source.generateObjectCode();
monitor.beginTask("Upload", (data.length + 127) / 128);
out.println("Sending to serial port " + serialPort.getPortName() + " ...");
terminal.uploadXModem(baseName + ".COM", data, monitor);
} catch (Exception e) {
e.printStackTrace();
}
out.println("Done");
monitor.done();
}
}).start();
}
static {
System.setProperty("SWT_GTK3", "0");
}

Wyświetl plik

@ -79,9 +79,13 @@ public class Preferences {
boolean generateHex;
boolean generateListing;
String lastPath;
String serialPort;
int serialBaud;
String downloadCommand;
String xmodemCommand;
int lastUploadType;
String lastPath;
List<String> lru;
final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
@ -101,6 +105,9 @@ public class Preferences {
generateListing = true;
serialBaud = 115200;
downloadCommand = "A:DOWNLOAD {0}";
xmodemCommand = "A:XMODEM {0} /R /X0 /Q";
lru = new ArrayList<String>();
}
@ -299,6 +306,30 @@ public class Preferences {
this.serialBaud = serialBaud;
}
public String getDownloadCommand() {
return downloadCommand;
}
public void setDownloadCommand(String downloadCommand) {
this.downloadCommand = downloadCommand;
}
public String getXmodemCommand() {
return xmodemCommand;
}
public void setXmodemCommand(String xmodemCommand) {
this.xmodemCommand = xmodemCommand;
}
public int getLastUploadType() {
return lastUploadType;
}
public void setLastUploadType(int lastUploadType) {
this.lastUploadType = lastUploadType;
}
public void save() throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);

Wyświetl plik

@ -58,6 +58,8 @@ public class PreferencesDialog extends Dialog {
Button generateBinary;
Button generateHex;
Button generateListing;
Text downloadCommand;
Text xmodemCommand;
Preferences preferences;
String defaultFont;
@ -160,7 +162,17 @@ public class PreferencesDialog extends Dialog {
createCompilerGroup(composite);
addSeparator(composite);
label = new Label(composite, SWT.NONE);
label.setText("Download cmd.");
downloadCommand = new Text(composite, SWT.BORDER);
downloadCommand.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
downloadCommand.setText(preferences.getDownloadCommand());
label = new Label(composite, SWT.NONE);
label.setText("XModem cmd.");
xmodemCommand = new Text(composite, SWT.BORDER);
xmodemCommand.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
xmodemCommand.setText(preferences.getXmodemCommand());
return composite;
}
@ -369,6 +381,8 @@ public class PreferencesDialog extends Dialog {
preferences.setGenerateBinary(generateBinary.getSelection());
preferences.setGenerateHex(generateHex.getSelection());
preferences.setGenerateListing(generateListing.getSelection());
preferences.setDownloadCommand(downloadCommand.getText());
preferences.setXmodemCommand(xmodemCommand.getText());
super.okPressed();
}

Wyświetl plik

@ -10,9 +10,7 @@
package com.maccasoft.tools;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
@ -39,13 +37,12 @@ import com.maccasoft.tools.internal.ImageRegistry;
import jssc.SerialPort;
import jssc.SerialPortEvent;
import jssc.SerialPortEventListener;
import jssc.SerialPortException;
import jssc.SerialPortList;
import jssc.SerialPortTimeoutException;
public class SerialTerminal extends Window {
public static final String PROP_PORT = "port";
public static final String PROP_BAUD = "baud";
public static final String[] BAUD_RATES = new String[] {
String.valueOf(SerialPort.BAUDRATE_9600),
String.valueOf(SerialPort.BAUDRATE_19200),
@ -54,6 +51,13 @@ public class SerialTerminal extends Window {
String.valueOf(SerialPort.BAUDRATE_115200)
};
public static final byte SOH = 0x01;
public static final byte EOT = 0x04;
public static final byte ACK = 0x06;
public static final byte NAK = 0x15;
public static final byte CAN = 0x18;
public static final byte C = 0x43; // 'C' which use in XModem/CRC
Display display;
Composite container;
Terminal term;
@ -66,16 +70,15 @@ public class SerialTerminal extends Window {
int baud;
SerialPort serialPort;
final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
Preferences preferences;
final SelectionAdapter comPortSelectionListener = new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
String oldPort = port;
port = comPort.getText();
updateSerialPortSettings();
changeSupport.firePropertyChange(PROP_PORT, oldPort, port);
preferences.setSerialPort(port);
term.setFocus();
}
};
@ -84,10 +87,9 @@ public class SerialTerminal extends Window {
@Override
public void widgetSelected(SelectionEvent e) {
int oldBaud = baud;
baud = Integer.valueOf(baudRate.getText());
updateSerialPortSettings();
changeSupport.firePropertyChange(PROP_BAUD, oldBaud, baud);
preferences.setSerialBaud(baud);
term.setFocus();
}
};
@ -268,6 +270,10 @@ public class SerialTerminal extends Window {
protected Control createContents(Composite parent) {
display = parent.getDisplay();
preferences = Preferences.getInstance();
port = preferences.getSerialPort();
baud = preferences.getSerialBaud();
container = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(1, false);
layout.marginWidth = layout.marginHeight = 0;
@ -291,12 +297,11 @@ public class SerialTerminal extends Window {
@Override
public void widgetDisposed(DisposeEvent e) {
try {
PropertyChangeListener[] l = changeSupport.getPropertyChangeListeners();
for (int i = 0; i < l.length; i++) {
changeSupport.removePropertyChangeListener(l[i]);
}
serialPort.removeEventListener();
} catch (Exception ex) {
// Do nothing
}
try {
if (serialPort.isOpened()) {
serialPort.closePort();
}
@ -414,50 +419,6 @@ public class SerialTerminal extends Window {
getShell().setText("Serial Terminal on " + port);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(propertyName, listener);
}
public String getPort() {
return port;
}
public void setPort(String port) {
if (comPort != null && !comPort.isDisposed()) {
int index = comPort.indexOf(port);
if (index != -1) {
comPort.select(index);
}
}
this.port = port;
}
public int getBaud() {
return baud;
}
public void setBaud(int baud) {
if (baudRate != null && !baudRate.isDisposed()) {
int index = baudRate.indexOf(String.valueOf(baud));
if (index != -1) {
baudRate.select(index);
}
}
this.baud = baud;
}
public void setFocus() {
term.setFocus();
}
@ -492,4 +453,284 @@ public class SerialTerminal extends Window {
public void dispose() {
getShell().dispose();
}
public void uploadPackedBinary(String name, byte[] data, IProgressMonitor monitor) throws SerialPortException, SerialPortTimeoutException {
int checksum;
String s;
try {
serialPort.removeEventListener();
} catch (SerialPortException e) {
// Do nothing
}
try {
s = preferences.getDownloadCommand();
s = s.replace("{0}", name.toUpperCase());
serialPort.writeString(s);
serialPort.writeInt(13);
flushOutput();
do {
s = readString();
} while (s.length() != 0 && !s.contains("DOWNLOAD "));
serialPort.writeString("U0\r");
flushOutput();
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
// Do nothing
}
serialPort.writeString(":");
flushOutput();
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
// Do nothing
}
checksum = 0;
for (int i = 0; i < data.length; i++) {
serialPort.writeString(String.format("%02X", data[i] & 0xFF));
flushOutput();
checksum += data[i] & 0xFF;
if (i > 0 && (i & 127) == 0) {
waitDot();
if (monitor != null) {
monitor.worked(1);
}
}
if (monitor != null && monitor.isCanceled()) {
return;
}
}
serialPort.writeString(">");
flushOutput();
if ((data.length & 127) != 0) {
waitDot();
if (monitor != null) {
monitor.worked(1);
}
}
} finally {
try {
serialPort.addEventListener(serialEventListener);
} catch (SerialPortException e) {
// Do nothing
}
}
serialPort.writeString(String.format("%02X", data.length & 0xFF));
flushOutput();
serialPort.writeString(String.format("%02X", checksum & 0xFF));
flushOutput();
}
void flushOutput() throws SerialPortException {
while (serialPort.getOutputBufferBytesCount() > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// Do nothing
}
}
}
String readString() throws SerialPortException, SerialPortTimeoutException {
String s = "";
while (true) {
byte[] b = serialPort.readBytes(1, 5000);
print(b[0]);
if (b[0] == '\r') {
try {
b = serialPort.readBytes(1, 200);
print(b[0]);
} catch (SerialPortTimeoutException e) {
// Do nothing
}
break;
}
if (b[0] >= ' ') {
s = s + (char) b[0];
}
}
return s;
}
void print(byte b) {
display.syncExec(new Runnable() {
@Override
public void run() {
term.print(b);
}
});
}
void waitDot() throws SerialPortException, SerialPortTimeoutException {
while (true) {
byte[] b = serialPort.readBytes(1, 5000);
if (b != null) {
print(b[0]);
if (b[0] == '.') {
break;
}
}
}
}
public void uploadXModem(String name, byte[] data, IProgressMonitor monitor) throws SerialPortException, SerialPortTimeoutException {
String s;
try {
serialPort.removeEventListener();
} catch (SerialPortException e) {
// Do nothing
}
try {
s = preferences.getXmodemCommand();
s = s.replace("{0}", name.toUpperCase());
serialPort.writeString(s);
serialPort.writeInt(13);
flushOutput();
do {
s = readString();
} while (s.length() != 0 && !s.contains("XMODEM "));
s = "";
while (true) {
byte[] b = serialPort.readBytes(1, 15000);
print(b[0]);
if (b[0] >= ' ') {
s = s + (char) b[0];
if (s.endsWith("Overwrite (Y/N)?")) {
serialPort.writeString("Y\r");
s = "";
}
if (s.endsWith("with CRCs")) {
break;
}
}
}
boolean doCrc = false;
int packet = 1;
int errors = 0;
int i = 0;
while (i < data.length) {
try {
byte[] b = serialPort.readBytes(1, 15000);
if (b[0] == CAN) {
break;
}
if (b[0] == C) {
doCrc = true;
b[0] = NAK;
}
if (b[0] == ACK) {
i += 128;
packet++;
if (monitor != null) {
monitor.worked(1);
}
errors++;
}
if (b[0] == NAK) {
errors++;
if (errors >= 10) {
return;
}
}
if (b[0] == ACK || b[0] == NAK) {
serialPort.writeByte(SOH);
serialPort.writeByte((byte) packet);
serialPort.writeByte((byte) (packet ^ 0xFF));
int checksum = 0, crc = 0;
int x = 0;
while (x < 128 && (i + x) < data.length) {
serialPort.writeByte(data[i + x]);
checksum += data[i + x] & 0xFF;
crc = updateCrc(crc, data[i + x] & 0xFF);
x++;
}
if (x < 128) {
serialPort.writeByte((byte) 0x1A);
checksum += 0x1A;
crc = updateCrc(crc, 0x1A);
x++;
while (x < 128) {
serialPort.writeByte((byte) 0);
crc = updateCrc(crc, 0);
x++;
}
}
if (doCrc) {
serialPort.writeByte((byte) ((crc >> 8) & 0xFF));
serialPort.writeByte((byte) (crc & 0xFF));
}
else {
serialPort.writeByte((byte) checksum);
}
}
if (monitor != null && monitor.isCanceled()) {
serialPort.writeBytes(new byte[] {
CAN, CAN, CAN
});
return;
}
} catch (SerialPortTimeoutException e) {
errors++;
if (errors >= 10) {
return;
}
}
}
if (i >= data.length) {
serialPort.writeByte(EOT);
byte[] b = serialPort.readBytes(1, 5000);
if (b[0] == NAK) {
serialPort.writeByte(EOT);
}
b = serialPort.readBytes(1, 5000);
if (b[0] == NAK) {
serialPort.writeByte(EOT);
}
}
} finally {
try {
serialPort.addEventListener(serialEventListener);
} catch (SerialPortException e) {
// Do nothing
}
}
}
int updateCrc(int crc, int b) {
for (int i = 0; i < 8; i++) {
boolean bit = ((b >>> (7 - i) & 1) == 1);
boolean c15 = ((crc >>> 15 & 1) == 1);
crc <<= 1;
if (c15 ^ bit) {
crc ^= 0x1021;
}
}
return crc & 0xFFFF;
}
}

Wyświetl plik

@ -31,6 +31,8 @@ public class StatusLine implements IProgressMonitor {
Label caretPositionLabel;
ProgressIndicator progressBar;
boolean canceled;
public StatusLine(Composite parent) {
display = parent.getDisplay();
@ -98,6 +100,8 @@ public class StatusLine implements IProgressMonitor {
@Override
public void beginTask(String name, final int totalWork) {
canceled = false;
final boolean animated = (totalWork == UNKNOWN || totalWork == 0);
display.syncExec(new Runnable() {
@ -162,12 +166,12 @@ public class StatusLine implements IProgressMonitor {
@Override
public boolean isCanceled() {
return false;
return canceled;
}
@Override
public void setCanceled(boolean value) {
canceled = value;
}
@Override