/* * Copyright (c) 2016 Seth J. Morabito * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.loomcom.symon.devices; import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryRangeException; /** * This is a simulation of the MOS 6551 ACIA, with limited * functionality. Interrupts are not supported. *

* Unlike a 16550 UART, the 6551 ACIA has only one-byte transmit and * receive buffers. It is the programmer's responsibility to check the * status (full or empty) for transmit and receive buffers before * writing / reading. */ public class Acia6551 extends Acia { public static final int ACIA_SIZE = 4; static final int DATA_REG = 0; static final int STAT_REG = 1; static final int CMND_REG = 2; static final int CTRL_REG = 3; /** * Registers. These are ignored in the current implementation. */ private int commandRegister; private int controlRegister; public Acia6551(int address) throws MemoryRangeException { super(address, ACIA_SIZE, "ACIA"); } @Override public int read(int address) throws MemoryAccessException { switch (address) { case DATA_REG: return rxRead(); case STAT_REG: return statusReg(); case CMND_REG: return commandRegister; case CTRL_REG: return controlRegister; default: throw new MemoryAccessException("No register."); } } @Override public void write(int address, int data) throws MemoryAccessException { switch (address) { case 0: txWrite(data); break; case 1: reset(); break; case 2: setCommandRegister(data); break; case 3: setControlRegister(data); break; default: throw new MemoryAccessException("No register."); } } private void setCommandRegister(int data) { commandRegister = data; // Bit 1 controls receiver IRQ behavior receiveIrqEnabled = (commandRegister & 0x02) == 0; // Bits 2 & 3 controls transmit IRQ behavior transmitIrqEnabled = (commandRegister & 0x08) == 0 && (commandRegister & 0x04) != 0; } /** * Set the control register and associated state. * * @param data Data to write into the control register */ private void setControlRegister(int data) { controlRegister = data; int rate = 0; // If the value of the data is 0, this is a request to reset, // otherwise it's a control update. if (data == 0) { reset(); } else { // Mask the lower three bits to get the baud rate. int baudSelector = data & 0x0f; switch (baudSelector) { case 0: rate = 0; break; case 1: rate = 50; break; case 2: rate = 75; break; case 3: rate = 110; // Real rate is actually 109.92 break; case 4: rate = 135; // Real rate is actually 134.58 break; case 5: rate = 150; break; case 6: rate = 300; break; case 7: rate = 600; break; case 8: rate = 1200; break; case 9: rate = 1800; break; case 10: rate = 2400; break; case 11: rate = 3600; break; case 12: rate = 4800; break; case 13: rate = 7200; break; case 14: rate = 9600; break; case 15: rate = 19200; break; } setBaudRate(rate); } } /** * @return The contents of the status register. */ @Override public int statusReg() { // TODO: Parity Error, Framing Error, DTR, DSR, and Interrupt flags. int stat = 0; if (rxFull && System.nanoTime() >= (lastRxRead + baudRateDelay)) { stat |= 0x08; } if (txEmpty && System.nanoTime() >= (lastTxWrite + baudRateDelay)) { stat |= 0x10; } if (overrun) { stat |= 0x04; } return stat; } private synchronized void reset() { txChar = 0; txEmpty = true; rxChar = 0; rxFull = false; receiveIrqEnabled = false; transmitIrqEnabled = false; } }