CPU bug fixes and Simulator enhancements.

Bug Fixes:

- Fixed several bugs in the CPU that caused processor status flags to
  be set incorrectly.  Instructions affected were: STA, STX, STY, CMP,
  CPX, CPY, BIT.

- Made some internal-use-only methods on the CPU class private.

- Fixed incorrect disassembly of (Indirect,X) and (Indirect),Y
  instructions. Although this didn't affect behavior, it certainly
  caused me some confusion in debugging.

- Added missing "BCS" instruction to instruction table.

Enhancements:

- Now includes a full version of Lee Davison's Enhanced 6502 BASIC
  bundled as source code and a ROM image. Get that REAL COMPUTER
  EXPERIENCE!(tm)

- If a file named "rom.bin" exists in the same directory where the
  simulator is executed, it will be loaded at addresses $d000-$ffff.

- Gave the CPU an idle loop to make simulated timing a little more
  realistic (but this is still an area needing major improvement)

- Changed the CPU's toString() method to give better debugging output.

- Added a small typeahead buffer to the Console.

- Better exception messaging.

Misc:

- Bumped version to 0.5, updated README.
This commit is contained in:
Seth Morabito committed 2012-10-21 20:05:05 -07:00
1 parent c1caf8c6b4
commit 795ccfde5d
25 files changed
+13622 -4508

No files matched your search

+6 -5
View File
@@ -42,9 +42,10 @@ public class Bus {
MemoryRange memRange = device.getMemoryRange();
for (Device d : devices) {
if (d.getMemoryRange().overlaps(memRange)) {
throw new MemoryRangeException("The device being added " +
"overlaps with an existing " +
"device.");
throw new MemoryRangeException("The device being added at " +
String.format("$%04X", memRange.startAddress()) +
" overlaps with an existing " +
"device, '" + d + "'");
}
}
@@ -114,7 +115,7 @@ public class Bus {
return d.read(devAddr);
}
}
throw new MemoryAccessException("Read failed! No device at address.");
throw new MemoryAccessException("Bus read failed. No device at address " + String.format("$%04X", address));
}
public void write(int address, int value) throws MemoryAccessException {
@@ -127,7 +128,7 @@ public class Bus {
return;
}
}
throw new MemoryAccessException("Write failed! No device at address.");
throw new MemoryAccessException("Bus write failed. No device at address " + String.format("$%04X", address));
}
public SortedSet<Device> getDevices() {
+63 -38
View File
@@ -29,6 +29,10 @@ public class Cpu implements InstructionTable {
public static final int NMI_VECTOR_L = 0xfffe;
public static final int NMI_VECTOR_H = 0xffff;
// The delay in microseconds between steps.
// TODO: Make configurable
private static final int CLOCK_IN_NS = 1000;
/* The Bus */
private Bus bus;
@@ -41,11 +45,13 @@ public class Cpu implements InstructionTable {
private int pc; // Program Counter register
private int sp; // Stack Pointer register, offset into page 1
private int ir; // Instruction register
private int lastPc; // The program counter before the most recent step.
private int[] args = new int[3]; // Decoded instruction args
private int instSize; // # of operands for the instruction
/* Scratch space for addressing mode and effective address
* calculations */
* calculations */
private int irAddressMode; // Bits 3-5 of IR: [ | | |X|X|X| | ]
private int irOpMode; // Bits 6-7 of IR: [ | | | | | |X|X]
private int effectiveAddress;
@@ -435,8 +441,8 @@ public class Cpu implements InstructionTable {
/** BIT - Bit Test ******************************************************/
case 0x24: // Zero Page
case 0x2c: // Absolute
tmp = a & bus.read(effectiveAddress);
setZeroFlag(tmp == 0);
tmp = bus.read(effectiveAddress);
setZeroFlag((a & tmp) == 0);
setNegativeFlag((tmp & 0x80) != 0);
setOverflowFlag((tmp & 0x40) != 0);
break;
@@ -553,7 +559,6 @@ public class Cpu implements InstructionTable {
case 0x99: // Absolute,Y
case 0x9d: // Absolute,X
bus.write(effectiveAddress, a);
setArithmeticFlags(a);
break;
@@ -562,7 +567,6 @@ public class Cpu implements InstructionTable {
case 0x8c: // Absolute
case 0x94: // Zero Page,X
bus.write(effectiveAddress, y);
setArithmeticFlags(y);
break;
@@ -571,7 +575,6 @@ public class Cpu implements InstructionTable {
case 0x8e: // Absolute
case 0x96: // Zero Page,Y
bus.write(effectiveAddress, x);
setArithmeticFlags(x);
break;
@@ -704,6 +707,8 @@ public class Cpu implements InstructionTable {
setOpTrap();
break;
}
delayLoop(ir);
}
/**
@@ -715,7 +720,7 @@ public class Cpu implements InstructionTable {
* @param operand The operand
* @return
*/
public int adc(int acc, int operand) {
private int adc(int acc, int operand) {
int result = (operand & 0xff) + (acc & 0xff) + getCarryBit();
int carry6 = (operand & 0x7f) + (acc & 0x7f) + getCarryBit();
setCarryFlag((result & 0x100) != 0);
@@ -729,7 +734,7 @@ public class Cpu implements InstructionTable {
* Add with Carry (BCD).
*/
public int adcDecimal(int acc, int operand) {
private int adcDecimal(int acc, int operand) {
int l, h, result;
l = (acc & 0x0f) + (operand & 0x0f) + getCarryBit();
if ((l & 0xff) > 9) l += 6;
@@ -748,12 +753,8 @@ public class Cpu implements InstructionTable {
* Common code for Subtract with Carry. Just calls ADC of the
* one's complement of the operand. This lets the N, V, C, and Z
* flags work out nicely without any additional logic.
*
* @param acc
* @param operand
* @return
*/
public int sbc(int acc, int operand) {
private int sbc(int acc, int operand) {
int result;
result = adc(acc, ~operand);
setArithmeticFlags(result);
@@ -762,12 +763,8 @@ public class Cpu implements InstructionTable {
/**
* Subtract with Carry, BCD mode.
*
* @param acc
* @param operand
* @return
*/
public int sbcDecimal(int acc, int operand) {
private int sbcDecimal(int acc, int operand) {
int l, h, result;
l = (acc & 0x0f) - (operand & 0x0f) - (carryFlag ? 0 : 1);
if ((l & 0x10) != 0) l -= 6;
@@ -784,23 +781,19 @@ public class Cpu implements InstructionTable {
/**
* Compare two values, and set carry, zero, and negative flags
* appropriately.
*
* @param reg
* @param operand
*/
public void cmp(int reg, int operand) {
private void cmp(int reg, int operand) {
int tmp = (reg - operand) & 0xff;
setCarryFlag(reg >= operand);
setZeroFlag(reg == operand);
setNegativeFlag((reg - operand) > 0);
setZeroFlag(tmp == 0);
setNegativeFlag((tmp & 0x80) != 0); // Negative bit set
}
/**
* Set the Negative and Zero flags based on the current value of the
* register operand.
*
* @param reg The register.
*/
public void setArithmeticFlags(int reg) {
private void setArithmeticFlags(int reg) {
zeroFlag = (reg == 0);
negativeFlag = (reg & 0x80) != 0;
}
@@ -1134,6 +1127,10 @@ public class Cpu implements InstructionTable {
this.y = val;
}
public int getLastProgramCounter() {
return lastPc;
}
public int getProgramCounter() {
return pc;
}
@@ -1285,15 +1282,15 @@ public class Cpu implements InstructionTable {
*/
public String toString() {
String opcode = opcode(ir, args[0], args[1]);
StringBuffer sb = new StringBuffer(String.format("$%04X", addr) +
" ");
StringBuffer sb = new StringBuffer(getInstructionByteStatus());
sb.append(" ");
sb.append(String.format("%-14s", opcode));
sb.append("A=" + getAccumulatorStatus() + " ");
sb.append("X=" + getXRegisterStatus() + " ");
sb.append("Y=" + getYRegisterStatus() + " ");
sb.append("PC=" + getProgramCounterStatus() + " ");
sb.append("SP=" + getStackPointerStatus() + " ");
sb.append("P=" + getProcessorStatusString());
sb.append("A:" + String.format("%02x", a) + " ");
sb.append("X:" + String.format("%02x", x) + " ");
sb.append("Y:" + String.format("%02x", y) + " ");
sb.append("F:" + String.format("%02x", getProcessorStatus()) + " ");
sb.append("S:" + String.format("1%02x", sp) + " ");
sb.append(getProcessorStatusString());
return sb.toString();
}
@@ -1397,6 +1394,19 @@ public class Cpu implements InstructionTable {
bus.write(RST_VECTOR_L, address & 0x00ff);
}
/*
* Perform a busy-loop for CLOCK_IN_NS nanoseconds
*/
private void delayLoop(int opcode) {
int clockSteps = Cpu.instructionClocks[0xff & opcode];
// Just a precaution. This could be better.
if (clockSteps == 0) { clockSteps = 1; }
long startTime = System.nanoTime();
long stopTime = startTime + (CLOCK_IN_NS * clockSteps);
// Busy loop
while (System.nanoTime() < stopTime) { ; }
}
/**
* Given an opcode and its operands, return a formatted name.
*
@@ -1430,10 +1440,10 @@ public class Cpu implements InstructionTable {
sb.append(String.format(" ($%04X)", address(op1, op2)));
break;
case XIN:
sb.append(String.format(" ($%02X),X", op1));
sb.append(String.format(" ($%02X,X)", op1));
break;
case INY:
sb.append(String.format(" ($%02X,Y)", op1));
sb.append(String.format(" ($%02X),Y", op1));
break;
case REL:
case ZPG:
@@ -1449,4 +1459,19 @@ public class Cpu implements InstructionTable {
return sb.toString();
}
}
public String getInstructionByteStatus() {
switch (Cpu.instructionSizes[ir]) {
case 0:
case 1:
return String.format("%04X %02X ", addr, ir);
case 2:
return String.format("%04X %02X %02X ", addr, ir, args[0]);
case 3:
return String.format("%04X %02X %02X %02X", addr, ir, args[0], args[1]);
default:
return null;
}
}
}
@@ -119,7 +119,7 @@ public interface InstructionTable {
"TYA", "STA", "TXS", null, null, "STA", null, null,
"LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null,
"TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null,
null, "LDA", null, null, "LDY", "LDA", "LDX", null,
"BCS", "LDA", null, null, "LDY", "LDA", "LDX", null,
"CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null,
"CPY", "CMP", null, null, "CPY", "CMP", "DEC", null,
"INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null,
@@ -173,13 +173,13 @@ public interface InstructionTable {
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x8c-0x8f
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x90-0x93
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97
Mode.IMP, Mode.ABX, Mode.IMP, Mode.NUL, // 0x98-0x9b
Mode.NUL, Mode.ABY, Mode.NUL, Mode.NUL, // 0x9c-0x9f
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x98-0x9b
Mode.NUL, Mode.ABX, Mode.NUL, Mode.NUL, // 0x9c-0x9f
Mode.IMM, Mode.XIN, Mode.IMM, Mode.NUL, // 0xa0-0xa3
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xa4-0xa7
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xa8-0xab
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xac-0xaf
Mode.NUL, Mode.INY, Mode.NUL, Mode.NUL, // 0xb0-0xb3
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xb0-0xb3
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7
Mode.IMP, Mode.ABX, Mode.IMP, Mode.NUL, // 0xb8-0xbb
Mode.ABX, Mode.ABY, Mode.ABY, Mode.NUL, // 0xbc-0xbf
@@ -239,11 +239,11 @@ public interface InstructionTable {
2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0,
2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0,
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0,
0, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0,
2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0,
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0
};
}
}
+36 -33
View File
@@ -2,6 +2,7 @@ package com.loomcom.symon;
import com.loomcom.symon.devices.Acia;
import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.exceptions.FifoUnderrunException;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException;
import com.loomcom.symon.exceptions.SymonException;
@@ -14,7 +15,6 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.Level;
@@ -27,16 +27,16 @@ public class Simulator implements ActionListener, Observer {
private static final int BUS_BOTTOM = 0x0000;
private static final int BUS_TOP = 0xffff;
// 32K of RAM
private static final int MEMORY_BASE = 0x0000;
private static final int MEMORY_SIZE = 0xc000; // 48 KB
private static final int MEMORY_SIZE = 0xC000;
private static final int ROM_BASE = 0xe000;
private static final int ROM_SIZE = 0x2000; // 8 KB
// IO area at $D000
private static final int ACIA_BASE = 0xC000;
private static final int ACIA_BASE = 0xc000;
// The delay in microseconds between steps.
private static final int DELAY_BETWEEN_STEPS_NS = 1000;
// 8KB ROM at E000-FFFF
private static final int ROM_BASE = 0xD000;
private static final int ROM_SIZE = 0x3000;
// Since it is very expensive to update the UI with Swing's Event Dispatch Thread, we can't afford
// to refresh the view on every simulated clock cycle. Instead, we will only refresh the view after this
@@ -84,18 +84,26 @@ public class Simulator implements ActionListener, Observer {
private JFileChooser fileChooser;
private PreferencesDialog preferences;
public Simulator() throws MemoryRangeException {
public Simulator() throws MemoryRangeException, IOException {
this.acia = new Acia(ACIA_BASE);
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
this.cpu = new Cpu();
this.ram = new Memory(MEMORY_BASE, MEMORY_SIZE, false);
// TODO: Load this ROM from a file, of course!
this.rom = new Memory(ROM_BASE, ROM_SIZE, false);
// TODO: Make this configurable, of course.
File romImage = new File("rom.bin");
if (romImage.canRead()) {
logger.info("Loading ROM image from file " + romImage);
this.rom = Memory.makeROM(ROM_BASE, ROM_SIZE, romImage);
} else {
logger.info("No ROM file 'rom.bin' found. ROM image will be empty R/W memory.");
// Just make it a normal RAM image.
this.rom = Memory.makeRAM(ROM_BASE, ROM_SIZE);
}
bus.addCpu(cpu);
bus.addDevice(acia);
bus.addDevice(ram);
bus.addDevice(acia);
bus.addDevice(rom);
}
@@ -212,7 +220,7 @@ public class Simulator implements ActionListener, Observer {
*/
public void actionPerformed(ActionEvent actionEvent) {
if (actionEvent.getSource() == resetButton) {
handleReset();
coldReset();
} else if (actionEvent.getSource() == stepButton) {
handleStep();
} else if (actionEvent.getSource() == runStopButton) {
@@ -274,7 +282,7 @@ public class Simulator implements ActionListener, Observer {
}
}
private void handleReset() {
private void coldReset() {
if (runLoop != null && runLoop.isRunning()) {
runLoop.requestStop();
runLoop.interrupt();
@@ -282,7 +290,7 @@ public class Simulator implements ActionListener, Observer {
}
try {
logger.log(Level.INFO, "Reset requested. Resetting CPU and clearing memory.");
logger.log(Level.INFO, "Cold reset requested. Resetting CPU and clearing memory.");
// Reset and clear memory
cpu.reset();
ram.fill(0x00);
@@ -307,6 +315,7 @@ public class Simulator implements ActionListener, Observer {
private void handleStep() {
try {
step();
// The simulator is lazy about updating the UI for performance reasons, so always request an
// immediate update after stepping manually.
SwingUtilities.invokeLater(new Runnable() {
@@ -337,8 +346,6 @@ public class Simulator implements ActionListener, Observer {
*/
private void step() throws MemoryAccessException {
delayLoop();
cpu.step();
// Read from the ACIA and immediately update the console if there's
@@ -351,8 +358,12 @@ public class Simulator implements ActionListener, Observer {
// If a key has been pressed, fill the ACIA.
// TODO: Interrupt handling.
if (console.hasInput()) {
acia.rxWrite((int)console.readInputChar());
try {
if (console.hasInput()) {
acia.rxWrite((int)console.readInputChar());
}
} catch (FifoUnderrunException ex) {
logger.severe("Console type-ahead buffer underrun!");
}
// This is a very expensive update, and we're doing it without
@@ -374,8 +385,6 @@ public class Simulator implements ActionListener, Observer {
* Load a program into memory at the simulatorDidStart address.
*/
private void loadProgram(byte[] program, int startAddress) throws MemoryAccessException {
cpu.setResetVector(startAddress);
int addr = startAddress, i;
for (i = 0; i < program.length; i++) {
bus.write(addr++, program[i] & 0xff);
@@ -388,6 +397,9 @@ public class Simulator implements ActionListener, Observer {
// Reset (but don't clear memory, naturally)
cpu.reset();
// Reset the stack program counter
cpu.setProgramCounter(preferences.getProgramStartAddress());
// Immediately update the UI.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
@@ -405,8 +417,8 @@ public class Simulator implements ActionListener, Observer {
Simulator simulator = new Simulator();
simulator.createAndShowUi();
// Reset the simulator.
simulator.handleReset();
} catch (MemoryRangeException e) {
simulator.coldReset();
} catch (Exception e) {
e.printStackTrace();
}
}
@@ -433,15 +445,6 @@ public class Simulator implements ActionListener, Observer {
}
}
/*
* Perform a busy-loop for DELAY_BETWEEN_STEPS_NS nanoseconds
*/
private void delayLoop() {
long startTime = System.nanoTime();
long stopTime = startTime + DELAY_BETWEEN_STEPS_NS;
// Busy loop
while (System.nanoTime() < stopTime) { ; }
}
/**
* The main run thread.
@@ -496,4 +499,4 @@ public class Simulator implements ActionListener, Observer {
isRunning = false;
}
}
}
}
@@ -1,9 +1,12 @@
package com.loomcom.symon.devices;
import java.io.*;
import java.util.*;
import com.loomcom.symon.exceptions.*;
import javax.swing.*;
public class Memory extends Device {
private boolean readOnly;
@@ -24,6 +27,17 @@ public class Memory extends Device {
this(address, size, false);
}
public static Memory makeROM(int address, int size, File f) throws MemoryRangeException, IOException {
Memory memory = new Memory(address, size, true);
memory.loadFromFile(f);
return memory;
}
public static Memory makeRAM(int address, int size) throws MemoryRangeException {
Memory memory = new Memory(address, size, false);
return memory;
}
public void write(int address, int data) throws MemoryAccessException {
if (readOnly) {
throw new MemoryAccessException("Cannot write to read-only memory at address " + address);
@@ -32,6 +46,34 @@ public class Memory extends Device {
}
}
/**
* Load the memory from a file.
*
* @param file The file to read an array of bytes from.
* @throws MemoryRangeException if the file and memory size do not match.
* @throws IOException if the file read fails.
*/
public void loadFromFile(File file) throws MemoryRangeException, IOException {
if (file.canRead()) {
long fileSize = file.length();
if (fileSize > mem.length) {
throw new MemoryRangeException("File will not fit in available memory.");
} else {
int i = 0;
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
while (dis.available() != 0) {
mem[i++] = dis.readUnsignedByte();
}
}
} else {
throw new IOException("Cannot open file " + file);
}
}
public int read(int address) throws MemoryAccessException {
return this.mem[address];
}
@@ -40,7 +82,6 @@ public class Memory extends Device {
Arrays.fill(this.mem, val);
}
public String toString() {
return "Memory: " + getMemoryRange().toString();
}
+34 -11
View File
@@ -7,6 +7,8 @@ import java.awt.event.MouseListener;
import com.grahamedgecombe.jterminal.JTerminal;
import com.grahamedgecombe.jterminal.vt100.Vt100TerminalModel;
import com.loomcom.symon.exceptions.FifoUnderrunException;
import com.loomcom.symon.util.FifoRingBuffer;
import javax.swing.*;
import javax.swing.border.BevelBorder;
@@ -22,16 +24,22 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
private static final int DEFAULT_COLUMNS = 80;
private static final int DEFAULT_ROWS = 24;
private static final int DEFAULT_BORDER_WIDTH = 10;
// If true, swap CR and LF characters
private static final boolean SWAP_CR_AND_LF = true;
// If true, send CRLF (0x0d 0x0a) whenever CR is typed
private static final boolean SEND_CR_LF_FOR_CR = false;
private boolean hasInput = false;
private char keyBuffer;
private FifoRingBuffer typeAheadBuffer;
public Console() {
this(DEFAULT_COLUMNS, DEFAULT_ROWS);
}
public Console(int columns, int rows) {
public Console(int columns, int rows) {
super(new Vt100TerminalModel(columns, rows));
// A small type-ahead buffer, as might be found in any real
// VT100-style serial terminal.
this.typeAheadBuffer = new FifoRingBuffer(128);
setBorderWidth(DEFAULT_BORDER_WIDTH);
addKeyListener(this);
addMouseListener(this);
@@ -39,7 +47,7 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
Border bevelBorder = BorderFactory.createBevelBorder(BevelBorder.LOWERED);
Border compoundBorder = BorderFactory.createCompoundBorder(emptyBorder, bevelBorder);
this.setBorder(compoundBorder);
}
}
/**
* Reset the console. This will cause the console to be cleared and the cursor returned to the
@@ -47,11 +55,11 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
*
*/
public void reset() {
typeAheadBuffer.reset();
getModel().clear();
getModel().setCursorColumn(0);
getModel().setCursorRow(0);
repaint();
this.hasInput = false;
}
/**
@@ -60,7 +68,7 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
* @return
*/
public boolean hasInput() {
return hasInput;
return !typeAheadBuffer.isEmpty();
}
/**
@@ -69,6 +77,24 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
* @param keyEvent The key event.
*/
public void keyTyped(KeyEvent keyEvent) {
char keyTyped = keyEvent.getKeyChar();
if (SWAP_CR_AND_LF) {
if (keyTyped == 0x0a) {
keyTyped = 0x0d;
}
else if (keyTyped == 0x0d) {
keyTyped = 0x0a;
}
}
if (SEND_CR_LF_FOR_CR && keyTyped == 0x0d) {
typeAheadBuffer.push(0x0d);
typeAheadBuffer.push(0x0a);
} else {
typeAheadBuffer.push(keyTyped);
}
keyEvent.consume();
}
@@ -78,8 +104,6 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
* @param keyEvent The key event.
*/
public void keyPressed(KeyEvent keyEvent) {
keyBuffer = keyEvent.getKeyChar();
hasInput = true;
keyEvent.consume();
}
@@ -88,9 +112,8 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
*
* @return The character typed.
*/
public char readInputChar() {
hasInput = false;
return this.keyBuffer;
public char readInputChar() throws FifoUnderrunException {
return (char)typeAheadBuffer.pop();
}
/**