BigOS/kernel/vga.c
2022-12-23 23:00:03 +08:00

260 lines
7.8 KiB
C

#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*)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 = 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)
static void newline(vga_buf* vga) {
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->head_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
}
}
}
}
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);
switch (ch)
{
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;
default:
buf[INDEX(vga->scr_cur_line, vga->cur_col)] = MAKE_CELL(DEFAULT_CHAR_COLOR, ch);
vga->cur_col ++;
if (vga->cur_col == SCR_WIDTH) {
newline(vga);
}
break;
}
vga->cur_row = CYCLE_SUB(vga->scr_top_line, vga->scr_cur_line, SCR_MAXLINE);
// 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));
if (vga->cur_row == SCR_WIDTH - 1) {
vga_flush_screen(&buf[INDEX(vga->scr_top_line, 0)]);
}
else {
cur_line = vga->scr_top_line;
for (i = 0; i <= vga->cur_row; ++ i){
vga_flush_line(&buf[INDEX(cur_line, 0)], i);
cur_line = NEXTLINE(cur_line);
}
for (; i < SCR_WIDTH; ++ 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 > 0) {
// down
if (vga->scr_top_line == vga->scr_cur_line) return;
vga->scr_top_line = NEXTLINE(vga->scr_top_line);
}
else {
if (vga->scr_top_line == vga->head_line) return;
vga->scr_top_line = LASTLINE(vga->scr_top_line);
}
vga->cur_row = abs(vga->scr_cur_line - vga->scr_top_line);
if (vga->cur_row >= SCR_HEIGHT) {
vga_disable_cursor();
}
else {
vga_enable_cursor(0, 15);
}
}
void vga_scroll_to_cur(NTTY* tty) {
vga_buf* vga = tty->output_buf;
u16* buf = vga->buf;
// vga->scr_top_line = vga->scr_cur_line
}
void vga_tty_select(NTTY* tty) {
//
}