+#include <string.h>
+#include "con.h"
+#include "vga.h"
+
+#define NCOLS 80
+#define NROWS 25
+
+/* must be pow2 */
+#define TEXTBUF_SIZE 256
+
+static char textbuf[TEXTBUF_SIZE][NCOLS];
+static int tbuf_first, tbuf_last;
+
+static int curx, cury, scroll;
+
+
+static void newline(void);
+
+
+void con_init(void)
+{
+ con_reset();
+}
+
+void con_reset(void)
+{
+ vga_reset();
+
+ curx = cury = scroll = 0;
+ tbuf_first = tbuf_last = 0;
+ memset(textbuf[0], 0, sizeof textbuf[0]);
+}
+
+void con_putchar(int c)
+{
+ switch(c) {
+ case '\t':
+ curx = (curx + 8) & 0xfffffff8;
+ if(curx >= NCOLS) {
+ newline();
+ }
+ break;
+
+ case '\r':
+ curx = 0;
+ break;
+
+ case '\n':
+ newline();
+ break;
+
+ case '\b':
+ if(curx > 0) {
+ textbuf[tbuf_last][--curx] = 0;
+ vga_drawchar(curx, cury, 0);
+ }
+ break;
+
+ default:
+ textbuf[tbuf_last][curx] = c;
+ vga_drawchar(curx, cury, c);
+ if(++curx >= NCOLS) {
+ newline();
+ }
+ break;
+ }
+
+ if(cury >= NROWS) {
+ cury--;
+ vga_scroll(++scroll);
+ vga_clearline(NROWS - 1);
+ }
+
+ vga_setcursor(curx, cury);
+}
+
+static void newline(void)
+{
+ int num;
+
+ curx = 0;
+ cury++;
+
+ num = (tbuf_last + 1) & (TEXTBUF_SIZE - 1);
+
+ if(tbuf_last == tbuf_first) {
+ tbuf_first = num;
+ }
+ tbuf_last = num;
+}