/* Pilote de console VGA en mode texte 80x25 16 couleurs */
/* Copyright (C) 2008 Emmanuel Varoquaux
This file is part of XOS.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
/* Documentation :
* - VGA : http://www.osdever.net/FreeVGA/ */
#include "video_driver.h"
#include "attr_mode.h"
#include "asm-video.h"
#include
#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)
/* Le nombre maximal de lignes que l'on peut defiler dans l'historique est :
* le nombre de lignes de l'ecran virtuel
* moins le nombre de lignes de l'ecran reel (qu'on ne peut pas defiler)
* moins le nombre de lignes de la zone de recouvrement. */
#define history_max_lines (virtual_screen_lines - lines - (lines - 1))
/* pilote */
static unsigned short *origin; /* adresse de base de la fenetre */
static unsigned short null_char; /* caractere vide */
static unsigned short erase_char; /* caractere d'effacement */
/* historique */
static unsigned short history_lines; /* nombre de lignes que l'on peut defiler */
static unsigned short history_pos; /* position dans l'historique */
static int history_updated; /* zone de recouvrement a jour ? */
/* Fonctions utilitaires */
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);
}
/* Routines d'entree/sortie */
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);
}
/* Routines d'affichage de bas niveau */
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);
}
/* Historique */
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);
}
/* Fonctions exportees */
void video_init()
{
origin = video_start;
null_char = 0x7 << 8; /* valeur par defaut */
erase_char = (0x7 << 8) | ' '; /* valeur par defaut */
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();
}
/* Implementation simpliste de cette fonctionnalite :
* - si type != 0, le curseur est affiche
* - si type == 0, le curseur est masque. */
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 | ' '; /* fgcolor est la couleur du curseur */
}
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);
}
/* Appele a deux occasions :
* - lorsqu'une interruption clavier emet un keycode ;
* - lorsque du texte est affiche sur l'ecran. */
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
};