/**
* video.c
*
* Text video routines
*
* Exports:
* blink();
* select_vc()
* putch_help()
* putch()
* init_video();
*
* Imports:
* main.c printf();
*/
/**
* TODO
*
* Fuck me with a blind melon...when the screen scrolls
* too much, it generates a panic of type invalid opcode.
*/
#include <string.h> /* memcpy(), memsetw() */
#include <ctype.h> /* isdigit() */
#include <x86.h> /* outportb(), inportb() */
#include <stdint.h>
#include "_krnl.h" /* MAX_VC, console_t */
/**
* Imports
*/
void printf(const char *fmt, ...);
#define VGA_MISC_READ 0x3CC
console_t _vc[MAX_VC];
static unsigned _num_vcs;
static console_t *_curr_vc;
static unsigned short *_vga_fb_adr;
static unsigned _crtc_io_adr, _vc_width, _vc_height;
unsigned curr_vtty;
/**
* blink()
*
*/
void blink(void)
{
(*(unsigned char *)_vga_fb_adr)++;
}
/**
* get_current_vc()
*
*/
unsigned get_current_vc()
{
return curr_vtty;
}
/**
* scroll()
*
*/
static void scroll(console_t *con)
{
unsigned short *fb_adr;
unsigned blank, temp;
blank = 0x20 | ((unsigned)con->attrib << 8);
fb_adr = con->fb_adr;
/**
* scroll up
*/
if(con->csr_y >= _vc_height)
{
temp = con->csr_y - _vc_height + 1;
memcpy(fb_adr, fb_adr + temp * _vc_width,
(_vc_height - temp) * _vc_width * 2);
/**
* blank bottom line of screen
*/
memsetw(fb_adr + (_vc_height - temp) * _vc_width,
blank, _vc_width);
con->csr_y = _vc_height - 1;
}
//for(i = 0; i < 0x1000000; i++) ;
}
/**
* set_attrib()
*
*/
static void set_attrib(console_t *con, unsigned att)
{
static const unsigned ansi_to_vga[] =
{
0, 4, 2, 6, 1, 5, 3, 7
};
unsigned new_att;
new_att = con->attrib;
if(att == 0)
new_att &= ~0x08; /* bold off */
else if(att == 1)
new_att |= 0x08; /* bold on */
else if(att >= 30 && att <= 37)
{
att = ansi_to_vga[att - 30];
new_att = (new_att & ~0x07) | att;/* fg color */
}
else if(att >= 40 && att <= 47)
{
att = ansi_to_vga[att - 40] << 4;
new_att = (new_att & ~0x70) | att;/* bg color */
}
con->attrib = new_att;
}
/**
* move_csr()
*
*/
static void move_csr(void)
{
unsigned temp;
temp = (_curr_vc->csr_y * _vc_width + _curr_vc->csr_x) +
(_curr_vc->fb_adr - _vga_fb_adr);
outportb(_crtc_io_adr + 0, 14);
outportb(_crtc_io_adr + 1, temp >> 8);
outportb(_crtc_io_adr + 0, 15);
outportb(_crtc_io_adr + 1, temp);
}
/**
* select_vc()
*
*/
void select_vc(unsigned which_vc)
{
unsigned i;
if(which_vc >= _num_vcs)
return;
_curr_vc = _vc + which_vc;
i = _curr_vc->fb_adr - _vga_fb_adr;
outportb(_crtc_io_adr + 0, 12);
outportb(_crtc_io_adr + 1, i >> 8);
outportb(_crtc_io_adr + 0, 13);
outportb(_crtc_io_adr + 1, i);
curr_vtty = which_vc;
move_csr();
}
/**
* putch_help()
*
*/
void putch_help(console_t *con, unsigned c)
{
unsigned short *fb_adr;
unsigned att;
att = (unsigned)con->attrib << 8;
fb_adr = con->fb_adr;
/**
* state machine to handle escape sequences
*
* ESC
*/
if(con->esc == 1)
{
if(c == '[')
{
con->esc++;
con->esc1 = 0;
return;
}
/* else fall-through: zero esc and print c */
}
/**
* ESC[
*/
else if(con->esc == 2)
{
if(isdigit(c))
{
con->esc1 = con->esc1 * 10 + c - '0';
return;
}
else if(c == ';')
{
con->esc++;
con->esc2 = 0;
return;
}
/**
* ESC[2J (clear screen)
*/
else if(c == 'J')
{
if(con->esc1 == 2)
{
memsetw(fb_adr, ' ' | att,
_vc_height * _vc_width);
con->csr_x = con->csr_y = 0;
}
}
/**
* ESC[num1m (set attribute num1)
*/
else if(c == 'm')
set_attrib(con, con->esc1);
con->esc = 0; /* anything else with one numeric arg */
return;
}
/**
* ESC[num1
*/
else if(con->esc == 3)
{
if(isdigit(c))
{
con->esc2 = con->esc2 * 10 + c - '0';
return;
}
else if(c == ';')
{
con->esc++; /* ESC[num1;num2; */
con->esc3 = 0;
return;
}
/**
* ESC[num1;num2H (move cursor to num1,num2)
*/
else if(c == 'H')
{
if(con->esc2 < _vc_width)
con->csr_x = con->esc2;
if(con->esc1 < _vc_height)
con->csr_y = con->esc1;
}
/**
* ESC[num1;num2m (set attributes num1,num2)
*/
else if(c == 'm')
{
set_attrib(con, con->esc1);
set_attrib(con, con->esc2);
}
con->esc = 0;
return;
}
/**
* ESC[num1;num2;num3
*/
else if(con->esc == 4)
{
if(isdigit(c))
{
con->esc3 = con->esc3 * 10 + c - '0';
return;
}
/**
* ESC[num1;num2;num3m (set attributes num1,num2,num3)
*/
else if(c == 'm')
{
set_attrib(con, con->esc1);
set_attrib(con, con->esc2);
set_attrib(con, con->esc3);
}
con->esc = 0;
return;
}
con->esc = 0;
/**
* escape character
*/
if(c == 0x1B)
{
con->esc = 1;
return;
}
/**
* backspace
*/
if(c == 0x08)
{
if(con->csr_x != 0)
con->csr_x--;
}
/**
* tab
*/
else if(c == 0x09)
con->csr_x = (con->csr_x + 8) & ~(8 - 1);
/**
* carriage return
*/
else if(c == '\r') /* 0x0D */
con->csr_x = 0;
/**
* line feed
*/
/* else if(c == '\n') *//* 0x0A */
/* con->csr_y++;*/
/**
* CR/LF
*/
else if(c == '\n') /* ### - 0x0A again */
{
con->csr_x = 0;
con->csr_y++;
}
/**
* printable ASCII
*/
else if(c >= ' ')
{
unsigned short *where;
where = fb_adr + (con->csr_y * _vc_width + con->csr_x);
*where = (c | att);
con->csr_x++;
}
if(con->csr_x >= _vc_width)
{
con->csr_x = 0;
con->csr_y++;
}
scroll(con);
/**
* move cursor only if the VC we're writing is the current VC
*/
if(_curr_vc == con)
move_csr();
}
/**
* putch()
*
*/
void putch(unsigned c)
{
/* all kernel messages to VC #0 */
// putch_help(_vc + 0, c);
/* all kernel messages to current VC */
putch_help(_curr_vc, c);
}
/**
* init_video()
*
*/
void init_video(void)
{
unsigned i;
/**
* check for monochrome or color VGA emulation
*/
if((inportb(VGA_MISC_READ) & 0x01) != 0)
{
_vga_fb_adr = (unsigned short *)0xB8000L;
_crtc_io_adr = 0x3D4;
}
else
{
_vga_fb_adr = (unsigned short *)0xB0000L;
_crtc_io_adr = 0x3B4;
}
/**
* read current screen size from BIOS data segment (addresses 400-4FF)
*/
_vc_width = *(unsigned short *)0x44A;
_vc_height = *(unsigned char *)0x484 + 1;
/**
* figure out how many VCs we can have with 32K of display memory.
* Use INTEGER division to round down.
*/
_num_vcs = 32768L / (_vc_width * _vc_height * 2);
if(_num_vcs > MAX_VC)
_num_vcs = MAX_VC;
/**
* init VCs, with a different foreground color for each
*/
for(i = 0; i < _num_vcs; i++)
{
_curr_vc = _vc + i;
//_curr_vc->attrib = i + 1;
/* terminal foreground color */
_curr_vc->attrib = 7;
_curr_vc->fb_adr = _vga_fb_adr +
_vc_width * _vc_height * i;
/**
* ESC[2J clears the screen
*/
//kprintf("\x1B[2J this is VC#%u (of 0-%u)\n",
// i, _num_vcs - 1);
printf("\x1B[2J");
if(i != 0)
printf("$ ");
}
select_vc(0);
curr_vtty = 0;
_curr_vc->attrib = 8;
printf("[ ");
_curr_vc->attrib = 15;
printf("init: video %5s emulation, %2ux%2u, framebuffer at 0x%1X ",
(_crtc_io_adr == 0x3D4) ? "color" : "mono",
_vc_width, _vc_height, _vga_fb_adr);
_curr_vc->attrib = 8;
printf("]................");
_curr_vc->attrib = 2;
printf("Ok");
_curr_vc->attrib = 7;
}