#include "type.h" #include "stdio.h" #include "const.h" #include "protect.h" #include "string.h" #include "proc.h" #include "tty.h" #include "console.h" #include "global.h" #include "keyboard.h" #include "proto.h" #include "x86.h" #include "memman.h" #include "assert.h" /***************************************************************************** * Low level vga driver *****************************************************************************/ /***************************************************************************** * Display the cursor by setting CRTC (6845 compatible) registers. * * @param position Position of the cursor based on the beginning of the video * memory. Note that it counts in WORDs, not in BYTEs. *****************************************************************************/ static inline void vga_set_cursor(unsigned int position) { disable_int(); outb(CRTC_ADDR_REG, CURSOR_H); outb(CRTC_DATA_REG, (position >> 8) & 0xFF); outb(CRTC_ADDR_REG, CURSOR_L); outb(CRTC_DATA_REG, position & 0xFF); enable_int(); } static inline void vga_disable_cursor() { outb(CRTC_ADDR_REG, 0x0A); outb(CRTC_DATA_REG, 0x20); } static inline void vga_enable_cursor(u8 cursor_start, u8 cursor_end) { outb(0x3D4, 0x0A); outb(0x3D5, (inb(0x3D5) & 0xC0) | cursor_start); outb(0x3D4, 0x0B); outb(0x3D5, (inb(0x3D5) & 0xE0) | cursor_end); } /***************************************************************************** * Routine for hardware screen scrolling. * * @param addr Offset in the video memory. *****************************************************************************/ static inline void vga_set_video_start_addr(u32 addr) { disable_int(); outb(CRTC_ADDR_REG, START_ADDR_H); outb(CRTC_DATA_REG, (addr >> 8) & 0xFF); outb(CRTC_ADDR_REG, START_ADDR_L); outb(CRTC_DATA_REG, addr & 0xFF); enable_int(); } /***************************************************************************** * Write data directly to Video Memory cell * * @param pos text mode position(pos*2 yourself) * @param dat data to be written, with format [ BG | FG | ASCII ] *****************************************************************************/ static inline void vga_put_raw(u32 pos, u16 dat) { u16 *pch = (u16 *)K_PHY2LIN(V_MEM_BASE + pos); *pch = dat; } /***************************************************************************** * copy a whole screen of text mode data into video memory, assume that screen * width is 80 or 40 * * @param src memory block with the same size as text mode video memory() *****************************************************************************/ static inline void vga_flush_screen(void *src) { u32 *_src = src; u32 *dst = (u32 *)K_PHY2LIN(V_MEM_BASE); for (int i = 0; i < SCR_SIZE * sizeof(u16) / sizeof(u32); ++i) { *dst++ = *_src++; } } /***************************************************************************** * copy a whole screen of text mode data into video memory, assume that screen * width is 80 or 40 * * @param src memory block with the same size as text mode video memory() *****************************************************************************/ static inline void vga_flush_line(void *src, int line_no) { u32 *_src = src; u32 *dst = (u32 *)K_PHY2LIN(V_MEM_BASE + line_no * SCR_WIDTH * 2); for (int i = 0; i < SCR_WIDTH * sizeof(u16) / sizeof(u32); ++i) { *dst++ = *_src++; } } static inline void vga_flush_blankline(int line_no) { u32 *dst = (u32 *)K_PHY2LIN(V_MEM_BASE + line_no * SCR_WIDTH * 2); for (int i = 0; i < SCR_WIDTH * sizeof(u16) / sizeof(u32); ++i) { *dst++ = (BLANK << 16) | BLANK; } } /***************************************************************************** * tty - vga driver *****************************************************************************/ static u16 pagebuf[3][SCR_BUFSIZE]; void vga_tty_init(NTTY *tty) { static int _cnt = 0; assert(tty->driver_type == 1); assert(tty->output_buf); vga_buf *vga = tty->output_buf; vga->buf = (void *)K_PHY2LIN(do_kmalloc(sizeof(u16) * SCR_BUFSIZE)); // vga->buf = (void *)pagebuf[_cnt++]; // kprintf("malloced %p %p %p\n", vga->buf, &vga->buf, &vga->scr_top_line); vga->cur_col = vga->cur_row = 0; // buf->max_line = SCR_BUFSIZE / SCR_WIDTH; vga->scr_top_line = vga->scr_cur_line = vga->scr_bot_line = 0; vga->head_line = 0; u32 *ptr_buf = (u32 *)vga->buf; for (int i = 0; i < SCR_BUFSIZE * sizeof(u16) / sizeof(u32); ++i) { ptr_buf[i] = (BLANK << 16) | BLANK; // bg-black, fg-white, ascii-space } // kprintf("%p 0x%x %d\n", vga->buf, ((u32 *)vga->buf)[20], vga->scr_cur_line); } #define INDEX(row, col) ((row)*SCR_WIDTH + (col)) #define NEXTLINE(row) NEXT(row, SCR_MAXLINE) #define LASTLINE(row) LAST(row, SCR_MAXLINE) #define ADDLINE(row, add_num) (((row) + add_num) % SCR_MAXLINE) // called by csi static void csi_scroll(vga_buf *vgabuf, i16 scroll_num) { while (scroll_num > 0) // down { scroll_num--; for (int i = SCR_HEIGHT - 1; i > 0; i--) { int line_dst = ADDLINE(vgabuf->scr_top_line, i); int line_src = ADDLINE(vgabuf->scr_top_line, i - 1); u32 *ptr_buf_dst = (u32 *)(vgabuf->buf + sizeof(u16) * line_dst * SCR_WIDTH); u32 *ptr_buf_src = (u32 *)(vgabuf->buf + sizeof(u16) * line_src * SCR_WIDTH); for (int p = 0; p < SCR_WIDTH * sizeof(u16) / sizeof(u32); ++p) { *ptr_buf_dst++ = *ptr_buf_src++; } } u32 *ptr_buf_start = (u32 *)(vgabuf->buf + sizeof(u16) * (vgabuf->scr_top_line) * SCR_WIDTH); for (int i = 0; i < SCR_WIDTH * sizeof(u16) / sizeof(u32); ++i) { *ptr_buf_start++ = (BLANK << 16) | BLANK; // bg-black, fg-white, ascii-space } } while (scroll_num < 0) // up { scroll_num++; for (int i = 0; i < SCR_HEIGHT - 1; i++) { int line_dst = ADDLINE(vgabuf->scr_top_line, i); int line_src = ADDLINE(vgabuf->scr_top_line, i + 1); u32 *ptr_buf_dst = (u32 *)(vgabuf->buf + sizeof(u16) * line_dst * SCR_WIDTH); u32 *ptr_buf_src = (u32 *)(vgabuf->buf + sizeof(u16) * line_src * SCR_WIDTH); for (int p = 0; p < SCR_WIDTH * sizeof(u16) / sizeof(u32); ++p) { *ptr_buf_dst++ = *ptr_buf_src++; } } int line_end = ADDLINE(vgabuf->scr_top_line, SCR_HEIGHT - 1); u32 *ptr_buf_end = (u32 *)(vgabuf->buf + sizeof(u16) * line_end * SCR_WIDTH); for (int i = 0; i < SCR_WIDTH * sizeof(u16) / sizeof(u32); ++i) { *ptr_buf_end++ = (BLANK << 16) | BLANK; // bg-black, fg-white, ascii-space } } } static void newline(vga_buf *vga) { u16 *buf = vga->buf; vga->cur_col = 0; // kprintf("bf %x\n", vgabuf->scr_cur_line); vga->scr_cur_line = NEXTLINE(vga->scr_cur_line); // kprintf("af %x\n", vgabuf->scr_cur_line); vga->cur_row = CYCLE_SUB(vga->scr_top_line, vga->scr_cur_line, SCR_MAXLINE); if (vga->cur_row == SCR_HEIGHT) { // auto scroll vga->scr_top_line = NEXTLINE(vga->scr_top_line); } if (vga->scr_cur_line == vga->head_line) { vga->head_line = NEXTLINE(vga->head_line); // remember to fill blank the old line u32 *ptr_buf = (u32 *)(vga->buf + sizeof(u16) * vga->scr_cur_line * SCR_WIDTH); for (int i = 0; i < SCR_WIDTH * sizeof(u16) / sizeof(u32); ++i) { *ptr_buf++ = (BLANK << 16) | BLANK; // bg-black, fg-white, ascii-space } // for (int i = 0; i < SCR_WIDTH; ++ i) { // buf[INDEX(vga->scr_cur_line, i)] = BLANK; // } } } static void nextcol(vga_buf *vgabuf) { vgabuf->cur_col++; if (vgabuf->cur_col == SCR_WIDTH) { newline(vgabuf); } } static void cursor_locate(i16 row, i16 col, vga_buf *vgabuf) { vgabuf->scr_cur_line = vgabuf->scr_top_line; while (row > 0 && row < SCR_HEIGHT) { row--; vgabuf->scr_cur_line = NEXTLINE(vgabuf->scr_cur_line); if (vgabuf->scr_cur_line == vgabuf->scr_bot_line) break; } if (col >= 0 && col <= SCR_WIDTH - 1) { vgabuf->cur_col = col; } } static void cursor_move(i16 move_row, i16 move_col, vga_buf *vgabuf) { // kprintf("%d,%d", move_row, move_col); while (move_row > 0) // down { move_row--; if (vgabuf->scr_cur_line == vgabuf->scr_bot_line) break; vgabuf->scr_cur_line = NEXTLINE(vgabuf->scr_cur_line); vgabuf->cur_row = CYCLE_SUB(vgabuf->scr_top_line, vgabuf->scr_cur_line, SCR_MAXLINE); if (vgabuf->cur_row == SCR_HEIGHT) { // auto scroll vgabuf->scr_top_line = NEXTLINE(vgabuf->scr_top_line); } } while (move_row < 0) // up { move_row++; vgabuf->scr_cur_line = LASTLINE(vgabuf->scr_cur_line); if (vgabuf->scr_cur_line == vgabuf->scr_top_line) { if (vgabuf->scr_top_line == vgabuf->head_line) break; // vgabuf->scr_cur_line = LASTLINE(vgabuf->scr_cur_line); vgabuf->scr_top_line = LASTLINE(vgabuf->scr_top_line); vgabuf->scr_bot_line = LASTLINE(vgabuf->scr_bot_line); } } vgabuf->cur_row = CYCLE_SUB(vgabuf->scr_top_line, vgabuf->scr_cur_line, SCR_MAXLINE); vgabuf->cur_col += move_col; if (vgabuf->cur_col < 0) vgabuf->cur_col = 0; else if (vgabuf->cur_col >= SCR_WIDTH) vgabuf->cur_col = SCR_WIDTH - 1; } inline static void param12vga_color(i16 *param) { u8 tmp = *param & 1; *param &= 0b0110; *param |= *param >> 2; *param &= 0b0011; *param |= tmp << 2; } static void clear_screen(vga_buf *vgabuf, i16 src_row, i16 src_col, i16 dst_row, i16 dst_col) { u16 *buf = vgabuf->buf; int src_index = INDEX(src_row, src_col); int dst_index = INDEX(dst_row, dst_col); if (src_index <= dst_index) for (int i = src_index; i <= dst_index; i++) { buf[i] = BLANK; } else // scr>dst { for (int i = 0; i <= dst_index; i++) buf[i] = BLANK; int index_end = INDEX(SCR_MAXLINE - 1, SCR_WIDTH - 1); for (int i = 0; i <= index_end; i++) buf[i] = BLANK; } } static void set_color(vga_buf *vgabuf) { if (vgabuf->param1 == 0) { vgabuf->color = DEFAULT_CHAR_COLOR; } else if (vgabuf->param1 == 1) { vgabuf->color |= 0x8800; } else if (vgabuf->param1 == 2) { vgabuf->color &= 0x7700; } else if (30 <= vgabuf->param1 && vgabuf->param1 <= 37) { vgabuf->param1 -= 30; param12vga_color(&(vgabuf->param1)); vgabuf->color = (vgabuf->color & 0xf8ff) | FOREGROUND(vgabuf->param1); } else if (40 <= vgabuf->param1 && vgabuf->param1 <= 47) { vgabuf->param1 -= 40; param12vga_color(&(vgabuf->param1)); vgabuf->color = (vgabuf->color & 0x8fff) | BACKGROUND(vgabuf->param1); } else if (90 <= vgabuf->param1 && vgabuf->param1 <= 97) { vgabuf->param1 -= 90; param12vga_color(&(vgabuf->param1)); vgabuf->param1 |= 0x8; vgabuf->color = (vgabuf->color & 0xf0ff) | FOREGROUND(vgabuf->param1); } else if (100 <= vgabuf->param1 && vgabuf->param1 <= 107) { vgabuf->param1 -= 100; param12vga_color(&(vgabuf->param1)); vgabuf->param1 |= 0x8; vgabuf->color = (vgabuf->color & 0x0fff) | BACKGROUND(vgabuf->param1); } else { warn("unsupport CSI: color"); } if (vgabuf->Is2param == true) { if (vgabuf->param2 == 0 && vgabuf->param1 == 0) { vgabuf->color = DEFAULT_CHAR_COLOR; } else if (vgabuf->param2 == 1) { vgabuf->color |= 0x8800; } else if (vgabuf->param2 == 2) { vgabuf->color &= 0x7700; } else if (30 <= vgabuf->param2 && vgabuf->param2 <= 37) { vgabuf->param2 -= 30; param12vga_color(&(vgabuf->param2)); vgabuf->color = (vgabuf->color & 0xf8ff) | FOREGROUND(vgabuf->param2); } else if (40 <= vgabuf->param2 && vgabuf->param2 <= 47) { vgabuf->param2 -= 40; param12vga_color(&(vgabuf->param2)); vgabuf->color = (vgabuf->color & 0x8fff) | BACKGROUND(vgabuf->param2); } else if (90 <= vgabuf->param2 && vgabuf->param2 <= 97) { vgabuf->param2 -= 90; param12vga_color(&(vgabuf->param2)); vgabuf->param2 |= 0x8; vgabuf->color = (vgabuf->color & 0xf0ff) | FOREGROUND(vgabuf->param2); } else if (100 <= vgabuf->param2 && vgabuf->param2 <= 107) { vgabuf->param2 -= 100; param12vga_color(&(vgabuf->param2)); vgabuf->param2 |= 0x8; vgabuf->color = (vgabuf->color & 0x0fff) | BACKGROUND(vgabuf->param2); } else { warn("unsupport CSI: color"); } } } static void CSI_Erase_handler(vga_buf *vgabuf, int n) { if (n == 2) { vgabuf->scr_bot_line = ADDLINE(vgabuf->scr_top_line, SCR_HEIGHT - 1); for (int i = 0; i < SCR_HEIGHT; i++) { vgabuf->scr_cur_line = NEXTLINE(vgabuf->scr_cur_line); vgabuf->scr_bot_line = NEXTLINE(vgabuf->scr_bot_line); // kprintf("af %x\n", vgabuf->scr_cur_line); vgabuf->cur_row = CYCLE_SUB(vgabuf->scr_top_line, vgabuf->scr_bot_line, SCR_MAXLINE); if (vgabuf->cur_row == SCR_HEIGHT) { // auto scroll vgabuf->scr_top_line = NEXTLINE(vgabuf->scr_top_line); } if (vgabuf->scr_bot_line == vgabuf->head_line) { vgabuf->head_line = NEXTLINE(vgabuf->head_line); // remember to fill blank the old line u32 *ptr_buf = (u32 *)(vgabuf->buf + sizeof(u16) * vgabuf->scr_bot_line * SCR_WIDTH); for (int i = 0; i < SCR_WIDTH * sizeof(u16) / sizeof(u32); ++i) { *ptr_buf++ = (BLANK << 16) | BLANK; // bg-black, fg-white, ascii-space } } } } if (n == 3) { int line = vgabuf->head_line; while (line != vgabuf->scr_top_line) { u32 *ptr_buf = (u32 *)(vgabuf->buf + sizeof(u16) * line * SCR_WIDTH); for (int i = 0; i < SCR_WIDTH * sizeof(u16) / sizeof(u32); ++i) { *ptr_buf++ = (BLANK << 16) | BLANK; // bg-black, fg-white, ascii-space } line = NEXTLINE(line); } vgabuf->head_line = vgabuf->scr_top_line; } } static void CSI_handler(u8 terminator, vga_buf *vgabuf) { vgabuf->CSI = CSI_ESC; switch (terminator) { case 'A': if (vgabuf->param1 == 0) vgabuf->param1 == 1; cursor_move(-vgabuf->param1, 0, vgabuf); break; case 'B': if (vgabuf->param1 == 0) vgabuf->param1 == 1; cursor_move(+vgabuf->param1, 0, vgabuf); break; case 'C': if (vgabuf->param1 == 0) vgabuf->param1 == 1; cursor_move(0, +vgabuf->param1, vgabuf); break; case 'D': if (vgabuf->param1 == 0) vgabuf->param1 == 1; cursor_move(0, -vgabuf->param1, vgabuf); // nothing break; case 'E': if (vgabuf->param1 == 0) vgabuf->param1 == 1; cursor_move(+vgabuf->param1, -vgabuf->cur_col, vgabuf); break; case 'F': if (vgabuf->param1 == 0) vgabuf->param1 == 1; cursor_move(-vgabuf->param1, -vgabuf->cur_col, vgabuf); break; case 'G': // added if (vgabuf->param1 == 0) vgabuf->param1 == 1; cursor_locate(CYCLE_SUB(vgabuf->scr_top_line, vgabuf->scr_cur_line, SCR_MAXLINE), vgabuf->param1 - 1, vgabuf); break; case 'H': case 'f': if (vgabuf->param1 == 0) vgabuf->param1 == 1; if (vgabuf->param2 == 0) vgabuf->param2 == 1; cursor_locate(vgabuf->param1 - 1, vgabuf->param2 - 1, vgabuf); break; case 'J': if (vgabuf->param1 == 0) clear_screen(vgabuf, vgabuf->scr_cur_line, vgabuf->cur_col, ADDLINE(vgabuf->scr_top_line, SCR_HEIGHT - 1), SCR_WIDTH - 1); else if (vgabuf->param1 == 1) clear_screen(vgabuf, vgabuf->scr_top_line, 0, vgabuf->scr_cur_line, vgabuf->cur_col); else if (vgabuf->param1 == 2) CSI_Erase_handler(vgabuf, 2); else if (vgabuf->param1 == 3) CSI_Erase_handler(vgabuf, 3); break; case 'K': if (vgabuf->param1 == 0) clear_screen(vgabuf, vgabuf->scr_cur_line, vgabuf->cur_col, vgabuf->scr_cur_line, SCR_WIDTH - 1); else if (vgabuf->param1 == 1) clear_screen(vgabuf, vgabuf->scr_cur_line, 0, vgabuf->scr_cur_line, vgabuf->cur_col); else if (vgabuf->param1 == 2) clear_screen(vgabuf, vgabuf->scr_cur_line, 0, vgabuf->scr_cur_line, SCR_WIDTH - 1); break; case 'S': // Scroll Up if (vgabuf->param1 == 0) vgabuf->param1 = 1; csi_scroll(vgabuf, -vgabuf->param1); break; case 'T': // Scroll Down if (vgabuf->param1 == 0) vgabuf->param1 = 1; csi_scroll(vgabuf, +vgabuf->param1); break; case 'm': set_color(vgabuf); break; } } void vga_tty_write(NTTY *tty, char ch) { // assert(tty->driver_type == 1); // assert(tty->output_buf); vga_buf *vga = tty->output_buf; u16 *buf = vga->buf; // kprintf("vga_tty_write %c to %d %d\n", ch, vga->scr_cur_line, vga->cur_col); if (vga->CSI == CSI_ESC) { switch (ch) { case '\t': if (INDEX(vga->scr_cur_line, vga->cur_col) == SCR_BUFSIZE - 1) break; while (vga->cur_col % 4 != 1) { nextcol(vga); } break; case '\n': newline(vga); break; case '\r': vga->cur_col = 0; break; case '\b': // this implementation is mimic to usual linux shell // it moves cursor left but neither crosses line nor delete character if (vga->cur_col > 0) { vga->cur_col--; } break; case '\x1b': vga->CSI = CSI_BRACKET; vga->Is2param = false; break; default: if (vga->color == 0) { buf[INDEX(vga->scr_cur_line, vga->cur_col)] = MAKE_CELL(DEFAULT_CHAR_COLOR, ch); } else { buf[INDEX(vga->scr_cur_line, vga->cur_col)] = MAKE_CELL(vga->color, ch); } // buf[INDEX(vga->scr_cur_line, vga->cur_col)] = MAKE_CELL(DEFAULT_CHAR_COLOR, ch); nextcol(vga); break; } } else if (vga->CSI == CSI_BRACKET) { switch (ch) { case '[': vga->CSI = CSI_PARAM1; vga->param1 = vga->param2 = 0; break; default: vga->CSI = CSI_ESC; break; } } else { switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (vga->CSI == CSI_PARAM1) vga->param1 = vga->param1 * 10 + ch - '0'; else if (vga->CSI == CSI_PARAM2) vga->param2 = vga->param2 * 10 + ch - '0'; else ; // do nothing break; case ';': vga->CSI = CSI_PARAM2; vga->Is2param = true; break; default: if (!(0x20 <= ch && ch <= 0x7e)) vga->CSI = CSI_ESC; if (0x40 <= ch && ch <= 0x7e) CSI_handler(ch, vga); break; } } vga->cur_row = CYCLE_SUB(vga->scr_top_line, vga->scr_cur_line, SCR_MAXLINE); if (CYCLE_SUB(vga->head_line, vga->scr_cur_line, SCR_MAXLINE) >= CYCLE_SUB(vga->head_line, vga->scr_bot_line, SCR_MAXLINE)) { vga->scr_bot_line = vga->scr_cur_line; } // kprintf("row: %d; ", vga->cur_row); } void vga_tty_backspace(NTTY *tty) { vga_buf *vga = tty->output_buf; u16 *buf = vga->buf; if (vga->cur_col == 0) { vga->cur_col = SCR_WIDTH - 1; vga->scr_cur_line = LASTLINE(vga->scr_cur_line); } else { vga->cur_col--; } buf[INDEX(vga->scr_cur_line, vga->cur_col)] = BLANK; vga->cur_row = CYCLE_SUB(vga->scr_top_line, vga->scr_cur_line, SCR_MAXLINE); } void vga_tty_flush(NTTY *tty) { vga_buf *vga = tty->output_buf; u16 *buf = vga->buf; int i, cur_line; vga_set_cursor(INDEX(vga->cur_row, vga->cur_col)); vga->cur_row = CYCLE_SUB(vga->scr_top_line, vga->scr_cur_line, SCR_MAXLINE); int bottom = min(max(vga->cur_row, CYCLE_SUB(vga->scr_top_line, vga->scr_bot_line, SCR_MAXLINE)), SCR_HEIGHT); // if (vga->cur_row == SCR_HEIGHT - 1) // { // vga_flush_screen(&buf[INDEX(vga->scr_top_line, 0)]); // } // else // { cur_line = vga->scr_top_line; for (i = 0; i <= bottom; ++i) { vga_flush_line(&buf[INDEX(cur_line, 0)], i); cur_line = NEXTLINE(cur_line); } for (; i < SCR_HEIGHT; ++i) { vga_flush_blankline(i); } // } // kprintf("flush: row=%d, top=%d, cur=%d\n", vga->cur_row, vga->scr_top_line, vga->scr_cur_line); } void vga_tty_scroll(NTTY *tty, int direction) { vga_buf *vga = tty->output_buf; u16 *buf = vga->buf; if (direction == MOUSESCR_DOWN) { // down if (vga->scr_top_line == vga->scr_cur_line) return; vga->scr_top_line = NEXTLINE(vga->scr_top_line); } else if (direction == MOUSESCR_UP) { if (vga->scr_top_line == vga->head_line) return; vga->scr_top_line = LASTLINE(vga->scr_top_line); } else { if (CYCLE_SUB(vga->scr_top_line, vga->scr_cur_line, SCR_MAXLINE) >= SCR_HEIGHT) { vga->scr_top_line = CYCLE_SUB(SCR_HEIGHT / 2, vga->scr_cur_line, SCR_MAXLINE); } // kprintf("scroll to cur top-%d, cur-%d\n", vga->scr_top_line, vga->scr_cur_line); } vga->cur_row = CYCLE_SUB(vga->scr_top_line, vga->scr_cur_line, SCR_MAXLINE); if (vga->cur_row >= SCR_HEIGHT) { vga_disable_cursor(); // kprintf("disable cursor: %d\n", vga->cur_row); } else { vga_enable_cursor(14, 15); } vga_tty_flush(tty); } void vga_tty_select(NTTY *tty) { disable_int(); cur_ntty = tty; enable_int(); vga_tty_flush(cur_ntty); }