added visualizer
authoradam <adam@megacz.com>
Mon, 12 Jun 2006 15:32:35 +0000 (16:32 +0100)
committeradam <adam@megacz.com>
Mon, 12 Jun 2006 15:32:35 +0000 (16:32 +0100)
src/edu/berkeley/obits/AtmelSerial.java
src/edu/berkeley/obits/Visual.java [new file with mode: 0644]
src/edu/berkeley/obits/device/atmel/At40k.java
src/edu/berkeley/obits/device/atmel/AtmelDevice.java
src/edu/berkeley/obits/device/atmel/AvrDrone.java

index a67d936..444c944 100644 (file)
@@ -29,7 +29,8 @@ public class AtmelSerial {
     }
     public static int PIPELEN=20;
     public static void main(String[] s) throws Exception {
-        AvrDrone device = new AvrDrone(detectObitsPort());
+        //AvrDrone device = new AvrDrone(detectObitsPort());
+        AvrDrone device = new AvrDrone();
         At40k at40k = new At40k.At40k10(device);
         int count = 0;
         try {
@@ -424,6 +425,12 @@ public class AtmelSerial {
             //scan(at40k, cell, YLUT, false);
 
             //device.scanFPGA(true);
+
+            Visual vis = new Visual(at40k);
+            vis.show();
+            vis.setSize(600, 600);
+            synchronized(AtmelSerial.class) { AtmelSerial.class.wait(); }
+
             Visualizer v = new Visualizer(at40k, device);
             v.show();
             v.setSize(1380, 1080);
diff --git a/src/edu/berkeley/obits/Visual.java b/src/edu/berkeley/obits/Visual.java
new file mode 100644 (file)
index 0000000..9e20da0
--- /dev/null
@@ -0,0 +1,396 @@
+package edu.berkeley.obits;
+
+import static edu.berkeley.obits.device.atmel.AtmelDevice.Constants.*;
+import static edu.berkeley.obits.device.atmel.AtmelDevice.Util.*;
+import edu.berkeley.obits.device.atmel.*;
+import java.awt.*;
+import java.awt.geom.*;
+import java.awt.event.*;
+import java.awt.color.*;
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+
+public class Visual extends JFrame implements KeyListener {
+
+    public static int SIZE = 92;
+    public static int RINGS = 3;
+    public static int BEVEL = 5;
+    public static int CORE_SIZE = 64;
+    public static int CORE_OFFSET = 10;
+
+    public static int HOFF = 52;
+
+    /*
+    public static void main(String[] s) {
+        Visual v = new Visual();
+        v.show();
+        v.setSize(400, 400);
+    }
+    */
+
+    At40k at40k;
+
+    public Visual(At40k at40k) {
+        this.at40k = at40k;
+        for(int x=0; x<7; x++)
+            for(int y=0; y<7; y++)
+                new Cell(x,y, at40k.cell(x+7, y+7));
+        addKeyListener(this);
+    }
+
+    private HashSet<Cell> cells = new HashSet<Cell>();
+
+    public class Cell {
+        Graphics2D g;
+        At40k.Cell cell;
+        int _x, _y;
+        public Cell(int x, int y, At40k.Cell cell) {
+            _x = x;
+            _y = y;
+            this.cell = cell;
+            cells.add(this);
+        }
+        public void draw() {
+            drawHwires();
+            drawVwires();
+            drawInternalRouting();
+            drawLocal();
+            drawGates();
+            drawMux();
+            drawRegister();
+            drawBorder();
+        }
+        public void drawHwires() {
+            g.setColor(Color.magenta);
+            for(int i=0; i<5; i++)
+                if (cell.hwire(i).isDriven())
+                    g.drawLine(0, SIZE-(2*(1+RINGS)+2*i), SIZE, SIZE-(2*(1+RINGS)+2*i));
+        }
+        public void drawVwires() {
+            g.setColor(Color.magenta);
+            for(int i=0; i<5; i++)
+                if (cell.vwire(i).isDriven())
+                    g.drawLine(2*(1+RINGS)+2*i, 0, 2*(1+RINGS)+2*i, SIZE);
+        }
+        public void drawInternalRouting() {
+        }
+
+        public void drawLocal() {
+            if (!cell.ylut_relevant() && !cell.ylut_relevant()) return;
+            Point2D in   = new Point2D.Double(HOFF, 0);
+            Point2D join = new Point2D.Double(HOFF, CORE_OFFSET);
+            rotateOuter(in);
+            rotateInner(join);
+            int rot = rot();
+            switch(rot) {
+                case 0: case 2:
+                    join.setLocation(in.getX(), join.getY());
+                    break;
+                case 1: case 3:
+                    join.setLocation(join.getX(), in.getY());
+                    break;
+            }
+
+            Point2D xi = null;
+            g.setColor(new Color(0xff, 0x00, 0x00));
+            int xring = 4;
+            switch(cell.xi()) {
+                case NW:
+                    xi = new Point2D.Double(0+2*xring, SIZE-2*xring);
+                    g.draw(new Line2D.Double(new Point2D.Double(0, SIZE), xi));
+                    break;
+
+                case SW:
+                    xi = new Point2D.Double(0+2*xring, 0+2*xring);
+                    g.draw(new Line2D.Double(new Point2D.Double(0, 0), xi));
+                    break;
+
+                case NE:
+                    xi = new Point2D.Double(SIZE-2*xring, SIZE-2*xring);
+                    g.draw(new Line2D.Double(new Point2D.Double(SIZE, SIZE), xi));
+                    break;
+
+                case SE:
+                    xi = new Point2D.Double(SIZE-2*xring, 0+2*xring);
+                    g.draw(new Line2D.Double(new Point2D.Double(SIZE, 0), xi));
+                    break;
+
+            }
+
+            if (cell.xlut_relevant()) {
+                g.setColor(new Color(0x00, 0x00, 0xff));
+                Point2D c   = new Point2D.Double(SIZE - CORE_OFFSET - CORE_SIZE/2 - CORE_SIZE / 6, 20);
+                rotateInner(c);
+                route(in, c, 5);
+
+                g.setColor(new Color(0xff, 0x00, 0x00));
+                c   = new Point2D.Double(SIZE - CORE_OFFSET - CORE_SIZE/2 - CORE_SIZE / 3, 20);
+                rotateInner(c);
+                if (xi != null)
+                    route(xi, c, 4);
+
+                Point2D xo   = new Point2D.Double(SIZE-CORE_OFFSET-CORE_SIZE+17 - 2, CORE_OFFSET + 41 - 3);
+                Point2D xout = new Point2D.Double(SIZE-CORE_OFFSET-CORE_SIZE+17 - 2, CORE_OFFSET + CORE_SIZE - 3);
+                rotateInner(xo);
+                rotateInner(xout);
+                g.setColor(new Color(0xff, 0xcc, 0xcc));
+                g.draw(new Line2D.Double(xo, xout));
+                if (cell.ne() != null && cell.ne().xi()==SW) {
+                    Point2D xoo = new Point2D.Double(SIZE-2*xring, SIZE-2*xring);
+                    g.draw(new Line2D.Double(new Point2D.Double(SIZE, SIZE), xoo));
+                    route(xout, xoo, xring);
+                }
+                if (cell.nw() != null && cell.nw().xi()==SE) {
+                    Point2D xoo = new Point2D.Double(0+2*xring, SIZE-2*xring);
+                    g.draw(new Line2D.Double(new Point2D.Double(0, SIZE), xoo));
+                    route(xout, xoo, xring);
+                }
+                if (cell.se() != null && cell.se().xi()==NW) {
+                    Point2D xoo = new Point2D.Double(SIZE-2*xring, 0+2*xring);
+                    g.draw(new Line2D.Double(new Point2D.Double(SIZE, 0), xoo));
+                    route(xout, xoo, xring);
+                }
+                if (cell.sw() != null && cell.sw().xi()==NE) {
+                    Point2D xoo = new Point2D.Double(0+2*xring, 0+2*xring);
+                    g.draw(new Line2D.Double(new Point2D.Double(0, 0), xoo));
+                    route(xout, xoo, xring);
+                }
+            }
+
+            if (cell.ylut_relevant()) {
+                g.setColor(new Color(0x00, 0x00, 0xff));
+                Point2D c   = new Point2D.Double(SIZE - CORE_OFFSET - CORE_SIZE/2 + CORE_SIZE / 6, 20);
+                rotateInner(c);
+                route(in, c, 5);
+
+                g.setColor(new Color(0xff, 0x00, 0x00));
+                c   = new Point2D.Double(SIZE - CORE_OFFSET - CORE_SIZE/2 + CORE_SIZE / 3, 20);
+                rotateInner(c);
+                if (xi != null)
+                    route(xi, c, 4);
+
+                Point2D yo   = new Point2D.Double(SIZE-CORE_OFFSET-CORE_SIZE+51 - 2, CORE_OFFSET + 41 - 3);
+                Point2D yout = new Point2D.Double(SIZE-CORE_OFFSET-CORE_SIZE+51 - 2, CORE_OFFSET + CORE_SIZE - 3);
+                rotateInner(yo);
+                rotateInner(yout);
+                g.setColor(new Color(0xbb, 0xbb, 0xff));
+                //g.setColor(new Color(0x00, 0x00, 0xff));
+                g.draw(new Line2D.Double(yo, yout));
+                if (cell.north() != null && cell.north().yi()==SOUTH) route(yout, new Point2D.Double(SIZE-40, SIZE+ 0), 2);
+                if (cell.east() != null  && cell.east().yi()==WEST)   route(yout, new Point2D.Double(SIZE+ 0,      40), 2);
+                if (cell.south() != null && cell.south().yi()==NORTH) route(yout, new Point2D.Double(     40,       0), 2);
+                if (cell.west() != null  && cell.west().yi()==EAST)   route(yout, new Point2D.Double(      0, SIZE-40), 2);
+            }
+
+        }
+
+        private void rotateOuter(Point2D p) {
+            int rot = rot();
+            AffineTransform a = new AffineTransform();
+            a.rotate((Math.PI/2) * rot);
+            switch(rot) {
+                case 0: break;
+                case 1: a.translate(0,  -SIZE); break;
+                case 2: a.translate(-SIZE, -SIZE); break;
+                case 3: a.translate(-SIZE, 0); break;
+            }
+            a.transform(p, p);
+        }
+
+        private void rotateInner(Point2D p) {
+            int rot = rot();
+            AffineTransform a = new AffineTransform();
+            a.translate(SIZE-CORE_SIZE-CORE_OFFSET, CORE_OFFSET);
+            a.rotate((Math.PI/2) * rot);
+            switch(rot) {
+                case 0: break;
+                case 1: a.translate(0,  -CORE_SIZE); break;
+                case 2: a.translate(-CORE_SIZE, -CORE_SIZE); break;
+                case 3: a.translate(-CORE_SIZE, 0); break;
+            }
+            a.translate(-1 * (SIZE-CORE_SIZE-CORE_OFFSET), -CORE_OFFSET);
+            a.transform(p, p);
+        }
+
+        private Point2D project(Point2D p1, int ring) {
+            double north = Math.abs( (SIZE-(ring*2)) - p1.getY() );
+            double south = Math.abs( (     (ring*2)) - p1.getY() );
+            double east  = Math.abs( (SIZE-(ring*2)) - p1.getX() );
+            double west  = Math.abs( (     (ring*2)) - p1.getX() );
+            if (north < south && north < east && north < west) {
+                return new Point2D.Double(p1.getX(), SIZE-ring*2);
+            } else if (south < east && south < west) {
+                return new Point2D.Double(p1.getX(), ring*2);
+            } else if (east < west) {
+                return new Point2D.Double(SIZE-ring*2, p1.getY());
+            } else {
+                return new Point2D.Double(ring*2, p1.getY());
+            }
+        }
+
+        private void route(Point2D p1, Point2D p2, int ring) {
+            int ringpos = ring * 2;
+            Point2D projected = project(p1, ring);
+            g.draw(new Line2D.Double(p1, projected));
+            p1 = projected;
+
+            projected = project(p2, ring);
+            g.draw(new Line2D.Double(p2, projected));
+            p2 = projected;
+
+            double x1 = p1.getX();
+            double y1 = p1.getY();
+            double x2 = p2.getX();
+            double y2 = p2.getY();
+
+            if (x1==x2 || y1==y2) {
+                g.draw(new Line2D.Double(p1, p2));
+                return;
+            }
+
+            if ((x1==SIZE-ring*2 || x1==ring*2) && !(y1==SIZE-ring*2 || y1==ring*2)) {
+                Point2D p3 = new Point2D.Double(x1, y2 > SIZE/2 ? SIZE-ring*2 : ring*2);
+                g.draw(new Line2D.Double(p1, p3));
+                route(p3, p2, ring);
+            }
+
+            if (y1==SIZE-ring*2 || y1==ring*2) {
+                Point2D p3 = new Point2D.Double(x2 > SIZE/2 ? SIZE-ring*2 : ring*2, y1);
+                g.draw(new Line2D.Double(p1, p3));
+                route(p3, p2, ring);
+            }
+
+        }
+        
+        private int rot() {
+            int rot = 0;
+            switch(cell.yi()) {
+                case SOUTH: rot = 0; break;
+                case NORTH: rot = 2; break;
+                case EAST: rot = 1; break;
+                case WEST: rot = 3; break;
+                default: {
+                    // FIXME: choose based on xin
+                    if (cell.north() != null && cell.north().yi()==SOUTH) { rot = 0; break; }
+                    if (cell.south() != null && cell.south().yi()==NORTH) { rot = 2; break; }
+                    if (cell.east()  != null && cell.east().yi()==WEST) { rot = 3; break; }
+                    if (cell.west()  != null && cell.west().yi()==EAST) { rot = 1; break; }
+                }
+            }
+            return rot;
+        }
+        
+        public void drawGates() {
+            AffineTransform t = g.getTransform();
+            try {
+                g.translate(SIZE-CORE_SIZE-CORE_OFFSET, CORE_OFFSET);
+                
+                int rot = rot();
+                g.rotate((Math.PI/2) * rot);
+                switch(rot) {
+                    case 0: break;
+                    case 1: g.translate(0,  -CORE_SIZE); break;
+                    case 2: g.translate(-CORE_SIZE, -CORE_SIZE); break;
+                    case 3: g.translate(-CORE_SIZE, 0); break;
+                }
+
+                //g.setColor(Color.gray);
+                //g.drawRect(0, 0, CORE_SIZE, CORE_SIZE);
+                g.scale(1, -1);
+
+                GeneralPath p = new GeneralPath();
+                p.moveTo(29.141f, 36.301f);
+                p.lineTo(29.141f, 36.301f-7.161f);
+                p.curveTo(27.71f, 11.24f, 23.413f, 9.45f, 14.82f, 0.5f);
+                p.curveTo(6.229f, 9.45f, 1.932f, 11.24f, 0.5f, 29.141f);
+                p.lineTo(0.5f, 29.141f+7.161f);
+                float x = 0.5f;
+                float y = 29.141f+7.161f;
+                p.curveTo(5.729f+x, -1.789f+y,
+                          6.444f+x, -2.686f+y,
+                          14.32f+x, -3.58f+y);
+                p.curveTo(22.697f, 33.616f, 23.413f, 34.512f, 29.141f, 36.301f);
+                g.translate(0,   -40f);
+                if (cell.xlut_relevant()) {
+                    g.setColor(Color.white);
+                    g.fill(p);
+                    g.setColor(Color.red);
+                    g.draw(p);
+                }
+                g.translate(34f, 0f);
+                if (cell.ylut_relevant()) {
+                    g.setColor(Color.white);
+                    g.fill(p);
+                    g.setColor(Color.blue);
+                    g.draw(p);
+                }
+            } finally {
+                g.setTransform(t);
+            }
+        }
+        public void drawMux() {
+            if (cell.c() != ZMUX) return;
+            g.setColor(Color.black);
+            g.drawLine(46, 54,     46+2, 54+5);
+            g.drawLine(46+2, 54+5, 60-2, 54+5);
+            g.drawLine(60-2, 54+5, 60,   54);
+            g.drawLine(60,   54,   46, 54);
+        }
+        public void drawRegister() {
+        }
+        public void drawBorder() {
+            g.setColor(Color.gray);
+            //g.drawLine(0, BEVEL, BEVEL,    0);
+            g.drawLine(BEVEL, 0, SIZE-BEVEL, 0);
+            //g.drawLine(SIZE-BEVEL, 0, SIZE, BEVEL);
+            g.drawLine(SIZE, BEVEL, SIZE, SIZE-BEVEL);
+            //g.drawLine(SIZE, SIZE-BEVEL, SIZE-BEVEL, SIZE);
+            g.drawLine(SIZE-BEVEL, SIZE, BEVEL, SIZE);
+            //g.drawLine(BEVEL, SIZE, 0, SIZE-BEVEL);
+            g.drawLine(0, SIZE-BEVEL, 0, BEVEL);
+        }
+    }
+
+    public void paint(Graphics _g) {
+        Graphics2D g = (Graphics2D)_g;
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        g.setRenderingHint(RenderingHints.KEY_RENDERING,    RenderingHints.VALUE_RENDER_QUALITY);
+        g.setStroke(new BasicStroke((float)0.5));
+        g.translate(10, 0);
+        g.scale(1, -1);
+        g.translate(5, -1 * getHeight() + 10);
+        g.scale(scale,scale);
+        for(Cell c : cells) {
+            AffineTransform t = g.getTransform();
+            g.translate(     c._x * SIZE/* + (10 * (c._x/4))*/,      c._y * SIZE/* + (10 * (c._y/4))*/);
+            c.g = g;
+            c.draw();
+            c.g = null;
+            g.setTransform(t);
+        }
+    }
+
+    double scale = 1.0;
+
+    public void clear() {
+        Graphics2D g = (Graphics2D)getGraphics();
+        //g.setColor(Color.black);
+        //g.setColor(Color.lightGray);
+        g.clearRect(0, 0, getWidth(), getHeight());
+    }
+
+    public void keyTyped(KeyEvent k) {
+    }
+    public void keyReleased(KeyEvent k) {
+    }
+    public void keyPressed(KeyEvent keyevent) {
+        char c = keyevent.getKeyChar();
+        switch(c) {
+            case '+': scale += 0.1; clear(); paint(getGraphics()); return;
+            case '-': scale -= 0.1; clear(); paint(getGraphics()); return;
+        }
+    }
+
+}
index c7aace0..828ad3e 100644 (file)
@@ -51,6 +51,14 @@ public class At40k {
             this.col= horizontal ? (col & ~0x3) : col;
             this.row=!horizontal ? (row & ~0x3) : row;
         }
+        public boolean isDriven() {
+            // FIXME: bridging connections (horiz-to-vert)
+            for(int i=0; i<4; i++)
+                if (cell(horizontal ? col+i : col,
+                         horizontal ? row   : row+i).out(plane)) return true;
+            // FIXME: sector switchbox drivers
+            return false;
+        }
         private int z(int z)       { return (horizontal ? 0x30 : 0x20) | z; }
         public int code(boolean topleft) {
             switch(plane) {
@@ -146,6 +154,10 @@ public class At40k {
         public Cell west() { return cell(col-1, row); }
         public Cell north() { return cell(col,   row+1); }
         public Cell south() { return cell(col,   row-1); }
+        public Cell ne() { return cell(col+1, row+1); }
+        public Cell nw() { return cell(col-1, row+1); }
+        public Cell se() { return cell(col+1, row-1); }
+        public Cell sw() { return cell(col-1, row-1); }
 
         public Sector sector() { return new Sector(this); }
         public Cell(int col, int row) {
@@ -301,6 +313,32 @@ public class At40k {
             dev.mode4(1, row, col, result, 0x34);
         }
 
+        public boolean xlut_relevant() {
+            if ((c()==XLUT || c()==ZMUX) && c_relevant()) return true;
+            if (xo()) return false;
+            if (nw() != null && nw().xi()==SE) return true;
+            if (ne() != null && ne().xi()==SW) return true;
+            if (sw() != null && sw().xi()==NE) return true;
+            if (se() != null && se().xi()==NW) return true;
+            return false;
+        }
+        public boolean ylut_relevant() {
+            if ((c()==YLUT || c()==ZMUX) && c_relevant()) return true;
+            if (yo()) return false;
+            if (north() != null && north().yi()==SOUTH) return true;
+            if (east() != null  && east().yi()==WEST) return true;
+            if (south() != null && south().yi()==NORTH) return true;
+            if (west() != null  && west().yi()==EAST) return true;
+            return false;
+        }
+        public boolean c_relevant() {
+            // FIXME: feedback line!
+            for(int i=0; i<5; i++)
+                if (out(i)) return true;
+            if (xo() || yo()) return true;
+            return false;
+        }
+
         public void c(int source) {
             switch(source) {
                 case XLUT: dev.mode4(1, row, col, 0x00, 0xc0); break;
@@ -319,6 +357,8 @@ public class At40k {
         }
         public void b(boolean registered) { dev.mode4(1, row, col, 3, !registered); }
         public void f(boolean registered) { dev.mode4(1, row, col, 2, !registered); }
+        public boolean xo()    { return (dev.mode4(1, row, col) & 0x01) != 0; }
+        public boolean yo()    { return (dev.mode4(1, row, col) & 0x02) != 0; }
         public void xo(boolean center)    { dev.mode4(1, row, col, 1, center); }
         public void yo(boolean center)    { dev.mode4(1, row, col, 0, center); }
         public boolean b() { return (dev.mode4(1, row, col) >> 3)!=1; }
@@ -326,6 +366,14 @@ public class At40k {
         public boolean x() { return (dev.mode4(1, row, col) >> 1)==1; }
         public boolean y() { return (dev.mode4(1, row, col) >> 0)==1; }
 
+        public int oe() {
+            switch (dev.mode4(0x02, row, col) & 0x3) {
+                case 0: return NONE;
+                case 1: return H4;
+                case 2: return V4;
+                default: throw new RuntimeException("invalid argument");                    
+            }
+        }
         public void oe(int source) {
             switch(source) {
                 case NONE: dev.mode4(0x02, row, col, 0, 0x3); break;
index 50c5c47..27f6c36 100644 (file)
@@ -29,9 +29,9 @@ public abstract class AtmelDevice extends Bits implements Device {
         public static final int L4    = 4;
 
         public static final int NORTH = 8;
-        public static final int SOUTH = 9;
-        public static final int EAST  = 10;
-        public static final int WEST  = 11;
+        public static final int WEST  = 9;
+        public static final int SOUTH = 10;
+        public static final int EAST  = 11;
 
         public static final int XLUT  = 12;
         public static final int YLUT  = 13;
index 2e50094..fc65f01 100644 (file)
@@ -15,6 +15,7 @@ public class AvrDrone extends AtmelDevice {
 
     final SerialPort sp;
 
+    public AvrDrone() { sp = null; in = null; out = null; } 
     public AvrDrone(SerialPort sp) throws IOException, UnsupportedCommOperationException, InterruptedException, DeviceException {
         this.sp = sp;
         //sp.setSerialPortParams(115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
@@ -38,6 +39,7 @@ public class AvrDrone extends AtmelDevice {
     }
 
     public synchronized void scanFPGA(boolean on) throws DeviceException {
+        if (sp==null) return;
         try {
             if (on) {
                 out.writeByte(3);
@@ -50,6 +52,7 @@ public class AvrDrone extends AtmelDevice {
     // fixme!
     public static int retval = 0;
     public synchronized int readCount() throws DeviceException {
+        if (sp==null) return 0;
         try {
             if (reader != null) {
                 reader.start();
@@ -85,7 +88,7 @@ public class AvrDrone extends AtmelDevice {
             public void run() {
                 while(true) {
                     try {
-                        byte b = in.readByte();
+                        byte b = sp==null ? 0 : in.readByte();
                         ByteCallback bc = (ByteCallback)callbacks.remove(0);
                         bc.call(b);
                     } catch (Exception e) {
@@ -98,8 +101,10 @@ public class AvrDrone extends AtmelDevice {
     public synchronized void readBus(ByteCallback bc) throws DeviceException {
         try {
             callbacks.add(bc);
-            out.writeByte(2);
-            out.flush();
+            if (sp!=null) {
+                out.writeByte(2);
+                out.flush();
+            }
             if (reader != null) {
                 reader.start();
                 reader = null;
@@ -110,8 +115,10 @@ public class AvrDrone extends AtmelDevice {
     public synchronized void readInterrupts(ByteCallback bc) throws DeviceException {
         try {
             callbacks.add(bc);
-            out.writeByte(6);
-            out.flush();
+            if (sp!=null) {
+                out.writeByte(6);
+                out.flush();
+            }
             if (reader != null) {
                 reader.start();
                 reader = null;
@@ -120,6 +127,7 @@ public class AvrDrone extends AtmelDevice {
     }
 
     public synchronized void reset() throws DeviceException {
+        if (sp==null) return;
         try {
             Log.info(this, "resetting device");
             sp.setDTR(true);
@@ -165,18 +173,20 @@ public class AvrDrone extends AtmelDevice {
             boolean xdec    = x==lastx-1;
             
             //System.out.println(zchange + " " + ychange + " " + xchange);
-            out.writeByte(0x80
-                          | (zinc?0x40:zdec?0x04:zchange?0x44:0x00)
-                          | (yinc?0x20:ydec?0x02:ychange?0x22:0x00)
-                          | (xinc?0x10:xdec?0x01:xchange?0x11:0x00));
-            if (!zinc && !zdec && zchange) out.writeByte(z); else save++;
-            if (!yinc && !ydec && ychange) out.writeByte(y); else save++;
-            if (!xinc && !xdec && xchange) out.writeByte(x); else save++;
-            saveof++;
-            lastz = z;
-            lastx = x;
-            lasty = y;
-            out.writeByte(d);
+            if (sp!=null) {
+                out.writeByte(0x80
+                              | (zinc?0x40:zdec?0x04:zchange?0x44:0x00)
+                              | (yinc?0x20:ydec?0x02:ychange?0x22:0x00)
+                              | (xinc?0x10:xdec?0x01:xchange?0x11:0x00));
+                if (!zinc && !zdec && zchange) out.writeByte(z); else save++;
+                if (!yinc && !ydec && ychange) out.writeByte(y); else save++;
+                if (!xinc && !xdec && xchange) out.writeByte(x); else save++;
+                saveof++;
+                lastz = z;
+                lastx = x;
+                lasty = y;
+                out.writeByte(d);
+            }
             if (cache[x & 0xff]==null) cache[x & 0xff] = new byte[24][];
             if (cache[x & 0xff][y & 0xff]==null) cache[x & 0xff][y & 0xff] = new byte[256];
             cache[x & 0xff][y & 0xff][z & 0xff] = (byte)(d & 0xff);
@@ -184,6 +194,7 @@ public class AvrDrone extends AtmelDevice {
     }
 
     public synchronized void flush() throws DeviceException {
+        if (sp==null) return;
         try {
             out.flush();
         } catch (IOException e) { throw new DeviceException(e); }