diff --git a/samples/echo.asm b/samples/echo.asm new file mode 100644 index 0000000..759e27a --- /dev/null +++ b/samples/echo.asm @@ -0,0 +1,36 @@ +;; +;; Read input from the keyboard, and echo to console. +;; + + +.alias iobase $c000 +.alias iostatus [iobase + 1] +.alias iocmd [iobase + 2] +.alias ioctrl [iobase + 3] + +.org $0300 + +start: cli + lda #$09 + sta iocmd ; Set command status + lda #$16 + sta ioctrl ; 0 stop bits, 8 bit word, 300 baud + +;; Load a character from the keyboard and store it into +;; the accumulator + +getkey: lda iostatus ; Read the ACIA status + and #$08 ; Is the rx register empty? + beq getkey ; Yes, wait for it to fill + lda iobase ; Otherwise, read into accumulator + +;; Write the current char in the accumulator to the console + +write: pha ; Save accumulator + lda iostatus ; Read the ACIA status + and #$10 ; Is the tx register empty? + beq write ; No, wait for it to empty + pla ; Otherwise, load saved accumulator + sta iobase ; and write to output. + + jmp getkey ; Repeat diff --git a/samples/echo.prg b/samples/echo.prg new file mode 100644 index 0000000..e0190cd Binary files /dev/null and b/samples/echo.prg differ diff --git a/src/assets/new_led_images.psd b/src/assets/new_led_images.psd new file mode 100644 index 0000000..9e2b1d6 Binary files /dev/null and b/src/assets/new_led_images.psd differ diff --git a/src/main/java/com/loomcom/symon/Simulator.java b/src/main/java/com/loomcom/symon/Simulator.java index b3da9f1..dcfb896 100644 --- a/src/main/java/com/loomcom/symon/Simulator.java +++ b/src/main/java/com/loomcom/symon/Simulator.java @@ -14,6 +14,7 @@ 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; @@ -61,6 +62,9 @@ public class Simulator implements ActionListener, Observer { private JButton stepButton; private JButton resetButton; + // The most recently read key code + private char keyBuffer; + // TODO: loadMenuItem seriously violates encapsulation! // A far better solution would be to extend JMenu and add callback // methods to enable and disable menus as required. @@ -71,10 +75,7 @@ public class Simulator implements ActionListener, Observer { private JFileChooser fileChooser; private Preferences preferences; - private StringBuffer aciaBuffer; - public Simulator() throws MemoryRangeException { - this.aciaBuffer = new StringBuffer(512); this.acia = new Acia(ACIA_BASE); this.bus = new Bus(BUS_BOTTOM, BUS_TOP); this.cpu = new Cpu(); @@ -275,7 +276,8 @@ public class Simulator implements ActionListener, Observer { // Update status. SwingUtilities.invokeLater(new Runnable() { public void run() { - updateConsoleAndStatus(cpu); + // Now update the state + statusPane.updateState(cpu); } }); } catch (MemoryAccessException ex) { @@ -294,7 +296,8 @@ public class Simulator implements ActionListener, Observer { // immediate update after stepping manually. SwingUtilities.invokeLater(new Runnable() { public void run() { - updateConsoleAndStatus(cpu); + // Now update the state + statusPane.updateState(cpu); } }); } catch (SymonException ex) { @@ -321,9 +324,27 @@ public class Simulator implements ActionListener, Observer { cpu.step(); - // Read from the ACIA - while (acia.hasTxChar()) { - aciaBuffer.append((char) acia.txRead()); + // Read from the ACIA and immediately update the console if there's + // output ready. + if (acia.hasTxChar()) { + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + console.print(Character.toString((char)acia.txRead())); + console.repaint(); + } + }); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + // If a key has been pressed, fill the ACIA. + // TODO: Interrupt handling. + if (console.hasInput()) { + acia.rxWrite((int)console.readInputChar()); } // This is a very expensive update, and we're doing it without @@ -332,7 +353,8 @@ public class Simulator implements ActionListener, Observer { if (stepsSinceLastUpdate++ > MAX_STEPS_BETWEEN_UPDATES) { SwingUtilities.invokeLater(new Runnable() { public void run() { - updateConsoleAndStatus(cpu); + // Now update the state + statusPane.updateState(cpu); } }); stepsSinceLastUpdate = 0; @@ -361,28 +383,12 @@ public class Simulator implements ActionListener, Observer { // Immediately update the UI. SwingUtilities.invokeLater(new Runnable() { public void run() { - updateConsoleAndStatus(cpu); + // Now update the state + statusPane.updateState(cpu); } }); } - /** - * Update the UI with the latest state of the simulator. - */ - private void updateConsoleAndStatus(Cpu cpu) { - // If we have accumulated any ACIA output in our temporary - // buffer since the last UI update, write it to the console, - // then clear the buffer. - if (aciaBuffer.length() > 0) { - console.print(aciaBuffer.toString()); - console.repaint(); - aciaBuffer.delete(0, aciaBuffer.length()); - } - - // Now update the state - statusPane.updateState(cpu); - } - public static void main(String args[]) { SwingUtilities.invokeLater(new Runnable() { public void run() { @@ -457,8 +463,8 @@ public class Simulator implements ActionListener, Observer { stepButton.setEnabled(true); loadMenuItem.setEnabled(true); runStopButton.setText("Run"); - // Update state. - updateConsoleAndStatus(cpu); + // Now update the state + statusPane.updateState(cpu); } }); diff --git a/src/main/java/com/loomcom/symon/devices/Acia.java b/src/main/java/com/loomcom/symon/devices/Acia.java index bf3fcb1..9cb0b32 100644 --- a/src/main/java/com/loomcom/symon/devices/Acia.java +++ b/src/main/java/com/loomcom/symon/devices/Acia.java @@ -32,6 +32,8 @@ public class Acia extends Device { private int commandRegister; private int controlRegister; + private boolean overrun = false; + /** * Read/Write buffers */ @@ -52,6 +54,8 @@ public class Acia extends Device { case DATA_REG: return rxRead(); case STAT_REG: + // TODO: Overrun, Parity Error, Framing Error, + // DTR, DSR, and Interrupt flags. return ((rxFull ? 0x08 : 0x00) | (txFull ? 0x00 : 0x10)); case CMND_REG: diff --git a/src/main/java/com/loomcom/symon/ui/Console.java b/src/main/java/com/loomcom/symon/ui/Console.java index 8e905e6..ce888a0 100644 --- a/src/main/java/com/loomcom/symon/ui/Console.java +++ b/src/main/java/com/loomcom/symon/ui/Console.java @@ -18,6 +18,9 @@ import javax.swing.border.Border; public class Console extends JTerminal implements KeyListener, MouseListener { + private boolean hasInput = false; + private char keyBuffer; + public Console() { super(); addKeyListener(this); @@ -33,6 +36,7 @@ public class Console extends JTerminal implements KeyListener, MouseListener { getModel().setCursorColumn(0); getModel().setCursorRow(0); repaint(); + this.hasInput = false; } public void keyTyped(KeyEvent keyEvent) { @@ -40,19 +44,23 @@ public class Console extends JTerminal implements KeyListener, MouseListener { } public void keyPressed(KeyEvent keyEvent) { - int keyCode = keyEvent.getKeyCode(); - int modifiersMask = keyEvent.getModifiers(); - int modifiersExMask = keyEvent.getModifiersEx(); + keyBuffer = keyEvent.getKeyChar(); + hasInput = true; - System.out.println("Key Pressed #" + keyEvent.getKeyCode() + " : " + - KeyEvent.getKeyText(keyCode) + " MASK : " + - modifiersMask + " EXT MASK : " + - modifiersExMask - ); + System.out.println("Key Pressed (0x" + Integer.toString((int)keyBuffer, 16) + ") : " + keyBuffer); keyEvent.consume(); } + public boolean hasInput() { + return hasInput; + } + + public char readInputChar() { + hasInput = false; + return this.keyBuffer; + } + public void keyReleased(KeyEvent keyEvent) { keyEvent.consume(); }