kopia lustrzana https://github.com/maccasoft/z80-tools
737 wiersze
24 KiB
Java
737 wiersze
24 KiB
Java
/*
|
|
* Copyright (c) 2018 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 org.eclipse.core.runtime.IProgressMonitor;
|
|
import org.eclipse.jface.window.Window;
|
|
import org.eclipse.swt.SWT;
|
|
import org.eclipse.swt.dnd.Clipboard;
|
|
import org.eclipse.swt.dnd.TextTransfer;
|
|
import org.eclipse.swt.events.DisposeEvent;
|
|
import org.eclipse.swt.events.DisposeListener;
|
|
import org.eclipse.swt.events.KeyAdapter;
|
|
import org.eclipse.swt.events.KeyEvent;
|
|
import org.eclipse.swt.events.SelectionAdapter;
|
|
import org.eclipse.swt.events.SelectionEvent;
|
|
import org.eclipse.swt.graphics.Image;
|
|
import org.eclipse.swt.graphics.Rectangle;
|
|
import org.eclipse.swt.layout.GridData;
|
|
import org.eclipse.swt.layout.GridLayout;
|
|
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.Label;
|
|
import org.eclipse.swt.widgets.Shell;
|
|
|
|
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[] BAUD_RATES = new String[] {
|
|
String.valueOf(SerialPort.BAUDRATE_9600),
|
|
String.valueOf(SerialPort.BAUDRATE_19200),
|
|
String.valueOf(SerialPort.BAUDRATE_38400),
|
|
String.valueOf(SerialPort.BAUDRATE_57600),
|
|
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;
|
|
|
|
Combo cursorKeys;
|
|
Combo comPort;
|
|
Combo baudRate;
|
|
|
|
String port;
|
|
int baud;
|
|
SerialPort serialPort;
|
|
|
|
Preferences preferences;
|
|
|
|
final SelectionAdapter comPortSelectionListener = new SelectionAdapter() {
|
|
|
|
@Override
|
|
public void widgetSelected(SelectionEvent e) {
|
|
port = comPort.getText();
|
|
updateSerialPortSettings();
|
|
preferences.setSerialPort(port);
|
|
term.setFocus();
|
|
}
|
|
};
|
|
|
|
final SelectionAdapter baudRateSelectionListener = new SelectionAdapter() {
|
|
|
|
@Override
|
|
public void widgetSelected(SelectionEvent e) {
|
|
baud = Integer.valueOf(baudRate.getText());
|
|
updateSerialPortSettings();
|
|
preferences.setSerialBaud(baud);
|
|
term.setFocus();
|
|
}
|
|
};
|
|
|
|
final SerialPortEventListener serialEventListener = new SerialPortEventListener() {
|
|
|
|
@Override
|
|
public void serialEvent(SerialPortEvent serialPortEvent) {
|
|
if (serialPortEvent.getEventType() == SerialPortEvent.RXCHAR) {
|
|
try {
|
|
final byte[] rx = serialPort.readBytes();
|
|
if (rx != null) {
|
|
display.syncExec(new Runnable() {
|
|
|
|
@Override
|
|
public void run() {
|
|
term.print(rx);
|
|
}
|
|
});
|
|
}
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
final KeyAdapter vt100TerminalKeyListener = new KeyAdapter() {
|
|
|
|
@Override
|
|
public void keyPressed(KeyEvent e) {
|
|
try {
|
|
if (!serialPort.isOpened()) {
|
|
return;
|
|
}
|
|
switch (e.keyCode) {
|
|
case SWT.ARROW_UP:
|
|
serialPort.writeByte((byte) 0x1B);
|
|
serialPort.writeByte((byte) 'A');
|
|
break;
|
|
case SWT.ARROW_DOWN:
|
|
serialPort.writeByte((byte) 0x1B);
|
|
serialPort.writeByte((byte) 'B');
|
|
break;
|
|
case SWT.ARROW_LEFT:
|
|
serialPort.writeByte((byte) 0x1B);
|
|
serialPort.writeByte((byte) 'D');
|
|
break;
|
|
case SWT.ARROW_RIGHT:
|
|
serialPort.writeByte((byte) 0x1B);
|
|
serialPort.writeByte((byte) 'C');
|
|
break;
|
|
case SWT.HOME:
|
|
serialPort.writeByte((byte) 0x1B);
|
|
serialPort.writeByte((byte) 'H');
|
|
break;
|
|
case SWT.END:
|
|
serialPort.writeByte((byte) 0x1B);
|
|
serialPort.writeByte((byte) 'K');
|
|
break;
|
|
case SWT.F1:
|
|
case SWT.F2:
|
|
case SWT.F3:
|
|
case SWT.F4:
|
|
case SWT.F5:
|
|
case SWT.F6:
|
|
case SWT.F7:
|
|
case SWT.F8:
|
|
case SWT.F9:
|
|
case SWT.F10:
|
|
serialPort.writeByte((byte) 0x1B);
|
|
serialPort.writeByte((byte) 'O');
|
|
serialPort.writeByte((byte) ('P' + (e.keyCode - SWT.F1)));
|
|
break;
|
|
default:
|
|
if (e.character != 0) {
|
|
serialPort.writeByte((byte) e.character);
|
|
}
|
|
break;
|
|
}
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
};
|
|
|
|
final KeyAdapter wordstarTerminalKeyListener = new KeyAdapter() {
|
|
|
|
@Override
|
|
public void keyPressed(KeyEvent e) {
|
|
try {
|
|
if (!serialPort.isOpened()) {
|
|
return;
|
|
}
|
|
switch (e.keyCode) {
|
|
case SWT.ARROW_UP:
|
|
serialPort.writeByte((byte) 5);
|
|
break;
|
|
case SWT.ARROW_DOWN:
|
|
serialPort.writeByte((byte) 24);
|
|
break;
|
|
case SWT.ARROW_LEFT:
|
|
if ((e.stateMask & SWT.MOD2) != 0) {
|
|
serialPort.writeByte((byte) 1);
|
|
}
|
|
else {
|
|
serialPort.writeByte((byte) 19);
|
|
}
|
|
break;
|
|
case SWT.ARROW_RIGHT:
|
|
if ((e.stateMask & SWT.MOD2) != 0) {
|
|
serialPort.writeByte((byte) 6);
|
|
}
|
|
else {
|
|
serialPort.writeByte((byte) 4);
|
|
}
|
|
break;
|
|
case SWT.INSERT:
|
|
if ((e.stateMask & SWT.MOD2) != 0) {
|
|
pasteFromClipboard();
|
|
}
|
|
else {
|
|
serialPort.writeByte((byte) 22);
|
|
}
|
|
break;
|
|
case SWT.HOME:
|
|
serialPort.writeByte((byte) 17);
|
|
serialPort.writeByte((byte) 'S');
|
|
break;
|
|
case SWT.END:
|
|
serialPort.writeByte((byte) 17);
|
|
serialPort.writeByte((byte) 'D');
|
|
break;
|
|
case SWT.PAGE_UP:
|
|
serialPort.writeByte((byte) 18);
|
|
break;
|
|
case SWT.PAGE_DOWN:
|
|
serialPort.writeByte((byte) 3);
|
|
break;
|
|
default:
|
|
if (e.character == SWT.DEL) {
|
|
serialPort.writeByte((byte) 7);
|
|
}
|
|
else if (e.character != 0) {
|
|
serialPort.writeByte((byte) e.character);
|
|
}
|
|
break;
|
|
}
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
};
|
|
|
|
KeyAdapter currentKeyListener;
|
|
|
|
public SerialTerminal() {
|
|
super((Shell) null);
|
|
}
|
|
|
|
@Override
|
|
protected void configureShell(Shell newShell) {
|
|
super.configureShell(newShell);
|
|
|
|
Image[] images = new Image[] {
|
|
ImageRegistry.getImageFromResources("app128.png"),
|
|
ImageRegistry.getImageFromResources("app64.png"),
|
|
ImageRegistry.getImageFromResources("app48.png"),
|
|
ImageRegistry.getImageFromResources("app32.png"),
|
|
ImageRegistry.getImageFromResources("app16.png"),
|
|
};
|
|
newShell.setImages(images);
|
|
|
|
newShell.setData(this);
|
|
}
|
|
|
|
@Override
|
|
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;
|
|
container.setLayout(layout);
|
|
|
|
term = new Terminal(container);
|
|
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);
|
|
updateSerialPortSettings();
|
|
|
|
currentKeyListener = vt100TerminalKeyListener;
|
|
term.addKeyListener(currentKeyListener);
|
|
|
|
container.addDisposeListener(new DisposeListener() {
|
|
|
|
@Override
|
|
public void widgetDisposed(DisposeEvent e) {
|
|
try {
|
|
serialPort.removeEventListener();
|
|
} catch (Exception ex) {
|
|
// Do nothing
|
|
}
|
|
try {
|
|
if (serialPort.isOpened()) {
|
|
serialPort.closePort();
|
|
}
|
|
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
});
|
|
|
|
return container;
|
|
}
|
|
|
|
void createBottomControls(Composite parent) {
|
|
int index;
|
|
|
|
Composite container = new Composite(parent, SWT.NONE);
|
|
GridLayout layout = new GridLayout(7, 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) {
|
|
term.removeKeyListener(currentKeyListener);
|
|
switch (cursorKeys.getSelectionIndex()) {
|
|
case 0:
|
|
currentKeyListener = vt100TerminalKeyListener;
|
|
break;
|
|
case 1:
|
|
currentKeyListener = wordstarTerminalKeyListener;
|
|
break;
|
|
}
|
|
term.addKeyListener(currentKeyListener);
|
|
term.setFocus();
|
|
}
|
|
});
|
|
|
|
label = new Label(container, SWT.NONE);
|
|
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
|
|
|
|
label = new Label(container, SWT.NONE);
|
|
label.setText("Port");
|
|
|
|
comPort = new Combo(container, SWT.DROP_DOWN | SWT.READ_ONLY);
|
|
comPort.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
|
|
comPort.setItems(SerialPortList.getPortNames());
|
|
index = port != null ? comPort.indexOf(port) : -1;
|
|
if (index == -1) {
|
|
index = 0;
|
|
port = comPort.getItem(index);
|
|
}
|
|
comPort.select(index);
|
|
comPort.addSelectionListener(comPortSelectionListener);
|
|
|
|
label = new Label(container, SWT.NONE);
|
|
label.setText("Baud");
|
|
|
|
baudRate = new Combo(container, SWT.DROP_DOWN | SWT.READ_ONLY);
|
|
baudRate.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
|
|
baudRate.setItems(BAUD_RATES);
|
|
index = baudRate.indexOf(String.valueOf(baud));
|
|
if (index == -1) {
|
|
index = 0;
|
|
baud = Integer.valueOf(baudRate.getItem(index));
|
|
}
|
|
baudRate.select(index);
|
|
baudRate.addSelectionListener(baudRateSelectionListener);
|
|
}
|
|
|
|
void updateSerialPortSettings() {
|
|
if (serialPort == null || !serialPort.getPortName().equals(port)) {
|
|
try {
|
|
serialPort.removeEventListener();
|
|
if (serialPort.isOpened()) {
|
|
serialPort.closePort();
|
|
}
|
|
} catch (Exception e) {
|
|
|
|
}
|
|
try {
|
|
serialPort = new SerialPort(port);
|
|
serialPort.openPort();
|
|
serialPort.addEventListener(serialEventListener);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
try {
|
|
serialPort.setParams(
|
|
baud,
|
|
SerialPort.DATABITS_8,
|
|
SerialPort.STOPBITS_1,
|
|
SerialPort.PARITY_NONE,
|
|
false,
|
|
false);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
getShell().setText("Serial Terminal on " + port);
|
|
}
|
|
|
|
public void setFocus() {
|
|
term.setFocus();
|
|
}
|
|
|
|
public SerialPort getSerialPort() {
|
|
return serialPort;
|
|
}
|
|
|
|
void pasteFromClipboard() {
|
|
Clipboard clipboard = new Clipboard(display);
|
|
try {
|
|
final String s = (String) clipboard.getContents(TextTransfer.getInstance());
|
|
if (s != null) {
|
|
new Thread(new Runnable() {
|
|
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
serialPort.writeString(s);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
}).start();
|
|
}
|
|
} finally {
|
|
clipboard.dispose();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
}
|