+
+ intr_set_level (old_level);
+}
+\f
+/* Clears the screen and moves the cursor to the upper left. */
+static void
+cls (void)
+{
+ size_t y;
+
+ for (y = 0; y < ROW_CNT; y++)
+ clear_row (y);
+
+ cx = cy = 0;
+ move_cursor ();
+}
+
+/* Clears row Y to spaces. */
+static void
+clear_row (size_t y)
+{
+ size_t x;
+
+ for (x = 0; x < COL_CNT; x++)
+ {
+ fb[y][x][0] = ' ';
+ fb[y][x][1] = GRAY_ON_BLACK;
+ }
+}
+
+/* Advances the cursor to the first column in the next line on
+ the screen. If the cursor is already on the last line on the
+ screen, scrolls the screen upward one line. */
+static void
+newline (void)
+{
+ cx = 0;
+ cy++;
+ if (cy >= ROW_CNT)
+ {
+ cy = ROW_CNT - 1;
+ memmove (&fb[0], &fb[1], sizeof fb[0] * (ROW_CNT - 1));
+ clear_row (ROW_CNT - 1);
+ }
+}
+
+/* Moves the hardware cursor to (cx,cy). */
+static void
+move_cursor (void)
+{
+ /* See [FREEVGA] under "Manipulating the Text-mode Cursor". */
+ uint16_t cp = cx + COL_CNT * cy;
+ outw (0x3d4, 0x0e | (cp & 0xff00));
+ outw (0x3d4, 0x0f | (cp << 8));
+}
+
+/* Reads the current hardware cursor position into (*X,*Y). */
+static void
+find_cursor (size_t *x, size_t *y)
+{
+ /* See [FREEVGA] under "Manipulating the Text-mode Cursor". */
+ uint16_t cp;
+
+ outb (0x3d4, 0x0e);
+ cp = inb (0x3d5) << 8;
+
+ outb (0x3d4, 0x0f);
+ cp |= inb (0x3d5);
+
+ *x = cp % COL_CNT;
+ *y = cp / COL_CNT;