Index: uspace/srv/console/console.c
===================================================================
--- uspace/srv/console/console.c	(revision 0a5116db604a87919e4c9037002491fc9ea6fdfe)
+++ uspace/srv/console/console.c	(revision dc033a109f5ba532e236382c55cb83e342bdfe06)
@@ -63,6 +63,5 @@
 int prev_console = 0;
 
-/** Information about framebuffer
- */
+/** Information about framebuffer */
 struct {
 	int phone;		/**< Framebuffer phone */
@@ -90,15 +89,11 @@
 						 * faster virtual console
 						 * switching */
-/** Size of fb_buf. */
-#define FB_BUF_SIZE 256
-
-/** Buffer for sending characters to FB driver. */
-static char fb_buf[FB_BUF_SIZE];
-
-/* Properties of fb_buf data. */
-static int fb_buf_row;		/**< Row where fb_buf data belong. */
-static int fb_buf_col;		/**< Column where fb_buf data start. */
-static int fb_console;		/**< VC to which fb_buf data belong. */
-int fb_bi = 0;			/**< Number of valid chars in fb_buf. */
+
+/** Information on row-span yet unsent to FB driver. */
+struct {
+	int row;		/**< Row where the span lies. */
+	int col;		/**< Leftmost column of the span. */
+	int n;			/**< Width of the span. */
+} fb_pending;
 
 /** Size of cwrite_buf. */
@@ -107,4 +102,6 @@
 /** Buffer for receiving data via the CONSOLE_WRITE call from the client. */
 static char cwrite_buf[CWRITE_BUF_SIZE];
+
+static void fb_putchar(char c, int row, int col);
 
 
@@ -176,70 +173,88 @@
 }
 
-/** Write a character vector to FB driver via IPC. */
-static ssize_t fb_write(const char *buf, size_t nbyte, int row, int col)
-{
-	ipcarg_t rc;
-	ipc_call_t answer;
-	aid_t req;
-
-	async_serialize_start();
-	
-	req = async_send_2(fb_info.phone, FB_WRITE, row, col, &answer);
-	rc = ipc_data_write_start(fb_info.phone, (void *) buf, nbyte);
-
-	if (rc != EOK) {
-		async_wait_for(req, NULL);
-		async_serialize_end();
-		return (ssize_t) rc;
-	}
-
-	async_wait_for(req, &rc);
-	async_serialize_end();
-
-	if (rc == EOK)
-		return (ssize_t) IPC_GET_ARG1(answer);
-	else
-		return -1;
-}
-
-/** Flush buffered characters to FB. */
-static void fb_buf_flush(void)
+/** Send an area of screenbuffer to the FB driver. */
+static void fb_update_area(connection_t *conn, int x, int y, int w, int h)
+{
+	int i, j;
+	int rc;
+	attrs_t *attrs;
+	keyfield_t *field;
+
+	if (interbuffer) {
+		for (j = 0; j < h; j++) {
+			for (i = 0; i < w; i++) {
+				interbuffer[i + j * w] =
+				    *get_field_at(&conn->screenbuffer,
+					x + i, y + j);
+			}
+		}
+
+		rc = async_req_4_0(fb_info.phone, FB_DRAW_TEXT_DATA,
+		    x, y, w, h);
+	} else {
+		rc = ENOTSUP;
+	}
+
+	if (rc != 0) {
+		attrs = &conn->screenbuffer.attrs;
+
+		for (j = 0; j < h; j++) {
+			for (i = 0; i < w; i++) {
+				field = get_field_at(&conn->screenbuffer,
+				    x + i, y + j);
+				if (!attrs_same(*attrs, field->attrs))
+					set_attrs(&field->attrs);
+				attrs = &field->attrs;
+
+				fb_putchar(field->character, y + j, x + i);
+			}
+		}
+	}
+}
+
+/** Flush pending cells to FB. */
+static void fb_pending_flush(void)
 {
 	screenbuffer_t *scr;
-	int i;
-
-	scr = &(connections[fb_console].screenbuffer);
-
-	if (fb_bi > 0) {
-		if (fb_write(fb_buf, fb_bi, fb_buf_row, fb_buf_col) < 0) {
-			/* Try falling back to char-by-char. */
-			for (i = 0; i < fb_bi; i++) {
-				async_msg_3(fb_info.phone, FB_PUTCHAR, fb_buf[i],
-				    fb_buf_row, fb_buf_col + i);
-			}
-		}
-		fb_bi = 0;
-	}
-}
+
+	scr = &(connections[active_console].screenbuffer);
+
+	if (fb_pending.n > 0) {
+		fb_update_area(&connections[active_console], fb_pending.col,
+		    fb_pending.row, fb_pending.n, 1);
+		fb_pending.n = 0;
+	}
+}
+
+/** Mark a character cell as changed.
+ *
+ * This adds the cell to the pending rowspan if possible. Otherwise
+ * the old span is flushed first.
+ */
+static void cell_mark_changed(int row, int col)
+{
+	if (fb_pending.n != 0) {
+		if (row != fb_pending.row ||
+		    col != fb_pending.col + fb_pending.n) {
+			fb_pending_flush();
+		}
+	}
+
+	if (fb_pending.n == 0) {
+		fb_pending.row = row;
+		fb_pending.col = col;
+	}
+
+	++fb_pending.n;
+}
+
 
 /** Print a character to the active VC with buffering. */
-static void prtchr(char c, int row, int col)
-{
-	if (fb_bi >= FB_BUF_SIZE)
-		fb_buf_flush();
-
-	if (fb_bi == 0) {
-		fb_buf_row = row;
-		fb_buf_col = col;
-		fb_console = active_console;
-	}
-
-	fb_buf[fb_bi++] = c;
-}
-
-/** Check key and process special keys. 
- *
- *
- */
+static void fb_putchar(char c, int row, int col)
+{
+	async_msg_3(fb_info.phone, FB_PUTCHAR, c, row, col);
+}
+
+/** Process a character from the client (TTY emulation). */
 static void write_char(int console, char key)
 {
@@ -249,5 +264,5 @@
 	switch (key) {
 	case '\n':
-		fb_buf_flush();
+		fb_pending_flush();
 		flush_cursor = true;
 		scr->position_y++;
@@ -255,23 +270,20 @@
 		break;
 	case '\r':
-		fb_buf_flush();
 		break;
 	case '\t':
-		fb_buf_flush();
 		scr->position_x += 8;
 		scr->position_x -= scr->position_x % 8; 
 		break;
 	case '\b':
-		fb_buf_flush();
 		if (scr->position_x == 0) 
 			break;
 		scr->position_x--;
 		if (console == active_console)
-			prtchr(' ', scr->position_y, scr->position_x);
+			cell_mark_changed(scr->position_y, scr->position_x);
 		screenbuffer_putchar(scr, ' ');
 		break;
 	default:	
 		if (console == active_console)
-			prtchr(key, scr->position_y, scr->position_x);
+			cell_mark_changed(scr->position_y, scr->position_x);
 
 		screenbuffer_putchar(scr, key);
@@ -280,5 +292,4 @@
 
 	if (scr->position_x >= scr->size_x) {
-		fb_buf_flush();
 		flush_cursor = true;
 		scr->position_y++;
@@ -286,4 +297,5 @@
 	
 	if (scr->position_y >= scr->size_y) {
+		fb_pending_flush();
 		scr->position_y = scr->size_y - 1;
 		screenbuffer_clear_line(scr, scr->top_line);
@@ -310,5 +322,5 @@
 		return;
 
-	fb_buf_flush();
+	fb_pending_flush();
 
 	if (newcons == KERNEL_CONSOLE) {
@@ -338,14 +350,17 @@
 		curs_visibility(false);
 		if (interbuffer) {
-			for (i = 0; i < conn->screenbuffer.size_x; i++)
-				for (j = 0; j < conn->screenbuffer.size_y; j++) {
+			for (j = 0; j < conn->screenbuffer.size_y; j++) {
+				for (i = 0; i < conn->screenbuffer.size_x; i++) {
 					unsigned int size_x;
 					
 					size_x = conn->screenbuffer.size_x; 
-					interbuffer[i + j * size_x] =
+					interbuffer[j * size_x + i] =
 					    *get_field_at(&conn->screenbuffer, i, j);
 				}
+			}
 			/* This call can preempt, but we are already at the end */
-			rc = async_req_0_0(fb_info.phone, FB_DRAW_TEXT_DATA);
+			rc = async_req_4_0(fb_info.phone, FB_DRAW_TEXT_DATA,
+			    0, 0, conn->screenbuffer.size_x,
+			    conn->screenbuffer.size_y);
 		}
 		
@@ -366,5 +381,5 @@
 						continue;
 
-					prtchr(field->character, j, i);
+					fb_putchar(field->character, j, i);
 				}
 		}
@@ -479,4 +494,5 @@
 	ipcarg_t arg1, arg2, arg3, arg4;
 	connection_t *conn;
+	screenbuffer_t *scr;
 	
 	if ((consnum = find_free_connection()) == -1) {
@@ -535,5 +551,4 @@
 			break;
 		case CONSOLE_GOTO:
-			fb_buf_flush();
 			screenbuffer_goto(&conn->screenbuffer,
 			    IPC_GET_ARG2(call), IPC_GET_ARG1(call));
@@ -547,10 +562,14 @@
 			break;
 		case CONSOLE_FLUSH:
-			fb_buf_flush();
-			if (consnum == active_console)
+			fb_pending_flush();
+			if (consnum == active_console) {
 				async_req_0_0(fb_info.phone, FB_FLUSH);
+
+				scr = &(connections[consnum].screenbuffer);
+				curs_goto(scr->position_y, scr->position_x);
+			}
 			break;
 		case CONSOLE_SET_STYLE:
-			fb_buf_flush();
+			fb_pending_flush();
 			arg1 = IPC_GET_ARG1(call);
 			screenbuffer_set_style(&conn->screenbuffer, arg1);
@@ -559,5 +578,5 @@
 			break;
 		case CONSOLE_SET_COLOR:
-			fb_buf_flush();
+			fb_pending_flush();
 			arg1 = IPC_GET_ARG1(call);
 			arg2 = IPC_GET_ARG2(call);
@@ -569,5 +588,5 @@
 			break;
 		case CONSOLE_SET_RGB_COLOR:
-			fb_buf_flush();
+			fb_pending_flush();
 			arg1 = IPC_GET_ARG1(call);
 			arg2 = IPC_GET_ARG2(call);
@@ -578,5 +597,5 @@
 			break;
 		case CONSOLE_CURSOR_VISIBILITY:
-			fb_buf_flush();
+			fb_pending_flush();
 			arg1 = IPC_GET_ARG1(call);
 			conn->screenbuffer.is_cursor_visible = arg1;
@@ -684,4 +703,6 @@
 	interbuffer = as_get_mappable_page(ib_size);
 
+	fb_pending.n = 0;
+
 	if (as_area_create(interbuffer, ib_size, AS_AREA_READ |
 	    AS_AREA_WRITE | AS_AREA_CACHEABLE) != interbuffer) {
Index: uspace/srv/fb/ega.c
===================================================================
--- uspace/srv/fb/ega.c	(revision 0a5116db604a87919e4c9037002491fc9ea6fdfe)
+++ uspace/srv/fb/ega.c	(revision dc033a109f5ba532e236382c55cb83e342bdfe06)
@@ -79,5 +79,5 @@
 static unsigned int scr_width;
 static unsigned int scr_height;
-static char *scr_addr;
+static uint8_t *scr_addr;
 
 static unsigned int style;
@@ -152,11 +152,28 @@
 }
 
-static void draw_text_data(keyfield_t *data)
-{
-	int i;
-
-	for (i = 0; i < scr_width * scr_height; i++) {
-		scr_addr[i * 2] = data[i].character;
-		scr_addr[i * 2 + 1] = attr_to_ega_style(&data[i].attrs);
+/** Draw text data to viewport.
+ *
+ * @param vport Viewport id
+ * @param data  Text data.
+ * @param x	Leftmost column of the area.
+ * @param y	Topmost row of the area.
+ * @param w	Number of rows.
+ * @param h	Number of columns.
+ */
+static void draw_text_data(keyfield_t *data, unsigned int x,
+    unsigned int y, unsigned int w, unsigned int h)
+{
+	unsigned int i, j;
+	keyfield_t *field;
+	uint8_t *dp;
+
+	for (j = 0; j < h; j++) {
+		for (i = 0; i < w; i++) {
+			field = &data[j * w + i];
+			dp = &scr_addr[2 * ((y + j) * scr_width + (x + i))];
+
+			dp[0] = field->character;
+			dp[1] = attr_to_ega_style(&field->attrs);
+		}
 	}
 }
@@ -232,43 +249,4 @@
 }
 
-#define FB_WRITE_BUF_SIZE 256
-static char fb_write_buf[FB_WRITE_BUF_SIZE];
-
-static void fb_write(ipc_callid_t rid, ipc_call_t *request)
-{
-	int row, col;
-	ipc_callid_t callid;
-	size_t len;
-	size_t i;
-
-	row = IPC_GET_ARG1(*request);
-	col = IPC_GET_ARG2(*request);
-
-	if ((col >= scr_width) || (row >= scr_height)) {
-		ipc_answer_0(callid, EINVAL);
-		ipc_answer_0(rid, EINVAL);
-		return;
-	}
-
-	if (!ipc_data_write_receive(&callid, &len)) {
-		ipc_answer_0(callid, EINVAL);
-		ipc_answer_0(rid, EINVAL);
-		return;
-	}
-
-	if (len > FB_WRITE_BUF_SIZE)
-		len = FB_WRITE_BUF_SIZE;
-	if (len >= scr_width - col)
-		len = scr_width - col;
-
-	(void) ipc_data_write_finalize(callid, fb_write_buf, len);
-
-	for (i = 0; i < len; i++) {
-		printchar(fb_write_buf[i], row, col++);
-	}
-
-	ipc_answer_1(rid, EOK, len);
-}
-
 static void ega_client_connection(ipc_callid_t iid, ipc_call_t *icall)
 {
@@ -277,5 +255,5 @@
 	ipc_call_t call;
 	char c;
-	unsigned int row, col;
+	unsigned int row, col, w, h;
 	int bg_color, fg_color, attr;
 	uint32_t bg_rgb, fg_rgb;
@@ -310,9 +288,17 @@
 			break;
 		case FB_DRAW_TEXT_DATA:
+			col = IPC_GET_ARG1(call);
+			row = IPC_GET_ARG2(call);
+			w = IPC_GET_ARG3(call);
+			h = IPC_GET_ARG4(call);
 			if (!interbuf) {
 				retval = EINVAL;
 				break;
 			}
-			draw_text_data(interbuf);
+			if (col + w > scr_width || row + h > scr_height) {
+				retval = EINVAL;
+				break;
+			}
+			draw_text_data(interbuf, col, row, w, h);
 			retval = 0;
 			break;
@@ -335,9 +321,4 @@
 			retval = 0;
 			break;
-		case FB_WRITE:
-			fb_write(callid, &call);
-
-			/* Message already answered */
-			continue;
  		case FB_CURSOR_GOTO:
 			row = IPC_GET_ARG1(call);
Index: uspace/srv/fb/fb.c
===================================================================
--- uspace/srv/fb/fb.c	(revision 0a5116db604a87919e4c9037002491fc9ea6fdfe)
+++ uspace/srv/fb/fb.c	(revision dc033a109f5ba532e236382c55cb83e342bdfe06)
@@ -867,33 +867,38 @@
 }
 
-
-/** Draw text data to viewport
+/** Draw text data to viewport.
  *
  * @param vport Viewport id
- * @param data  Text data fitting exactly into viewport
- *
- */
-static void draw_text_data(viewport_t *vport, keyfield_t *data)
-{
-	unsigned int i;
+ * @param data  Text data.
+ * @param x	Leftmost column of the area.
+ * @param y	Topmost row of the area.
+ * @param w	Number of rows.
+ * @param h	Number of columns.
+ */
+static void draw_text_data(viewport_t *vport, keyfield_t *data, unsigned int x,
+    unsigned int y, unsigned int w, unsigned int h)
+{
+	unsigned int i, j;
 	bb_cell_t *bbp;
 	attrs_t *a;
 	attr_rgb_t rgb;
-	
-	for (i = 0; i < vport->cols * vport->rows; i++) {
-		unsigned int col = i % vport->cols;
-		unsigned int row = i / vport->cols;
-		
-		bbp = &vport->backbuf[BB_POS(vport, col, row)];
-		uint8_t glyph = bbp->glyph;
-
-		a = &data[i].attrs;
-		rgb_from_attr(&rgb, a);
-
-		bbp->glyph = data[i].character;
-		bbp->fg_color = rgb.fg_color;
-		bbp->bg_color = rgb.bg_color;
-
-		draw_vp_glyph(vport, false, col, row);
+
+	for (j = 0; j < h; j++) {
+		for (i = 0; i < w; i++) {
+			unsigned int col = x + i;
+			unsigned int row = y + j;
+
+			bbp = &vport->backbuf[BB_POS(vport, col, row)];
+			uint8_t glyph = bbp->glyph;
+
+			a = &data[j * w + i].attrs;
+			rgb_from_attr(&rgb, a);
+
+			bbp->glyph = data[j * w + i].character;
+			bbp->fg_color = rgb.fg_color;
+			bbp->bg_color = rgb.bg_color;
+
+			draw_vp_glyph(vport, false, col, row);
+		}
 	}
 	cursor_show(vport);
@@ -999,4 +1004,6 @@
 	unsigned int x;
 	unsigned int y;
+	unsigned int w;
+	unsigned int h;
 	
 	switch (IPC_GET_METHOD(*call)) {
@@ -1059,13 +1066,21 @@
 		break;
 	case FB_DRAW_TEXT_DATA:
+		x = IPC_GET_ARG1(*call);
+		y = IPC_GET_ARG2(*call);
+		w = IPC_GET_ARG3(*call);
+		h = IPC_GET_ARG4(*call);
 		if (!interbuffer) {
 			retval = EINVAL;
 			break;
 		}
-		if (intersize < vport->cols * vport->rows * sizeof(*interbuffer)) {
+		if (x + w > vport->cols || y + h > vport->rows) {
 			retval = EINVAL;
 			break;
 		}
-		draw_text_data(vport, interbuffer);
+		if (intersize < w * h * sizeof(*interbuffer)) {
+			retval = EINVAL;
+			break;
+		}
+		draw_text_data(vport, interbuffer, x, y, w, h);
 		break;
 	default:
@@ -1472,44 +1487,4 @@
 	return rgb_from_idx(&vport->attr, fg_color, bg_color, flags);
 }
-
-#define FB_WRITE_BUF_SIZE 256
-static char fb_write_buf[FB_WRITE_BUF_SIZE];
-
-static void fb_write(viewport_t *vport, ipc_callid_t rid, ipc_call_t *request)
-{
-	int row, col;
-	ipc_callid_t callid;
-	size_t len;
-	size_t i;
-
-	row = IPC_GET_ARG1(*request);
-	col = IPC_GET_ARG2(*request);
-
-	if ((col >= vport->cols) || (row >= vport->rows)) {
-		ipc_answer_0(callid, EINVAL);
-		ipc_answer_0(rid, EINVAL);
-		return;
-	}
-
-	if (!ipc_data_write_receive(&callid, &len)) {
-		ipc_answer_0(callid, EINVAL);
-		ipc_answer_0(rid, EINVAL);
-		return;
-	}
-
-	if (len > FB_WRITE_BUF_SIZE)
-		len = FB_WRITE_BUF_SIZE;
-	if (len >= vport->cols - col)
-		len = vport->cols - col;
-
-	(void) ipc_data_write_finalize(callid, fb_write_buf, len);
-
-	for (i = 0; i < len; i++) {
-		draw_char(vport, fb_write_buf[i], col++, row);
-	}
-
-	ipc_answer_1(rid, EOK, len);
-}
-
 
 /** Function for handling connections to FB
@@ -1587,9 +1562,4 @@
 			/* Message already answered */
 			continue;
-		case FB_WRITE:
-			fb_write(vport, callid, &call);
-			
-			/* Message already answered */
-			continue;
 		case FB_CLEAR:
 			vport_clear(vport);
Index: uspace/srv/fb/msim.c
===================================================================
--- uspace/srv/fb/msim.c	(revision 0a5116db604a87919e4c9037002491fc9ea6fdfe)
+++ uspace/srv/fb/msim.c	(revision dc033a109f5ba532e236382c55cb83e342bdfe06)
@@ -46,5 +46,5 @@
 
 #define WIDTH 80
-#define HEIGHT 25
+#define HEIGHT 24
 
 static char *virt_addr;
Index: uspace/srv/fb/serial_console.c
===================================================================
--- uspace/srv/fb/serial_console.c	(revision 0a5116db604a87919e4c9037002491fc9ea6fdfe)
+++ uspace/srv/fb/serial_console.c	(revision dc033a109f5ba532e236382c55cb83e342bdfe06)
@@ -224,19 +224,38 @@
 }
 
-static void draw_text_data(keyfield_t *data)
-{
-	int i, j;
+/** Draw text data to viewport.
+ *
+ * @param vport Viewport id
+ * @param data  Text data.
+ * @param x	Leftmost column of the area.
+ * @param y	Topmost row of the area.
+ * @param w	Number of rows.
+ * @param h	Number of columns.
+ */
+static void draw_text_data(keyfield_t *data, unsigned int x,
+    unsigned int y, unsigned int w, unsigned int h)
+{
+	unsigned int i, j;
+	keyfield_t *field;
 	attrs_t *a0, *a1;
 
-	serial_goto(0, 0);
+	serial_goto(y, x);
 	a0 = &data[0].attrs;
 	serial_set_attrs(a0);
 
-	for (i = 0; i < scr_height; i++) {
-		for (j = 0; j < scr_width; j++) {
-			a1 = &data[i * scr_width + j].attrs;
+	for (j = 0; j < h; j++) {
+		if (j > 0 && w != scr_width)
+			serial_goto(y, x);
+
+		for (i = 0; i < w; i++) {
+			unsigned int col = x + i;
+			unsigned int row = y + j;
+
+			field = &data[j * w + i];
+
+			a1 = &field->attrs;
 			if (!attrs_same(*a0, *a1))
 				serial_set_attrs(a1);
-			(*putc_function)(data[i * scr_width + j].character);
+			(*putc_function)(field->character);
 			a0 = a1;
 		}
@@ -246,49 +265,4 @@
 int lastcol = 0;
 int lastrow = 0;
-
-#define FB_WRITE_BUF_SIZE 256
-static char fb_write_buf[FB_WRITE_BUF_SIZE];
-
-static void fb_write(ipc_callid_t rid, ipc_call_t *request)
-{
-	int row, col;
-	ipc_callid_t callid;
-	size_t len;
-	size_t i;
-
-	row = IPC_GET_ARG1(*request);
-	col = IPC_GET_ARG2(*request);
-
-	if ((col >= scr_width) || (row >= scr_height)) {
-		ipc_answer_0(callid, EINVAL);
-		ipc_answer_0(rid, EINVAL);
-		return;
-	}
-
-	if (!ipc_data_write_receive(&callid, &len)) {
-		ipc_answer_0(callid, EINVAL);
-		ipc_answer_0(rid, EINVAL);
-		return;
-	}
-
-	if (len > FB_WRITE_BUF_SIZE)
-		len = FB_WRITE_BUF_SIZE;
-	if (len >= scr_width - col)
-		len = scr_width - col;
-
-	(void) ipc_data_write_finalize(callid, fb_write_buf, len);
-
-	if ((lastcol != col) || (lastrow != row))
-		serial_goto(row, col);
-
-	for (i = 0; i < len; i++) {
-		(*putc_function)(fb_write_buf[i]);
-	}
-
-	lastcol = col + len;
-	lastrow = row;
-
-	ipc_answer_1(rid, EOK, len);
-}
 
 /**
@@ -304,6 +278,5 @@
 
 	char c;
-	int newcol;
-	int newrow;
+	int col, row, w, h;
 	int fgcolor;
 	int bgcolor;
@@ -346,33 +319,38 @@
 			break;
 		case FB_DRAW_TEXT_DATA:
+			col = IPC_GET_ARG1(call);
+			row = IPC_GET_ARG2(call);
+			w = IPC_GET_ARG3(call);
+			h = IPC_GET_ARG4(call);
 			if (!interbuf) {
 				retval = EINVAL;
 				break;
 			}
-			draw_text_data(interbuf);
+			if (col + w > scr_width || row + h > scr_height) {
+				retval = EINVAL;
+				break;
+			}
+			draw_text_data(interbuf, col, row, w, h);
+			lastrow = row + h - 1;
+			lastcol = col + w;
 			retval = 0;
 			break;
 		case FB_PUTCHAR:
 			c = IPC_GET_ARG1(call);
-			newrow = IPC_GET_ARG2(call);
-			newcol = IPC_GET_ARG3(call);
-			if ((lastcol != newcol) || (lastrow != newrow))
-				serial_goto(newrow, newcol);
-			lastcol = newcol + 1;
-			lastrow = newrow;
+			row = IPC_GET_ARG2(call);
+			col = IPC_GET_ARG3(call);
+			if ((lastcol != col) || (lastrow != row))
+				serial_goto(row, col);
+			lastcol = col + 1;
+			lastrow = row;
 			(*putc_function)(c);
 			retval = 0;
 			break;
-		case FB_WRITE:
-			fb_write(callid, &call);
-			
-			/* Message already answered */
-			continue;
 		case FB_CURSOR_GOTO:
-			newrow = IPC_GET_ARG1(call);
-			newcol = IPC_GET_ARG2(call);
-			serial_goto(newrow, newcol);
-			lastrow = newrow;
-			lastcol = newcol;
+			row = IPC_GET_ARG1(call);
+			col = IPC_GET_ARG2(call);
+			serial_goto(row, col);
+			lastrow = row;
+			lastcol = col;
 			retval = 0;
 			break;
Index: uspace/srv/fb/ski.c
===================================================================
--- uspace/srv/fb/ski.c	(revision 0a5116db604a87919e4c9037002491fc9ea6fdfe)
+++ uspace/srv/fb/ski.c	(revision dc033a109f5ba532e236382c55cb83e342bdfe06)
@@ -48,5 +48,5 @@
 
 #define WIDTH 80
-#define HEIGHT 25
+#define HEIGHT 24
 
 /** Display character on ski debug console
