diff --git a/Nasal/screen.nas b/Nasal/screen.nas
new file mode 100644
index 000000000..d2b4a1254
--- /dev/null
+++ b/Nasal/screen.nas
@@ -0,0 +1,149 @@
+# $Id$
+#
+# screen log window
+# =================
+#
+#
+# simple use:
+#
+#     foo = screen.window.new()
+#     foo.write("message in the middle of the screen");
+#
+#
+# advanced use:
+#
+#     bar = screen.window.new(nil, -100, 3, 10);
+#     bar.fg = [1, 1, 1, 1];    # choose white color
+#     bar.align = "left";
+#
+#     bar.write("first line");
+#     bar.write("second line (red)", 1, 0, 0);
+#     bar.write("third line");
+#
+#
+#
+# arguments:
+#            x ... x coordinate
+#            y ... y coordinate
+#                  positive coords position relative to the left/lower corner,
+#                  negative coords from the right/upper corner, nil centers
+#     maxlines ... max number of displayed lines; if more are pushed into the
+#                  screen, then the ones on top fall off
+#   autoscroll ... seconds that each line should be shown; can be less if
+#                  a message falls off; if 0 then don't scroll at all
+#
+
+dialog_id = 0;
+
+window = {
+	new : func(x = nil, y = nil, maxlines = 10, autoscroll = 10) {
+		m = { parents : [window] };
+		#
+		# "public"
+		m.x = x;
+		m.y = y;
+		m.maxlines = maxlines;
+		m.autoscroll = autoscroll;	# display time in seconds
+		m.font = "SANS_12B";
+		m.bg = [0, 0, 0, 0];		# background color
+		m.fg = [1, 0.5, 0, 1];		# default foreground color
+		m.align = nil;			# "left", "right", "center" (default)
+		#
+		# "private"
+		m.name = "__screen_window_" ~ (dialog_id += 1) ~ "__";
+		m.lines = [];
+		m.skiptimer = 0;
+		m.dialog = nil;
+		m.namenode = props.Node.new({ "dialog-name" : m.name });
+		setlistener("/sim/startup/xsize", func { m._redraw_() });
+		setlistener("/sim/startup/ysize", func { m._redraw_() });
+		return m;
+	},
+
+	write : func(msg, r = nil, g = nil, b = nil, a = nil) {
+		if (me.namenode == nil) { return }
+		if (r == nil) { r = me.fg[0] }
+		if (g == nil) { g = me.fg[1] }
+		if (b == nil) { b = me.fg[2] }
+		if (a == nil) { a = me.fg[3] }
+		append(me.lines, [msg, r, g, b, a]);
+		if (size(me.lines) > me.maxlines) {
+			me.lines = subvec(me.lines, 1);
+			if (me.autoscroll) {
+				me.skiptimer += 1;
+			}
+		}
+		me._show_();
+		if (me.autoscroll) {
+			settimer(func { me._timeout_() }, me.autoscroll, 1);
+		}
+	},
+
+	_show_ : func {
+		fgcommand("dialog-close", me.namenode);
+		if (me.dialog != nil) {
+			me.x = me.dialog.prop().getNode("lastx").getValue();
+			me.y = me.dialog.prop().getNode("lasty").getValue();
+		}
+
+		me.dialog = gui.Widget.new();
+		me.dialog.set("name", me.name);
+		if (me.x != nil) { me.dialog.set("x", me.x) }
+		if (me.y != nil) { me.dialog.set("y", me.y) }
+		me.dialog.set("layout", "vbox");
+		me.dialog.set("default-padding", 2);
+		me.dialog.setFont(me.font);
+		me.dialog.setColor(me.bg[0], me.bg[1], me.bg[2], me.bg[3]);
+
+		for (i = 0; i < me.maxlines; i += 1) {
+			var w = me.dialog.addChild("text");
+			if (i < size(me.lines)) {
+				w.set("label", me.lines[i][0]);
+				w.setColor(me.lines[i][1], me.lines[i][2],
+						me.lines[i][3], me.lines[i][4]);
+				if (me.align != nil) { w.set("halign", me.align) }
+			} else {
+				w.set("label", "");
+			}
+		}
+
+		fgcommand("dialog-new", me.dialog.prop());
+		fgcommand("dialog-show", me.namenode);
+	},
+
+	_timeout_ : func {
+		if (me.skiptimer > 0) {
+			me.skiptimer -= 1;
+			return;
+		}
+		if (size(me.lines) > 1) {
+			me.lines = subvec(me.lines, 1);
+			me._show_();
+		} else {
+			fgcommand("dialog-close", me.namenode);
+			dialog = nil;
+			me.lines = [];
+		}
+	},
+
+	_redraw_ : func {
+		if (me.dialog != nil) {
+			fgcommand("dialog-close", me.namenode);
+			me.x = me.dialog.prop().getNode("lastx").getValue();
+			me.y = me.dialog.prop().getNode("lasty").getValue();
+			me._show_();
+		}
+	},
+};
+
+
+
+# open one window for general use:   screen.log.write("message");
+#
+log = nil;
+
+INIT = func {
+	log = window.new(nil, -40, 10, 10);
+}
+
+settimer(INIT, 0);