/*
JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine
Release Version 2.4
A project from the Physics Dept, The University of Oxford
Copyright (C) 2007-2010 The University of Oxford
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as published by
the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Details (including contact information) can be found at:
jpc.sourceforge.net
or the developer website
sourceforge.net/projects/jpc/
Conceived and Developed by:
Rhys Newman, Ian Preston, Chris Dennis
End of licence header
*/
package org.jpc.emulator.peripheral;
import org.jpc.emulator.*;
import org.jpc.emulator.motherboard.*;
import java.io.*;
import java.nio.charset.Charset;
import java.util.logging.*;
/**
* Emulates a standard 16450 UART.
*
* @author Chris Dennis
*/
public class SerialPort extends AbstractHardwareComponent implements IODevice
{
private static final Logger LOGGING = Logger.getLogger(SerialPort.class.getName());
private static final Charset US_ASCII = Charset.forName("US-ASCII");
private static final byte UART_LCR_DLAB = (byte)0x80; /* Divisor latch access bit */
private static final byte UART_IER_MSI = 0x08; /* Enable Modem status interrupt */
private static final byte UART_IER_RLSI = 0x04; /* Enable receiver line status interrupt */
private static final byte UART_IER_THRI = 0x02; /* Enable Transmitter holding register int. */
private static final byte UART_IER_RDI = 0x01; /* Enable receiver data interrupt */
private static final byte UART_IIR_NO_INT = 0x01; /* No interrupts pending */
private static final byte UART_IIR_ID = 0x06; /* Mask for the interrupt ID */
private static final byte UART_IIR_MSI = 0x00; /* Modem status interrupt */
private static final byte UART_IIR_THRI = 0x02; /* Transmitter holding register empty */
private static final byte UART_IIR_RDI = 0x04; /* Receiver data interrupt */
private static final byte UART_IIR_RLSI = 0x06; /* Receiver line status interrupt */
/*
* These are the definitions for the Modem Control Register
*/
private static final byte UART_MCR_LOOP = 0x10; /* Enable loopback test mode */
private static final byte UART_MCR_OUT2 = 0x08; /* Out2 complement */
private static final byte UART_MCR_OUT1 = 0x04; /* Out1 complement */
private static final byte UART_MCR_RTS = 0x02; /* RTS complement */
private static final byte UART_MCR_DTR = 0x01; /* DTR complement */
/*
* These are the definitions for the Modem Status Register
*/
private static final byte UART_MSR_DCD = (byte)0x80; /* Data Carrier Detect */
private static final byte UART_MSR_RI = 0x40; /* Ring Indicator */
private static final byte UART_MSR_DSR = 0x20; /* Data Set Ready */
private static final byte UART_MSR_CTS = 0x10; /* Clear to Send */
private static final byte UART_MSR_DDCD = 0x08; /* Delta DCD */
private static final byte UART_MSR_TERI = 0x04; /* Trailing edge ring indicator */
private static final byte UART_MSR_DDSR = 0x02; /* Delta DSR */
private static final byte UART_MSR_DCTS = 0x01; /* Delta CTS */
private static final byte UART_MSR_ANY_DELTA = 0x0F; /* Any of the delta bits! */
private static final byte UART_LSR_TEMT = 0x40; /* Transmitter empty */
private static final byte UART_LSR_THRE = 0x20; /* Transmit-hold-register empty */
private static final byte UART_LSR_BI = 0x10; /* Break interrupt indicator */
private static final byte UART_LSR_FE = 0x08; /* Frame error indicator */
private static final byte UART_LSR_PE = 0x04; /* Parity error indicator */
private static final byte UART_LSR_OE = 0x02; /* Overrun error indicator */
private static final byte UART_LSR_DR = 0x01; /* Receiver data ready */
private static final int[] ioPorts = new int[]{0x3f8, 0x2f8, 0x3e8, 0x2e8};
private static final int[] irqLines = new int[]{4, 3, 4, 3};
private short divider;
private byte receiverBufferRegister; /* receiver buffer register */
private byte interruptEnableRegister; /* interrupt enable register */
private byte interruptIORegister; /* interrupt I/O register */ /* read only */
private byte lineControlRegister; /* line control register */
private byte lineStatusRegister; /* line status register */ /* read only */
private byte modemControlRegister; /* modem control register */
private byte modemStatusRegister; /* modem status register */
private byte scratchRegister; /* scratch register */
private boolean thrIPending; /* transmitter holding register interrupt */
private int irq; /* irq channel */
private int baseAddress; /* base I/O Port Pointer */
private InterruptController irqDevice;
private final StringBuilder serialOutputBuffer = new StringBuilder();
private final Logger serialOutput;
public SerialPort(int portNumber)
{
ioportRegistered = false;
if (portNumber > 3 || portNumber < 0) {
LOGGING.log(Level.WARNING, "port number {0} is not valid, using 0", Integer.valueOf(portNumber));
portNumber = 0;
}
this.irq = SerialPort.irqLines[portNumber];
this.baseAddress = SerialPort.ioPorts[portNumber];
this.lineStatusRegister = UART_LSR_TEMT | UART_LSR_THRE;
this.interruptIORegister = UART_IIR_NO_INT;
serialOutput = Logger.getLogger(SerialPort.class.getName() + ".port" + portNumber);
}
public void saveState(DataOutput output) throws IOException
{
output.writeShort(divider);
output.writeByte(receiverBufferRegister);
output.writeByte(interruptEnableRegister);
output.writeByte(interruptIORegister);
output.writeByte(lineControlRegister);
output.writeByte(modemControlRegister);
output.writeByte(lineStatusRegister);
output.writeByte(modemStatusRegister);
output.writeByte(scratchRegister);
output.writeBoolean(thrIPending);
output.writeInt(irq);
output.writeInt(baseAddress);
}
public void loadState(DataInput input) throws IOException
{
ioportRegistered = false;
divider = input.readShort();
receiverBufferRegister = input.readByte();
interruptEnableRegister = input.readByte();
interruptIORegister = input.readByte();
lineControlRegister = input.readByte();
modemControlRegister = input.readByte();
lineStatusRegister = input.readByte();
modemStatusRegister = input.readByte();
scratchRegister = input.readByte();
thrIPending = input.readBoolean();
irq = input.readInt();
baseAddress = input.readInt();
}
public int canReceive()
{
if (0 == (lineStatusRegister & UART_LSR_DR))
return 1;
else
return 0;
}
public void recieve(byte data)
{
receiverBufferRegister = data;
if (0 == data)
lineStatusRegister = (byte)(lineStatusRegister | UART_LSR_DR);
else
lineStatusRegister = (byte)(lineStatusRegister | UART_LSR_BI | UART_LSR_DR);
this.updateIRQ();
}
private void updateIRQ()
{
if((0 != (lineStatusRegister & UART_LSR_DR)) && (0 != (interruptEnableRegister & UART_IER_RDI))) {
interruptIORegister = UART_IIR_RDI;
} else if (thrIPending && (0 != (interruptEnableRegister & UART_IER_THRI))) {
interruptIORegister = UART_IIR_THRI;
} else {
interruptIORegister = UART_IIR_NO_INT;
}
if(interruptIORegister != UART_IIR_NO_INT) {
irqDevice.setIRQ(irq, 1);
} else {
irqDevice.setIRQ(irq, 0);
}
}
public void ioPortWrite8(int address, int data)
{
this.ioportWrite(address, data);
}
public void ioPortWrite16(int address, int data){}
public void ioPortWrite32(int address, int data){}
public int ioPortRead8(int address)
{
return this.ioportRead(address);
}
public int ioPortRead16(int address)
{
return 0xffff;
}
public int ioPortRead32(int address)
{
return 0xffffffff;
}
public int[] ioPortsRequested()
{
return new int[]{baseAddress, baseAddress + 1, baseAddress + 2, baseAddress + 3, baseAddress + 4, baseAddress + 5, baseAddress + 6, baseAddress + 7};
}
private void ioportWrite(int address, int data)
{
address &= 7;
switch(address) {
default:
case 0:
if (0 != (lineControlRegister & UART_LCR_DLAB)) {
divider = (short)((divider & 0xff00) | data);
} else {
thrIPending = false;
lineStatusRegister = (byte)(lineStatusRegister & ~UART_LSR_THRE);
this.updateIRQ();
print(new String(new byte[]{(byte) data}, US_ASCII));
//output data
thrIPending = true;
lineStatusRegister = (byte)(lineStatusRegister | UART_LSR_THRE | UART_LSR_TEMT);
this.updateIRQ();
}
break;
case 1:
if (0 != (lineControlRegister & UART_LCR_DLAB)) {
divider = (short)((divider & 0x00ff) | (data << 8));
} else {
interruptEnableRegister = (byte)(data & 0x0f);
if (0 != (lineStatusRegister & UART_LSR_THRE)) {
thrIPending = true;
}
this.updateIRQ();
}
break;
case 2:
break;
case 3:
lineControlRegister = (byte)data;
break;
case 4:
modemControlRegister = (byte)(data & 0x1f);
break;
case 5:
break;
case 6:
modemStatusRegister = (byte)data;
break;
case 7:
scratchRegister = (byte)data;
break;
}
}
private int ioportRead(int address)
{
address &= 7;
int ret;
switch(address) {
default:
case 0:
if(0 != (lineControlRegister & UART_LCR_DLAB)) {
return divider & 0xff;
} else {
lineStatusRegister = (byte)(lineStatusRegister & ~(UART_LSR_DR | UART_LSR_BI));
ret = receiverBufferRegister;
this.updateIRQ();
return ret;
}
case 1:
if (0 != (lineControlRegister & UART_LCR_DLAB)) {
return (divider >>> 8) & 0xff;
} else {
return interruptEnableRegister;
}
case 2:
ret = interruptIORegister;
if ((ret & 0x7) == UART_IIR_THRI)
thrIPending = false;
this.updateIRQ();
return ret;
case 3:
return lineControlRegister;
case 4:
return modemControlRegister;
case 5:
return lineStatusRegister;
case 6:
if (0 != (modemControlRegister & UART_MCR_LOOP)) {
/* in loopback, the modem output pins are connected to the inputs */
ret = (modemControlRegister & 0x0c) << 4;
ret |= (modemControlRegister & 0x02) << 3;
ret |= (modemControlRegister & 0x01) << 5;
return ret;
} else {
return modemStatusRegister;
}
case 7:
return scratchRegister;
}
}
private boolean ioportRegistered;
public void reset()
{
irqDevice = null;
ioportRegistered = false;
this.lineStatusRegister = UART_LSR_TEMT | UART_LSR_THRE;
this.interruptIORegister = UART_IIR_NO_INT;
}
public boolean initialised()
{
return ioportRegistered && (irqDevice != null);
}
public boolean updated()
{
return ioportRegistered && irqDevice.updated();
}
public void updateComponent(HardwareComponent component)
{
if ((component instanceof IOPortHandler) && component.updated())
{
((IOPortHandler)component).registerIOPortCapable(this);
ioportRegistered = true;
}
}
public void acceptComponent(HardwareComponent component)
{
if ((component instanceof InterruptController) &&
component.initialised()) {
irqDevice = (InterruptController)component;
}
if ((component instanceof IOPortHandler)
&& component.initialised()) {
((IOPortHandler)component).registerIOPortCapable(this);
ioportRegistered = true;
}
}
private void print(String data)
{
synchronized (serialOutputBuffer) {
int newline;
while ((newline = data.indexOf('\n') + 1) > 0) {
serialOutputBuffer.append(data.substring(0, newline));
serialOutput.log(Level.INFO, serialOutputBuffer.toString());
serialOutputBuffer.delete(0, serialOutputBuffer.length());
data = data.substring(newline);
}
serialOutputBuffer.append(data);
}
}
}