diff --git a/exec/dorkit/screen.js b/exec/dorkit/screen.js
new file mode 100644
index 0000000000000000000000000000000000000000..5d65da7a9f911f18484653a8d2397e2609cb5cd6
--- /dev/null
+++ b/exec/dorkit/screen.js
@@ -0,0 +1,369 @@
+/*
+ * Screen object...
+ * This object is the estimate of what a screen looks like modeled
+ * as a Graphic object (Screen.graphic).
+ * 
+ * The Graphic object should not be written directly, but instead,
+ * all data should be provided using the print() method.
+ */
+
+if (js.global.Graphic === undefined)
+	load("graphic.js");
+if (js.global.Attribute === undefined)
+	load("attribute.js");
+
+function Screen(w, h, attr, fill)
+{
+	this.graphic = new Graphic(w, h, attr, fill);
+	this.escbuf = '';
+	this.pos = {x:0, y:0};
+	this.stored_pos = {x:0, y:0};
+	this.attr = new Attribute(7);
+}
+
+Screen.prototype.print=function(str) {
+	var m;
+	var ext;
+	var pb;
+	var ib;
+	var fb;
+	var p;
+	var chars;
+	var remain;
+	var i;
+	var tg;
+	var seq;
+
+	function writech(scr, ch) {
+		var i;
+		var gr;
+
+		function check_scrollup(scr) {
+			while (scr.pos.y >= scr.graphic.height) {
+				// Scroll up...
+				gr = scr.graphic.get(0,1,scr.graphic.width-1, scr.graphic.height-2);
+				scr.graphic.put(gr,0,0);
+				for (i=0; i<scr.graphic.width; i++) {
+					scr.graphic.data[i][scr.graphic.height-1].ch = scr.graphic.ch;
+					scr.graphic.data[i][scr.graphic.height-1].attr = scr.attr.value;
+				}
+				scr.pos.y--;
+			}
+		}
+
+		// Handle special chars.
+		switch(ch) {
+			case '\x00':	// NUL is not displayed.
+				break;
+			case '\x07':	// Beep is not displayed.
+				break;
+			case '\x08':	// Backspace.
+				scr.pos.x--;
+				if (scr.pos.x < 0)
+					scr.pos.x = 0;
+				break;
+			case '\x09':	// Tab
+				do {
+					scr.pos.x++;
+				} while(scr.pos.x % 8);
+				if (scr.pos.x >= scr.graphic.width)
+					scr.pos.x = scr.graphic.width-1;
+				break;
+			case '\x0a':	// Linefeed
+				scr.pos.y++;
+				check_scrollup(scr);
+				break;
+			case '\x0c':	// For feed (clear screen and home)
+				scr.graphic.clear();
+				scr.pos.x=0;
+				scr.pos.y=0;
+				break;
+			case '\x0d':	// Carriage return
+				scr.pos.x = 0;
+				break;
+			default:
+				scr.graphic.data[scr.pos.x][scr.pos.y].ch = ch;
+				scr.graphic.data[scr.pos.x][scr.pos.y].attr = new Attribute(scr.attr);
+				scr.pos.x++;
+				if (scr.pos.x >= scr.graphic.width) {
+					scr.pos.x = 0;
+					scr.pos.y++;
+					check_scrollup(scr);
+				}
+				break;
+		}
+	}
+
+	function param_defaults(params, defaults) {
+		var i;
+
+		for (i=0; i<defaults.length; i++) {
+			if (params[i] == undefined || params[i].length == 0)
+				params[i]=defaults[i];
+		}
+		for (i=0; i<params.length; i++) {
+			if (params[i]===undefined || params[i]==='')
+				params[i] = 0;
+			else
+				params[i] = parseInt(params[i], 10);
+		}
+	}
+
+	// Prepend the ESC buffer to avoid failing on split ANSI
+	str = this.escbuf + str;
+	this.escbuf = '';
+
+	while((m=str.match(/^(.*?)\x1b\[([<-\?]{0,1})([0-;]*)([ -\/]*)([@-~])([\x00-\xff]*)$/)) !== null) {
+		chars = m[1];
+		ext = m[2];
+		pb = m[3];
+		ib = m[4];
+		fb = m[5];
+		remain = m[6];
+		seq = ext + ib + fb;
+		var x;
+		var y;
+
+		str = remain;
+
+		// Send regular chars before the sequence...
+		for (i=0; i<chars.length; i++)
+			writech(this, chars[i]);
+
+		// We don't support any : paramters... strip and ignore.
+		p = pb.replace(/:[^;:]*/g, '');
+		p = p.split(';');
+
+		switch(fb) {
+			case '@':	// Insert character
+				param_defaults(p, [1]);
+				if (p[1] > this.graphic.width - this.pos.x)
+					p[1] = this.graphic.width - this.pos.x;
+				if (this.pos.x < this.graphic.width-1) {
+					tg = this.graphic.get(this.pos.x, this.pos.y, this.graphic.width-1 - p[1], this.pos.y);
+					tg = this.graphic.put(this.pos.x + p[1], this.pos.y, this.graphic.width - 1, this.pos.y);
+				}
+				for (x = 0; x<p[1]; x++) {
+					this.graphic.data[this.pos.x + x][this.pos.y].ch = this.graphic.ch;
+					this.graphic.data[this.pos.x + x][this.pos.y].attr = this.attr.value;
+				}
+				break;
+			case 'A':	// Cursor Up
+				param_defaults(p, [1]);
+				this.pos.y -= p[0];
+				if (this.pos.y < 0)
+					this.pos.y = 0;
+				break;
+			case 'B':	// Cursor Down
+				param_defaults(p, [1]);
+				this.pos.y += p[0];
+				if (this.pos.y >= this.graphic.height)
+					this.pos.y = this.graphic.height-1;
+				break;
+			case 'C':	// Cursor Right
+				param_defaults(p, [1]);
+				this.pos.x += p[0];
+				if (this.pos.x >= this.graphic.width)
+					this.pos.x = this.graphic.width-1;
+				break;
+			case 'D':	// Cursor Left
+				param_defaults(p, [1]);
+				this.pos.x -= p[0];
+				if (this.pos.x < 0)
+					this.pos.x = 0;
+				break;
+			case 'H':	// Cursor position
+			case 'f':
+				param_defaults(p, [1,1]);
+				if (p[0] >= 0 && p[0] < this.graphic.height && p[1] >= 0 && p[1] <= this.graphic.width) {
+					this.pos.x = p[1];
+					this.pos.y = p[0];
+				}
+				break;
+			case 'J':	// Erase in screen
+				param_defaults(p, [0]);
+				switch(p[0]) {
+					case 0:	// Erase to end of screen...
+						for (x = this.pos.x; x<this.pos.width; x++) {
+							this.graphic.data[x][this.pos.y].ch = this.graphic.ch;
+							this.graphic.data[x][this.pos.y].attr = this.attr.value;
+						}
+						for (y = this.pos.y+1; y<this.pos.height; y++) {
+							for (x = 0; x<this.graphic.width; x++) {
+								this.graphic.data[x][y].ch = this.graphic.ch;
+								this.graphic.data[x][y].attr = this.attr.value;
+							}
+						}
+						break;
+					case 1:	// Erase to beginning of screen...
+						for (y = 0; y < this.pos.y; y++) {
+							for (x = 0; x<this.graphic.width; x++) {
+								this.graphic.data[x][y].ch = this.graphic.ch;
+								this.graphic.data[x][y].attr = this.attr.value;
+							}
+						}
+						for (x = 0; x<=this.pos.x; x++) {
+							this.graphic.data[x][this.pos.y].ch = this.graphic.ch;
+							this.graphic.data[x][this.pos.y].attr = this.attr.value;
+						}
+						break;
+					case 2:	// Erase entire screen (Most BBS terminals also move to 1/1)
+						this.graphic.clear();
+						break;
+				}
+				break;
+			case 'K':	// Erase in line
+				param_defaults(p, [0]);
+				switch(p[0]) {
+					case 0:	// Erase to eol
+						for (x = this.pos.x; x<this.pos.width; x++) {
+							this.graphic.data[x][this.pos.y].ch = this.graphic.ch;
+							this.graphic.data[x][this.pos.y].attr = this.attr.value;
+						}
+						break;
+					case 1:	// Erase to start of line
+						for (x = 0; x<=this.pos.x; x++) {
+							this.graphic.data[x][this.pos.y].ch = this.graphic.ch;
+							this.graphic.data[x][this.pos.y].attr = this.attr.value;
+						}
+						break;
+					case 2:	// Erase entire line
+						for (x = 0; x<this.graphic.width; x++) {
+							this.graphic.data[x][this.pos.y].ch = this.graphic.ch;
+							this.graphic.data[x][this.pos.y].attr = this.attr.value;
+						}
+						break;
+					default:
+						break;
+				}
+				break;
+			case 'P':	// Delete character
+				param_defaults(p, [1]);
+				if (p[1] > this.graphic.width - this.pos.x)
+					p[1] = this.graphic.width - this.pos.x;
+				if (this.pos.x < this.graphic.width-1) {
+					tg = this.graphic.get(this.pos.x + p[1], this.pos.y, this.graphic.width - 1, this.pos.y);
+					tg = this.graphic.put(this.pos.x, this.pos.y, (this.graphic.width - 1) - p[1], this.pos.y);
+				}
+				for (x = 0; x<p[1]; x++) {
+					this.graphic.data[(this.width - 1) - x][this.pos.y].ch = this.graphic.ch;
+					this.graphic.data[(this.width - 1) - x][this.pos.y].attr = this.attr.value;
+				}
+				break;
+			case 'X':	// Erase character
+				param_defaults(p, [1]);
+				if (p[1] > this.graphic.width - this.pos.x)
+					p[1] = this.graphic.width - this.pos.x;
+				for (x = 0; x<p[1]; x++) {
+					this.graphic.data[this.pos.x + x][this.pos.y].ch = this.graphic.ch;
+					this.graphic.data[this.pos.x + x][this.pos.y].attr = this.attr.value;
+				}
+				break;
+			case 'm':
+				param_defaults(p, [0]);
+				for (i=0; i<p.length; i++) {
+					switch(p[i]) {
+						case 0:
+							this.attr.value = this.graphic.attribute.value;
+							break;
+						case 1:
+							this.attr.bright = true;
+							break;
+						case 2:
+						case 22:
+							this.attr.bright = false;
+							break;
+						case 5:
+						case 6:
+							this.attr.blink = true;
+							break;
+						case 7:
+							tg = this.attr.bg;
+							this.attr.bg = this.attr.fg;
+							this.attr.fg = tg;
+							break;
+						case 8:
+							this.attr.fg = this.attr.bg;
+							break;
+						case 25:
+							this.attr.blink = false;
+							break;
+						case 30:
+							this.attr.fg = Attribute.BLACK;
+							break;
+						case 31:
+							this.attr.fg = Attribute.RED;
+							break;
+						case 32:
+							this.attr.fg = Attribute.GREEN;
+							break;
+						case 33:
+							this.attr.fg = Attribute.YELLOW;
+							break;
+						case 34:
+							this.attr.fg = Attribute.BLUE;
+							break;
+						case 35:
+							this.attr.fg = Attribute.MAGENTA;
+							break;
+						case 36:
+							this.attr.fg = Attribute.CYAN;
+							break;
+						case 37:
+							this.attr.fg = Attribute.WHITE;
+							break;
+						case 40:
+							this.attr.bg = Attribute.BLACK;
+							break;
+						case 41:
+							this.attr.bg = Attribute.RED;
+							break;
+						case 42:
+							this.attr.bg = Attribute.GREEN;
+							break;
+						case 43:
+							this.attr.bg = Attribute.YELLOW;
+							break;
+						case 44:
+							this.attr.bg = Attribute.BLUE;
+							break;
+						case 45:
+							this.attr.bg = Attribute.MAGENTA;
+							break;
+						case 46:
+							this.attr.bg = Attribute.CYAN;
+							break;
+						case 47:
+							this.attr.bg = Attribute.WHITE;
+							break;
+					}
+				}
+				break;
+			case 's':
+				this.saved_pos.x = this.pos.x;
+				this.saved_pos.y = this.pos.y;
+				break;
+			case 'u':
+				this.pos.x = this.saved_pos.x;
+				this.pos.y = this.saved_pos.y;
+				break;
+			// Still TODO...
+			case 'n':	// Device status report... no action from this object.
+			case 'Z':	// Back tabulate
+			case 'S':	// Scroll up
+			case 'T':	// Scroll down
+			case 'L':	// Insert line
+			case 'M':	// Delete line (also ANSI music!)
+			default:
+				log("Sent unsupported ANSI sequence '"+ext+pb+ib+fb+"' please let shurd@sasktel.net net know about this so it can be fixed.");
+		}
+	}
+	if ((m=str.match(/^(.*?)(\x1b(\[([<-\?]{0,1})([0-;]*)([ -\/]*)([@-~])?)?)$/)) !== null) {
+		str = m[1];
+		this.escbuf = m[2];
+	}
+	// Send regular chars before the sequence...
+	for (i=0; i<str.length; i++)
+		writech(this, str[i]);
+
+};
diff --git a/exec/load/dorkit.js b/exec/load/dorkit.js
index 9779c0cf48a1fba10a8bc6b50e6ae46ba7e9c9b1..10ecdd4d937bc5c6e2a011f4009d147ca361bcd9 100644
--- a/exec/load/dorkit.js
+++ b/exec/load/dorkit.js
@@ -1,8 +1,7 @@
 js.load_path_list.unshift(js.exec_dir+"/dorkit/");
 if (js.global.system !== undefined)
 	js.load_path_list.unshift(system.exec_dir+"/dorkit/");
-load("attribute.js");
-load("graphic.js");
+load("screen.js");
 
 var dk = {
 	console:{
@@ -73,14 +72,25 @@ var dk = {
 
 		x:1,					// Current column (1-based)
 		y:1,					// Current row (1-based)
-		attr:new Attribute(7),	// Current attribute
+		_attr:new Attribute(7),
+		get attr() {
+			return this._attr;
+		},
+		set attr(val) {
+			var n = new Attribute(val);
+			this.print(n.ansi(this._attr));
+			this._attr = n;
+		},
 		ansi:true,				// ANSI support is enabled
 		charset:'cp437',		// Supported character set
 		local:true,				// True if writes should go to the local screen
 		remote:true,			// True if writes should go to the remote terminal
-		rows:undefined,			// Rows in users terminal
-		cols:undefined,			// Columns in users terminal
+		rows:24,				// Rows in users terminal
+		cols:80,				// Columns in users terminal
+
 		keybuf:'',
+		local_screen:new Screen(80, 24, 7, ' '),
+		remote_screen:new Screen(80, 24, 7, ' '),
 
 		/*
 		 * Returns a string with ^A codes converted to ANSI or stripped
@@ -198,6 +208,7 @@ var dk = {
 		 * sets the current attribute to 7
 		 */
 		clear:function() {
+			this.attr=7;
 			if (this.local)
 				this.local_io.clear();
 			if (this.remote)
@@ -232,19 +243,29 @@ var dk = {
 		 * or undefined on error (ie: invalid block specified).
 		 */
 		getblock:function(sx,sy,ex,ey) {
+			return this.remote_screen.graphic.get(sx,sy,ex,ey);
 		},
 
 		/*
 		 * Writes a string unmodified.
-		 * TODO: This needs to parse ANSI and set attr...
 		 */
 		print:function(string) {
 			var m;
 
-			if (this.local)
+			if (this.local) {
+				if (this.local_screen !== undefined) {
+					this.local_screen.print(string);
+					this._attr.value = this.local_screen.attr.value;
+				}
 				this.local_io.print(string);
-			if (this.remote)
+			}
+			if (this.remote) {
+				if (this.remote_screen !== undefined) {
+					this.remote_screen.print(string);
+					this.attr.value = this.remote_screen.attr.value;
+				}
 				this.remote_io.print(string);
+			}
 		},
 
 		/*
@@ -258,14 +279,14 @@ var dk = {
 		 * Writes a string after parsing ^A codes.
 		 */
 		aprint:function(string) {
-			this.println(this.parse_ctrla(line));
+			this.println(this.parse_ctrla(line, this.attr));
 		},
 
 		/*
 		 * Writes a string after parsing ^A codes and appends a "\r\n".
 		 */
 		aprintln:function(line) {
-			this.println(this.parse_ctrla(line));
+			this.println(this.parse_ctrla(line, this.attr));
 		},
 
 		/*
@@ -455,6 +476,10 @@ var dk = {
 			if(this.console.waitkey(500)) {
 				if (this.console.getkey() == this.console.key.POSITION_REPORT) {
 					// TODO: Should we trust the drop file on number of rows?
+					if (this.console.cols != this.console.last_pos.x || this.console.rows != this.console.last_pos.y) {
+						this.console.remote_screen = new Screen(this.console.last_pos.x, this.console.last_pos.y, 7, ' ');
+						this.console.local_screen = new Screen(this.console.last_pos.x, this.console.last_pos.y, 7, ' ');
+					}
 					this.console.cols = this.console.last_pos.x;
 					this.console.rows = this.console.last_pos.y;
 					this.console.ansi = true;
@@ -468,6 +493,7 @@ var dk = {
 	parse_dropfile:function(path) {
 		var f = new File(path);
 		var df;
+		var rows;
 
 		if (!f.open("r"))
 			return false;
@@ -511,7 +537,12 @@ var dk = {
 				this.codepage = '7-bit';
 				break;
 		}
-		this.rows = parseInt(df[20], 10);
+		rows = parseInt(df[20], 10);
+		if (rows != this.console.rows) {
+			this.console.remote_screen = new Screen(this.console.cols, rows, 7, ' ');
+			this.console.local_screen = new Screen(this.console.cols, rows, 7, ' ');
+		}
+		this.rows = rows;
 		this.user.expert_mode = (df[21].toUpperCase === 'Y') ? true : false;
 		this.user.conference = df[22].split(/\s*,\s*/);
 		this.user.curr_conference = parseInt(df[23]);