BigOS/kernel/console.c
2022-12-23 12:59:26 +08:00

344 lines
10 KiB
C

#include "type.h"
#include "stdio.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "fs.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"
#include "x86.h"
/* local routines */
static void set_cursor(unsigned int position);
static void set_video_start_addr(u32 addr);
static void flush(CONSOLE *con);
static void w_copy(unsigned int dst, const unsigned int src, int size);
static void clear_screen(int pos, int len);
void scroll_screen(CONSOLE *con, int dir);
/*****************************************************************************
* init_screen
*****************************************************************************/
/**
* Initialize the console of a certain tty.
*
* @param tty Whose console is to be initialized.
*****************************************************************************/
void init_screen(TTY *tty)
{
int nr_tty = tty - tty_table;
tty->console = console_table + nr_tty;
/*
* NOTE:
* variables related to `position' and `size' below are
* in WORDs, but not in BYTEs.
*/
int v_mem_size = V_MEM_SIZE >> 1; /* size of Video Memory */
int size_per_con = (v_mem_size / NR_CONSOLES) / 80 * 80;
tty->console->orig = nr_tty * size_per_con;
tty->console->con_size = size_per_con / SCR_WIDTH * SCR_WIDTH;
tty->console->cursor = tty->console->crtc_start = tty->console->orig;
tty->console->is_full = 0;
tty->console->current_line = 0;
if (nr_tty == 0)
{
tty->console->cursor = disp_pos / 2;
}
const char prompt[] = "[TTY #?]\n";
const char *p = prompt;
for (; *p; p++)
{
out_char(tty->console, *p == '?' ? nr_tty + '0' : *p);
}
set_cursor(tty->console->cursor);
}
static inline void write_char_c(int pos, char ch)
{
u16 *pch = (u16 *)K_PHY2LIN(V_MEM_BASE + pos);
*pch = (0x0f << 8) | ch;
}
/*****************************************************************************
* out_char
*****************************************************************************/
/**
* Print a char in a certain console.
*
* @param con The console to which the char is printed.
* @param ch The char to print.
*****************************************************************************/
void out_char(CONSOLE *con, char ch)
{
disable_int();
int cursor_x = (con->cursor - con->orig) % SCR_WIDTH;
int cursor_y = (con->cursor - con->orig) / SCR_WIDTH;
switch (ch)
{
case '\n':
con->cursor = con->orig + SCR_WIDTH * (cursor_y + 1);
break;
case '\b':
if (con->cursor > con->orig)
{
con->cursor--;
//*(pch - 2) = ' ';
//*(pch - 1) = DEFAULT_CHAR_COLOR;
disp_pos = con->cursor * 2;
// write_char(' ');
write_char_c(disp_pos, ' ');
}
break;
default:
//*pch++ = ch;
//*pch++ = DEFAULT_CHAR_COLOR;
disp_pos = con->cursor * 2;
// write_char(ch);
write_char_c(disp_pos, ch);
con->cursor++;
break;
}
if (con->cursor - con->orig >= con->con_size)
{
cursor_x = (con->cursor - con->orig) % SCR_WIDTH;
cursor_y = (con->cursor - con->orig) / SCR_WIDTH;
int cp_orig = con->orig + (cursor_y + 1) * SCR_WIDTH - SCR_SIZE;
w_copy(con->orig, cp_orig, SCR_SIZE - SCR_WIDTH);
con->crtc_start = con->orig;
con->cursor = con->orig + (SCR_SIZE - SCR_WIDTH) + cursor_x;
clear_screen(con->cursor, SCR_WIDTH);
if (!con->is_full)
con->is_full = 1;
}
// assert(con->cursor - con->orig < con->con_size);
while (con->cursor >= con->crtc_start + SCR_SIZE ||
con->cursor < con->crtc_start)
{
scroll_screen(con, SCR_UP);
clear_screen(con->cursor, SCR_WIDTH);
}
flush(con);
enable_int();
}
/*****************************************************************************
* clear_screen
*****************************************************************************/
/**
* Write whitespaces to the screen.
*
* @param pos Write from here.
* @param len How many whitespaces will be written.
*****************************************************************************/
static void clear_screen(int pos, int len)
{
u8 *pch = (u8 *)K_PHY2LIN(V_MEM_BASE + pos * 2);
while (--len >= 0)
{
*pch++ = ' ';
*pch++ = (u8)(DEFAULT_CHAR_COLOR >> 8);
}
}
/*****************************************************************************
* is_current_console
*****************************************************************************/
/**
* Uses `nr_current_console' to determine if a console is the current one.
*
* @param con Ptr to console.
*
* @return TRUE if con is the current console.
*****************************************************************************/
int is_current_console(CONSOLE *con)
{
return (con == &console_table[current_console]);
}
/*****************************************************************************
* set_cursor
*****************************************************************************/
/**
* 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 void 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();
}
/*****************************************************************************
* set_video_start_addr
*****************************************************************************/
/**
* Routine for hardware screen scrolling.
*
* @param addr Offset in the video memory.
*****************************************************************************/
static void 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();
}
/*****************************************************************************
* select_console
*****************************************************************************/
/**
* Select a console as the current.
*
* @param nr_console Console nr, range in [0, NR_CONSOLES-1].
*****************************************************************************/
void select_console(int nr_console)
{
if ((nr_console < 0) || (nr_console >= NR_CONSOLES))
return;
flush(&console_table[current_console = nr_console]);
}
/*****************************************************************************
* scroll_screen
*****************************************************************************/
/**
* Scroll the screen.
*
* Note that scrolling UP means the content of the screen will go upwards, so
* that the user can see lines below the bottom. Similarly scrolling DOWN means
* the content of the screen will go downwards so that the user can see lines
* above the top.
*
* When there is no line below the bottom of the screen, scrolling UP takes no
* effects; when there is no line above the top of the screen, scrolling DOWN
* takes no effects.
*
* @param con The console whose screen is to be scrolled.
* @param dir SCR_UP : scroll the screen upwards;
* SCR_DN : scroll the screen downwards
*****************************************************************************/
void scroll_screen(CONSOLE *con, int dir)
{
/*
* variables below are all in-console-offsets (based on con->orig)
*/
int oldest; /* addr of the oldest available line in the console */
int newest; /* .... .. ... latest ......... .... .. ... ....... */
int scr_top; /* position of the top of current screen */
newest = (con->cursor - con->orig) / SCR_WIDTH * SCR_WIDTH;
oldest = con->is_full ? (newest + SCR_WIDTH) % con->con_size : 0;
scr_top = con->crtc_start - con->orig;
if (dir == SCR_DN)
{
if (!con->is_full && scr_top > 0)
{
con->crtc_start -= SCR_WIDTH;
}
else if (con->is_full && scr_top != oldest)
{
if (con->cursor - con->orig >= con->con_size - SCR_SIZE)
{
if (con->crtc_start != con->orig)
con->crtc_start -= SCR_WIDTH;
}
else if (con->crtc_start == con->orig)
{
scr_top = con->con_size - SCR_SIZE;
con->crtc_start = con->orig + scr_top;
}
else
{
con->crtc_start -= SCR_WIDTH;
}
}
}
else if (dir == SCR_UP)
{
if (!con->is_full && newest >= scr_top + SCR_SIZE)
{
con->crtc_start += SCR_WIDTH;
}
else if (con->is_full && scr_top + SCR_SIZE - SCR_WIDTH != newest)
{
if (scr_top + SCR_SIZE == con->con_size)
con->crtc_start = con->orig;
else
con->crtc_start += SCR_WIDTH;
}
}
else
{
// assert(dir == SCR_DN || dir == SCR_UP);
}
flush(con);
}
/*****************************************************************************
* flush
*****************************************************************************/
/**
* Set the cursor and starting address of a console by writing the
* CRT Controller Registers.
*
* @param con The console to be set.
*****************************************************************************/
static void flush(CONSOLE *con)
{
if (is_current_console(con))
{
set_cursor(con->cursor);
set_video_start_addr(con->crtc_start);
}
}
/*****************************************************************************
* w_copy
*****************************************************************************/
/**
* Copy data in WORDS.
*
* Note that the addresses of dst and src are not pointers, but integers, 'coz
* in most cases we pass integers into it as parameters.
*
* @param dst Addr of destination.
* @param src Addr of source.
* @param size How many words will be copied.
*****************************************************************************/
static void w_copy(unsigned int dst, const unsigned int src, int size)
{
memcpy((void *)(V_MEM_BASE + (dst << 1)),
(void *)(V_MEM_BASE + (src << 1)),
size << 1);
}