#include "video_driver.h"
#include "attr_mode.h"
#include "asm-video.h"
#include <asm.h>
#define VIDEO_START 0xb8000
#define VIDEO_END 0xc0000
#define LINES 25
#define COLUMNS 80
static unsigned short *const video_start = (unsigned short *)VIDEO_START;
static unsigned short *const video_end = (unsigned short *)VIDEO_END;
static const unsigned short lines = LINES;
static const unsigned short columns = COLUMNS;
#define virtual_screen_lines ((video_end - video_start) / columns)
#define history_max_lines (virtual_screen_lines - lines - (lines - 1))
static unsigned short *origin;
static unsigned short null_char;
static unsigned short erase_char;
static unsigned short history_lines;
static unsigned short history_pos;
static int history_updated;
static inline unsigned char read_crtcreg(unsigned char index)
{
outbp(index, 0x3d4);
return inbp(0x3d5);
}
static inline void write_crtcreg(unsigned char index, unsigned char val)
{
outbp(index, 0x3d4);
outbp(val, 0x3d5);
}
static void set_start_addr(unsigned short start_addr)
{
int intr;
disable_intr(intr);
write_crtcreg(0x0c, start_addr >> 8);
write_crtcreg(0x0d, start_addr);
restore_intr(intr);
}
static unsigned short get_cursor_loc()
{
int intr;
unsigned short loc;
disable_intr(intr);
loc = read_crtcreg(0x0e) << 8;
loc += read_crtcreg(0x0f);
restore_intr(intr);
return loc;
}
static void set_cursor_loc(unsigned short loc)
{
int intr;
disable_intr(intr);
write_crtcreg(0x0e, loc >> 8);
write_crtcreg(0x0f, loc);
restore_intr(intr);
}
static int get_cursor_disable()
{
int intr;
unsigned char val;
disable_intr(intr);
val = read_crtcreg(0x0a);
restore_intr(intr);
return val | 0x20;
}
static void set_cursor_disable(int disable)
{
int intr;
unsigned char val;
disable_intr(intr);
val = read_crtcreg(0x0a);
write_crtcreg(0x0a, disable ? val | 0x20 : val & ~0x20);
restore_intr(intr);
}
static void scroll()
{
origin += columns;
if (origin + lines * columns > video_end) {
copy_fill(video_start, origin, (lines - 1) * columns, erase_char, columns);
origin = video_start;
}
else
fill(origin + (lines - 1) * columns, erase_char, columns);
set_start_addr(origin - video_start);
if (history_lines < history_max_lines)
history_lines++;
}
static void clear(unsigned short start, unsigned short end)
{
fill(origin + start, erase_char, end - start);
}
static void history_display(unsigned short new_history_pos)
{
unsigned short *history_origin;
history_pos = new_history_pos;
if ((history_origin = origin - history_pos * columns) < video_start) {
history_origin += (history_max_lines + lines) * columns;
if (!history_updated) {
copy(video_start + (history_max_lines + lines) * columns, video_start, (lines - 1) * columns);
history_updated = 1;
}
}
set_start_addr(history_origin - video_start);
}
static void history_cancel()
{
history_pos = 0;
history_updated = 0;
set_start_addr(origin - video_start);
}
void video_init()
{
origin = video_start;
null_char = 0x7 << 8;
erase_char = (0x7 << 8) | ' ';
history_lines = 0;
history_pos = 0;
history_updated = 0;
}
static unsigned short video_get_lines()
{
return lines;
}
static unsigned short video_get_columns()
{
return columns;
}
static unsigned char video_get_cursor_type()
{
return get_cursor_disable();
}
static void video_set_cursor_type(unsigned char type)
{
set_cursor_disable(!type);
}
static unsigned short video_get_cursor_pos()
{
return get_cursor_loc() - (origin - video_start);
}
static void video_set_cursor_pos(unsigned short pos)
{
set_cursor_loc(origin - video_start + pos);
}
static void video_set_attr(const struct attr_mode_struct *attr_mode)
{
unsigned char attr;
attr = 0;
if (attr_mode->concealed) {
if (attr_mode->reverse)
attr |= (attr_mode->bgcolor << 4) | attr_mode->bgcolor;
else
attr |= (attr_mode->bgcolor << 4) | attr_mode->bgcolor;
}
else {
if (attr_mode->intensity >= 2)
attr |= 0x08;
if (attr_mode->blink)
attr |= 0x80;
if (attr_mode->reverse)
attr |= (attr_mode->fgcolor << 4) | attr_mode->bgcolor;
else
attr |= (attr_mode->bgcolor << 4) | attr_mode->fgcolor;
}
null_char = attr << 8;
erase_char = ((attr_mode->bgcolor << 4) | attr_mode->fgcolor) << 8 | ' ';
}
static void video_putc(unsigned short pos, unsigned char c)
{
*(origin + pos) = null_char | c;
}
static void video_clear(unsigned short start, unsigned short end)
{
clear(start, end);
}
static void video_scroll()
{
scroll();
}
static void video_history_scroll(short n)
{
unsigned short new_history_pos;
if (history_pos + n < 0)
new_history_pos = 0;
else if (history_pos + n > history_lines)
new_history_pos = history_lines;
else
new_history_pos = history_pos + n;
if (new_history_pos != history_pos)
history_display(new_history_pos);
}
static void video_history_cancel()
{
if (history_pos || history_updated)
history_cancel();
}
const struct video_driver_struct video_driver = {
.get_lines = video_get_lines,
.get_columns = video_get_columns,
.get_cursor_type = video_get_cursor_type,
.set_cursor_type = video_set_cursor_type,
.get_cursor_pos = video_get_cursor_pos,
.set_cursor_pos = video_set_cursor_pos,
.set_attr = video_set_attr,
.putc = video_putc,
.clear = video_clear,
.scroll = video_scroll,
.history_scroll = video_history_scroll,
.history_cancel = video_history_cancel
};