View of xos/drivers/video.c


XOS | Parent Directory | View | Download

/* 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 <http://www.gnu.org/licenses/>. */
 
/* Documentation :
 * - VGA : http://www.osdever.net/FreeVGA/ */
 
#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)
/* 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
};