/* 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 };