Files
dosemu2/src/base/bios/int10.c
geos_one 91736529d5
Some checks failed
Master / Scheduled (FULL) (push) Has been cancelled
Master / Triggered (push) Has been cancelled
Master / Triggered (ASAN) (push) Has been cancelled
Master / Triggered (FULL) (push) Has been cancelled
New upstream version 2.0pre9.2
2025-08-10 12:35:43 +02:00

1748 lines
51 KiB
C

/*
* DANG_BEGIN_MODULE
*
* REMARK
* Video BIOS implementation.
*
* This module handles the int10 video functions.
* Most functions here change only the video memory and status
* variables; the actual screen is then rendered asynchronously
* after these by Video->update_screen.
*
* /REMARK
* DANG_END_MODULE
*
* DANG_BEGIN_CHANGELOG
*
* 5/24/95, Erik Mouw (J.A.K.Mouw@et.tudelft.nl) and
* Arjan Filius (I.A.Filius@et.tudelft.nl)
* changed int10() to make graphics work with X.
*
* 1998/04/05: Put some work into set_video_mode() (made it
* more VGA compatible) and removed new_set_video_mode().
* Removed (useless) global variable "gfx_mode".
* -- sw (Steffen Winterfeldt <wfeldt@suse.de>)
*
* Readded new_set_video_mode, its needed for non-X compiles.
* -- EB 3 June 1998
*
* Renamed new_set_video_mode to X_set_video_mode to avoid confusion
* in the future
* -- Hans 980614
*
* 1998/12/12: Fixed some bugs and integrated Josef Pavlik's <jet@spintec.com>
* patches; improved font/palette changes, get screen mode (ah = 0x0f) fixed,
* cursor shape is now initialized during mode set.
* -- sw
*
* 2000/05/18: Split int10() into a X and non-X part. Reworked to X part so
* that it supports fonts in gfx modes.
* -- sw
*
* 2002/11/30: Started user font support. Needs some more work!
* -- eric@coli.uni-sb.de - Eric
*
* DANG_END_CHANGELOG
*/
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include "emu.h"
#include "video.h"
#include "bios.h"
#include "int.h"
#include "port.h"
#include "speaker.h"
#include "utilities.h"
#include "dos2linux.h"
#include "timers.h"
#include "vgaemu.h"
#include "vgatext.h"
/*
* Activate some debug output.
*/
#define DEBUG_INT10 1
#define BIOS_CONFIG_SCREEN_MODE (READ_WORD(BIOS_CONFIGURATION) & 0x30)
#define IS_SCREENMODE_MDA (BIOS_CONFIG_SCREEN_MODE == 0x30)
static const int max_page = 7;
int video_mode;
int video_combo;
unsigned screen_adr(int page)
{
/* This is the text screen base, the DOS program actually has to use.
* Programs that support simultaneous dual monitor support rely on
* the fact, that the BIOS takes B0000 for EQUIP-flags 4..5 = 3
* else B8000 as regenbuffer address. Each compatible PC-BIOS behaves so.
* This is ugly, but there is no screen buffer address in the BIOS-DATA
* at 0x400. (Hans)
*/
unsigned base = IS_SCREENMODE_MDA ? MDA_VIRT_TEXT_BASE : VGA_VIRT_TEXT_BASE;
return base + page * READ_WORD(BIOS_VIDEO_MEMORY_USED);
}
/* this maps the cursor shape given by int10, fn1 to the actually
displayed cursor start&end values in cursor_shape. This seems
to be typical IBM Black Compatibility Magic.
I modeled it approximately from the behaviour of my own
VGA's BIOS.
I'm not sure if it is correct for start=end and for font_heights
other than 16.
*/
#define i10_msg(x...) v_printf("INT10: " x)
#if DEBUG_INT10 >= 1
#define i10_deb(x...) v_printf("INT10: " x)
#else
#define i10_deb(x...)
#endif
static void tty_char_out(unsigned char ch, int s, int attr);
static void vga_ROM_to_RAM(unsigned height, int bank);
static void crt_outw(unsigned index, unsigned value)
{
unsigned port = READ_WORD(BIOS_VIDEO_PORT);
port_outw(port, index | (value & 0xff00));
port_outw(port, (index + 1) | ((value & 0xff) << 8));
}
static unsigned do_set_cursor_pos(unsigned page, int x, int y)
{
unsigned co, old_y;
old_y = get_bios_cursor_y_position(page);
set_bios_cursor_x_position(page, x);
set_bios_cursor_y_position(page, y);
co = READ_WORD(BIOS_SCREEN_COLUMNS);
crt_outw(0xe, READ_WORD(BIOS_VIDEO_MEMORY_ADDRESS)/2 + y * co + x);
return old_y;
}
static void set_cursor_pos(unsigned page, int x, int y)
{
unsigned old_y = do_set_cursor_pos(page, x, y);
if (config.dumb_video && y > old_y) {
int i;
if (no_local_video && !config.tty_stderr)
return;
for (i = 0; i < y - old_y; i++)
fputs("\r\n", config.tty_stderr ? stderr : stdout);
}
}
static void set_cursor_shape(uint16_t shape)
{
int cs,ce;
cshape cursor_shape;
cursor_shape.w = shape;
WRITE_WORD(BIOS_CURSOR_SHAPE, cursor_shape.w);
cs=CURSOR_START(cursor_shape) & 0x1F;
ce=CURSOR_END(cursor_shape) & 0x1F;
if (cursor_shape.w & 0x6000 || cs>ce) {
i10_deb("no cursor\n");
crt_outw(0xa, NO_CURSOR);
return;
}
cs&=0x0F;
ce&=0x0F;
if (ce>3 && ce<12 && (config.cardtype != CARD_MDA)) {
int vga_font_height = READ_WORD(BIOS_FONT_HEIGHT);
if (cs>ce-3) cs+=vga_font_height-ce-1;
else if (cs>3) cs=vga_font_height/2;
ce=vga_font_height-1;
}
i10_msg("mapped cursor: start %d, end %d\n", cs, ce);
CURSOR_START(cursor_shape)=cs;
CURSOR_END(cursor_shape)=ce;
crt_outw(0xa, cursor_shape.w);
}
/* This is a better scroll routine, mostly for aesthetic reasons. It was
* just too horrible to contemplate a scroll that worked 1 character at a
* time :-)
*
* It may give some performance improvement on some systems (it does
* on mine) (Andrew Tridgell)
*/
static void
bios_scroll(int x0, int y0, int x1, int y1, int l, int att)
{
int dx = x1 - x0 + 1;
int dy = y1 - y0 + 1;
int x, y, co, li;
uint16_t blank = ' ' | (att << 8);
uint16_t tbuf[MAX_COLUMNS];
unsigned sadr;
if (config.dumb_video)
return;
li= READ_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1) + 1;
co= READ_WORD(BIOS_SCREEN_COLUMNS);
sadr = screen_adr(0) + READ_WORD(BIOS_VIDEO_MEMORY_ADDRESS);
if (sadr < VGA_PHYS_TEXT_BASE && ((att & 7) != 0) && ((att & 7) != 7))
{
blank = ' ' | ((att | 7) << 8);
}
if (x1 >= co || y1 >= li)
{
v_printf("VID: Scroll parameters out of bounds, in Scroll!\n");
v_printf("VID: Attempting to fix with clipping!\n");
/* kludge for ansi.sys' clear screen - we'd better do real clipping */
/* Also a cludge to fix list, but in the other dimension */
if (x1 >= co) x1 = co -1;
if (y1 >= li) y1 = li -1;
dx = x1 - x0 +1;
dy = y1 - x0 +1;
}
if (dx <= 0 || dy <= 0 || x0 < 0 || x1 >= co || y0 < 0 || y1 >= li)
{
v_printf("VID: Scroll parameters impossibly out of bounds, giving up!\n");
return;
}
/* make a blank line */
for (x = 0; x < dx; x++)
tbuf[x] = blank;
if (l >= dy || l <= -dy)
l = 0;
if (l == 0) { /* Wipe mode */
for (y = y0; y <= y1; y++)
memcpy_to_vga(sadr + 2 * (y * co + x0), tbuf, dx * 2);
return;
}
if (l > 0) {
if (dx == co)
vga_memcpy(sadr + 2 * y0 * co, sadr + 2 * (y0 + l) * co, (dy - l) * dx * 2);
else
for (y = y0; y <= (y1 - l); y++)
vga_memcpy(sadr + 2 * (y * co + x0), sadr + 2 * ((y + l) * co + x0), dx * 2);
for (y = y1 - l + 1; y <= y1; y++)
memcpy_to_vga(sadr + 2 * (y * co + x0), tbuf, dx * 2);
}
else {
for (y = y1; y >= (y0 - l); y--)
vga_memcpy(sadr + 2 * (y * co + x0), sadr + 2 * ((y + l) * co + x0), dx * 2);
for (y = y0 - l - 1; y >= y0; y--)
memcpy_to_vga(sadr + 2 * (y * co + x0), tbuf, dx * 2);
}
}
static int using_text_mode(void)
{
unsigned char mode = READ_BYTE(BIOS_VDU_CONTROL);
return (!(mode & 2));
}
static int using_mono_mode(void)
{
return ((READ_BYTE(BIOS_VDU_CONTROL) & 0xc) == 0xc);
}
/*
* Output a character to the screen.
* If attr != -1, set the attribute byte, too.
*/
void tty_char_out(unsigned char ch, int s, int attr)
{
int xpos, ypos, co, li;
int gfx_mode = 0;
unsigned dst;
/* i10_deb("tty_char_out: char 0x%02x, page %d, attr 0x%02x\n", ch, s, attr); */
if (config.dumb_video) {
struct char_set_state term_state;
t_unicode uni = dos_to_unicode_table[ch];
unsigned char buff[MB_LEN_MAX + 1];
int num, i;
if (no_local_video && !config.tty_stderr)
return;
init_charset_state(&term_state, trconfig.output_charset);
num = unicode_to_charset(&term_state, uni, buff, MB_LEN_MAX);
if (num <= 0)
return;
for (i = 0; i < num; i++)
fputc(buff[i], config.tty_stderr ? stderr : stdout);
}
li= READ_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1) + 1;
co= READ_WORD(BIOS_SCREEN_COLUMNS);
xpos = get_bios_cursor_x_position(s);
ypos = get_bios_cursor_y_position(s);
/* check for gfx mode */
if(!using_text_mode()) gfx_mode = 1;
switch (ch) {
case '\r': /* Carriage return */
xpos = 0;
break;
case '\n': /* Newline */
ypos++;
break;
case 8: /* Backspace */
if (xpos > 0) xpos--;
break;
case '\t': /* Tab */
i10_deb("char_out: tab\n");
do {
tty_char_out(' ', s, -1);
xpos = get_bios_cursor_x_position(s);
} while (xpos % 8 != 0);
break;
case 7: /* Bell */
speaker_on(125, 0x637);
return;
default: /* Printable character */
if(gfx_mode) {
vgaemu_put_char(ch, s, attr);
}
else
{
dst = screen_adr(s) + 2 * (ypos*co + xpos);
WRITE_BYTE(dst, ch);
if(attr != -1) WRITE_BYTE(dst + 1, attr);
}
xpos++;
}
if (xpos == co) {
xpos = 0;
ypos++;
}
if (ypos == li) {
ypos--;
if(gfx_mode)
vgaemu_scroll(0, 0, co - 1, li - 1, 1, 0);
else {
/* Scroll with color newline */
bios_scroll(0,0,co-1,li-1,1,
vga_read(screen_adr(s) + 2*(ypos*co + xpos) + 1));
}
}
do_set_cursor_pos(s, xpos, ypos);
}
/* The following clears the screen buffer. It does it only to the screen
* buffer. If in termcap mode, the screen will be cleared the next time
* restore_screen() is called.
*/
static void clear_screen(void)
{
unsigned schar;
u_short blank = ' ' | (7 << 8);
int lx, s;
if (config.dumb_video)
return;
v_printf("INT10: cleared screen: screen_adr %x\n", screen_adr(0));
for (schar = screen_adr(0), lx = 0; lx < 16*1024;
WRITE_WORD(schar, blank), lx++, schar+=2);
for (s = 0; s < 8; s++)
do_set_cursor_pos(s, 0, 0);
}
/* return number of vertical scanlines based on the bytes at
40:88 and 40:89 */
static int get_text_scanlines(void)
{
int info = READ_BYTE(BIOS_VIDEO_INFO_2);
v_printf("scanlines=%x\n", info);
if ((info & 0x80) && !(info & 0x10))
return 200;
if (!(info & 0x80) && !(info & 0x10))
return 350;
if (!(info & 0x80))
return 400;
return 480;
}
/* set number of vertical scanlines at the bytes at
40:88 and 40:89 */
static void set_text_scanlines(int lines)
{
int info = READ_BYTE(BIOS_VIDEO_INFO_2) & ~0x90;
if (lines == 200)
info |= 0x80;
else {
if (lines == 400)
info |= 0x10;
else if (lines == 480)
info |= 0x90;
}
v_printf("scanlines=%x %d\n", info, lines);
WRITE_BYTE(BIOS_VIDEO_INFO_2, info);
}
static int adjust_font_size(int vga_font_height)
{
/* RBIL says:
Recalculate: BIOS_FONT_HEIGHT, BIOS_ROWS_ON_SCREEN_MINUS_1, and
BIOS_VIDEO_MEMORY_USED.
Update CRTC registers 9 (for font height), A/B (cursor), 12 (display end),
14 (underline location) */
int li, text_scanlines;
ioport_t port;
if(vga_font_height == 0)
return 0;
i10_msg("adjust_font_size: font size %d lines\n", vga_font_height);
text_scanlines = (READ_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1) + 1) *
READ_WORD(BIOS_FONT_HEIGHT);
if (text_scanlines <= 200) text_scanlines = 200;
else if (text_scanlines <= 350) text_scanlines = 350;
else if (text_scanlines <= 400) text_scanlines = 400;
else text_scanlines = 480;
li = text_scanlines / vga_font_height;
if (li > MAX_LINES)
return 0;
text_scanlines = li * vga_font_height;
port = READ_WORD(BIOS_VIDEO_PORT);
port_outw(port, 0x12 | (((text_scanlines-1) & 0xff) << 8));
port_outb(port, 0x14);
port_outb(port + 1, (port_inb(port + 1) & ~0x1f) + vga_font_height);
port_outb(port, 9);
port_outb(port + 1, (port_inb(port + 1) & ~0x1f) + vga_font_height -1);
WRITE_WORD(BIOS_FONT_HEIGHT, vga_font_height);
if (using_mono_mode())
set_cursor_shape(0x0b0d);
else
set_cursor_shape(0x0607);
if (Video->setmode != NULL) {
/* otherwise we can't change li */
WRITE_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1, li - 1);
WRITE_WORD(BIOS_VIDEO_MEMORY_USED,
TEXT_SIZE(READ_WORD(BIOS_SCREEN_COLUMNS), li));
}
return 1;
}
/*
* set_video_mode() accepts both (S)VGA and VESA mode numbers
* It should be fully compatible with the old set_video_mode()
* function.
* Note: bit 16 in "mode" is used internally to indicate that
* nothing should be done except adjusting the font size.
* -- 1998/04/04 sw
*/
bool set_video_mode(int mode)
{
vga_mode_info *vmi;
int clear_mem = 1;
unsigned u;
int co, li, vga_font_height, orig_mode;
ioport_t port;
if (config.dumb_video) {
i10_msg("set_video_mode: no video!\n");
return 0;
}
i10_msg("set_video_mode: mode 0x%02x\n", mode);
if((vmi = vga_emu_find_mode(mode, NULL)) == NULL) {
i10_msg("set_video_mode: undefined video mode\n");
return 0;
}
if (vmi->mode_class == TEXT) {
vga_mode_info *vmi2 = vmi;
vga_mode_info *vmi_best = vmi;
int ts = get_text_scanlines();
int delta = ts - vmi2->height;
i10_msg("look for mode with scan=%i font_height=%i\n",
ts, READ_BYTE(BIOS_FONT_HEIGHT));
while ((vmi2 = vga_emu_find_mode(mode, vmi2))) {
if (vmi2->height > ts ||
vmi2->char_height != READ_BYTE(BIOS_FONT_HEIGHT))
continue;
if (delta < 0 || ts - vmi2->height < delta) {
i10_msg("better mode found: %ix%i %ix%i scan=%i\n",
vmi2->text_width, vmi2->text_height,
vmi2->char_width, vmi2->char_height,
vmi2->height);
vmi_best = vmi2;
delta = ts - vmi2->height;
}
vmi = vmi_best;
}
}
if (vmi->mode_class == GRAPH && config.term) {
error("Cannot set graphics mode under terminal!\n");
return 0;
}
if (!memcheck_is_reserved(vmi->buffer_start << 4, 0x8000, 'v')) {
error("VGA: cannot set mode %i because of UMB at 0x%x\n",
mode, vmi->buffer_start);
return 0;
}
orig_mode = mode;
if(mode >= 0x80 && mode < 0x100) {
mode &= 0x7f;
clear_mem = 0;
}
if(mode & 0x8000) {
mode &= ~0x8000;
clear_mem = 0;
}
WRITE_BYTE(BIOS_CURRENT_SCREEN_PAGE, 0);
WRITE_WORD(BIOS_VIDEO_MEMORY_ADDRESS, 0);
WRITE_BYTE(BIOS_VIDEO_INFO_0, clear_mem ? 0x60 : 0xe0);
MEMSET_DOS(0x450, 0, 0x10); /* equiv. to set_bios_cursor_(x/y)_position(0..7, 0) */
if(config.cardtype == CARD_MDA) mode = 7;
if(vmi->type == TEXT_MONO) {
WRITE_BYTE(BIOS_CONFIGURATION, READ_BYTE(BIOS_CONFIGURATION) | 0x30);
port = 0x3b4;
} else {
WRITE_BYTE(BIOS_CONFIGURATION,
(READ_BYTE(BIOS_CONFIGURATION) & ~0x30) | 0x20);
port = 0x3d4;
}
WRITE_WORD(BIOS_VIDEO_PORT, port);
/*
* We store the SVGA mode number (if possible) even when setting
* a VESA mode.
* Note that this gives mode 0x7f if no VGA mode number
* had been assigned. -- sw
*/
WRITE_BYTE(BIOS_VIDEO_MODE, vmi->VGA_mode & 0x7f);
if (mode == 3)
WRITE_BYTE(BIOS_VDU_CONTROL, 9);
else if (vmi->type == TEXT_MONO)
WRITE_BYTE(BIOS_VDU_CONTROL, 0xc);
else if (vmi->mode_class == TEXT)
WRITE_BYTE(BIOS_VDU_CONTROL, 8);
else
WRITE_BYTE(BIOS_VDU_CONTROL, 0xa);
if (Video->setmode != NULL) {
li = vmi->text_height;
co = vmi->text_width;
} else {
li = READ_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1) + 1;
co = READ_WORD(BIOS_SCREEN_COLUMNS);
}
video_mode = orig_mode;
#if USE_DUALMON
/*
* The following code (& comment) is taken literally
* from the old set_video_mode(). Don't know what it's
* for (or if it works). -- 1998/04/04 sw
*/
/*
* This to be sure in case of older DOS programs probing HGC.
* There was no secure way to detect a HGC before VGA was invented.
* ( Now we can do INT 10, AX=1A00 ).
* Some older DOS programs do it by modifying EQUIP-flags
* and then let the BIOS say, if it can ?!?!)
* If we have config.dualmon, this happens legally.
*/
if(vmi->type == TEXT_MONO && config.dualmon)
vga_emu_setmode(7, co, li);
else
#endif
/* setmode needs video_mode to _still have_ the memory-clear bit -- sw */
vga_emu_setmode(mode, co, li);
/*
* video_mode is expected to be the mode number _without_ the
* memory-clear bit
* -- sw
*/
video_mode = mode;
if(clear_mem && using_text_mode()) clear_screen();
if (mode == 0x6)
WRITE_BYTE(BIOS_VDU_COLOR_REGISTER, 0x3f);
else if (mode <= 0x7 || vmi->type == TEXT_MONO)
WRITE_BYTE(BIOS_VDU_COLOR_REGISTER, 0x30);
vga_font_height = vmi->char_height;
if (li <= MAX_LINES) {
if(using_text_mode()) {
port_outb(port, 9);
port_outb(port + 1, (port_inb(port + 1) & ~0x1f) + vga_font_height -1);
/* adjust number of scanlines in the CRT; setmode set it at 400 */
if (vmi->height == 200) {
/* just set doublescan */
port_outb(port + 1, 0x80 | port_inb(port + 1));
} else if (vmi->height != 400) {
/* adjust display end */
port_outw(port, 0x12 | (((vmi->height-1) & 0xff) << 8));
}
WRITE_WORD(BIOS_VIDEO_MEMORY_USED, TEXT_SIZE(co, li));
} else {
unsigned page_size = roundUpToNextPowerOfTwo(
(vga.scan_len * vga.height) | 0xfff);
if (page_size > vga.mem.bank_pages * 4096)
page_size = vga.mem.bank_pages * 4096;
WRITE_WORD(BIOS_VIDEO_MEMORY_USED, page_size);
}
WRITE_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1, li - 1);
WRITE_WORD(BIOS_SCREEN_COLUMNS, co);
}
switch(vga_font_height) {
case 14:
u = vgaemu_bios.font_14;
break;
case 16:
u = vgaemu_bios.font_16;
break;
default:
u = vgaemu_bios.font_8;
}
SETIVEC(0x43, 0xc000, u);
WRITE_WORD(BIOS_FONT_HEIGHT, vga_font_height); // before set_cursor_shape()
set_cursor_shape(vmi->type == TEXT_MONO ? 0x0b0d : 0x0607);
if (using_text_mode()) {
v_printf("INT10: X_set_video_mode: 8x%d ROM font -> bank 0\n",
vga_font_height);
vga_ROM_to_RAM(vga_font_height, 0); /* 0 is default bank */
i10_msg("activated font bank 0\n");
}
return 1;
}
/* get the active and alternate display combination code */
static void get_dcc(int *active_dcc, int *alternate_dcc)
{
int cur_video_combo = READ_BYTE(BIOS_VIDEO_COMBO);
#if USE_DUALMON
if (config.dualmon) {
if (IS_SCREENMODE_MDA) {
*active_dcc = MDA_VIDEO_COMBO; /* active display */
*alternate_dcc = cur_video_combo;
}
else {
*active_dcc = cur_video_combo; /* active display */
*alternate_dcc = MDA_VIDEO_COMBO;
}
return;
}
#endif
*active_dcc = cur_video_combo; /* active display */
*alternate_dcc = 0; /* no inactive display */
}
/* INT 10 AH=1B - FUNCTIONALITY/STATE INFORMATION (PS,VGA/MCGA) */
static void return_state(unsigned int statebuf) {
int active_dcc, alternate_dcc;
WRITE_WORD(statebuf, vgaemu_bios.functionality - 0xc0000);
WRITE_WORD(statebuf + 2, 0xc000);
/* store bios 0:449-0:466 at ofs 0x04 */
MEMCPY_DOS2DOS(statebuf + 0x04, 0x449, 0x466 - 0x449 + 1);
/* store bios 0:484-0:486 at ofs 0x22 */
MEMCPY_DOS2DOS(statebuf + 0x22, 0x484, 0x486 - 0x484 + 1);
/* correct number of rows-1 to number of rows at offset 0x22 */
WRITE_BYTE(statebuf + 0x22, READ_BYTE(statebuf + 0x22) + 1);
get_dcc(&active_dcc, &alternate_dcc);
WRITE_BYTE(statebuf + 0x25, active_dcc);
WRITE_BYTE(statebuf + 0x26, alternate_dcc);
WRITE_BYTE(statebuf + 0x27, 16); /* XXX number of colors, low byte */
WRITE_BYTE(statebuf + 0x28, 0); /* XXX number of colors, high byte */
WRITE_BYTE(statebuf + 0x29, 8); /* XXX number of pages supported */
WRITE_BYTE(statebuf + 0x2a, 2); /* XXX number of scanlines 0-3=200,350,400,480 */
WRITE_BYTE(statebuf + 0x2b, 0); /* XXX primary character block */
WRITE_BYTE(statebuf + 0x2c, 0); /* XXX secondary character block */
WRITE_BYTE(statebuf + 0x31, 3); /* video memory: 3 = 256K */
WRITE_BYTE(statebuf + 0x32, 0); /* XXX save pointer state flags */
MEMSET_DOS(statebuf + 0x33, 0, 13);
}
/* helpers for font processing - eric@coli.uni-sb.de 11/2002 */
/* only for TEXT mode: Otherwise, int 0x43 is used... */
static void vga_RAM_to_RAM(unsigned height, unsigned char chr, unsigned count,
unsigned seg, unsigned ofs, int bank)
{
unsigned char *dst;
unsigned i;
unsigned long src;
unsigned bankofs;
if (!count) {
v_printf("Tried to load 0 characters of font data???\n");
return;
}
src = seg;
src <<= 4;
src += ofs;
bankofs = ((bank & 3) << 1) + ((bank & 4) >> 2);
bankofs <<= 13; /* unit is 8k */
i10_msg("load 8x%d font (char %d..%d) 0x%04x:0x%04x -> bank %d\n",
height, chr, chr+count-1, seg, ofs, bank);
dst = vga.mem.base + 0x20000 + bankofs;
/* copy count characters of height bytes each to vga_font_mem */
for(i = chr; i < chr + count; i++) {
MEMCPY_2UNIX(dst + i * 32, src + (i - chr) * height, height);
if (height < 32)
memset(dst + i * 32 + height, 0, 32 - height);
}
vga.reconfig.mem = 1;
}
static void vga_ROM_to_RAM(unsigned height, int bank)
{
unsigned seg, ofs;
seg = 0xc000;
switch (height) { /* ALTERNATE ROM fonts not yet usable! */
case 8:
ofs = vgaemu_bios.font_8;
break;
case 14:
ofs = vgaemu_bios.font_14;
break;
case 16:
ofs = vgaemu_bios.font_16;
break;
default:
v_printf("Error! Tried to load 8x%d ROM font!?\n",height);
ofs = vgaemu_bios.font_16;
}
vga_RAM_to_RAM(height,0,256,seg,ofs,bank);
memcpy(vga.backup_font, vga.mem.base + 0x20000, 256 * 32);
}
/******************************************************************/
/* the actual int10 handler */
int int10(void) /* with dualmon */
{
/* some code here is copied from Alan Cox ***************/
int x, y, co, li;
unsigned page, page_size, address;
unsigned sm;
#if 0 && USE_DUALMON
static int last_equip=-1;
if (config.dualmon && (last_equip != BIOS_CONFIG_SCREEN_MODE)) {
v_printf("VID: int10 entry, equip-flags=0x%04x\n",READ_WORD(BIOS_CONFIGURATION));
last_equip = BIOS_CONFIG_SCREEN_MODE;
if (IS_SCREENMODE_MDA) Video->update_screen = NULL;
else Video->update_screen = Video_default->update_screen;
}
#endif
li= READ_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1) + 1;
co= READ_WORD(BIOS_SCREEN_COLUMNS);
if (debug_level('v') >= 3)
{
if (debug_level('v') >= 4)
i10_msg("near %04x:%08x\n", READ_SEG_REG(cs), REG(eip));
if ( (LO(ax) >= ' ') && (LO(ax) < 0x7f) )
i10_msg("AH=%02x AL=%02x '%c'\n",
HI(ax), LO(ax), LO(ax));
else
i10_msg("AH=%02x AL=%02x\n", HI(ax), LO(ax));
}
#if 0
i10_msg("ax %04x, bx %04x\n",LWORD(eax), LWORD(ebx));
#endif
switch(HI(ax)) {
case 0x00: /* set video mode */
i10_msg("set video mode: 0x%x\n", LO(ax));
if(!set_video_mode(LO(ax))) {
i10_msg("set_video_mode() failed\n");
}
break;
case 0x01: /* set text cursor shape */
i10_deb("set text cursor shape: %u-%u\n", HI(cx), LO(cx));
set_cursor_shape(LWORD(ecx));
break;
case 0x02: /* set cursor pos */
page = HI(bx);
x = LO(dx);
y = HI(dx);
i10_deb("set cursor pos: page %d, x.y %d.%d\n", page, x, y);
if(page > 7) {
i10_msg("set cursor pos: page > 7: %d\n", page);
return 1;
}
if (x >= co || y >= li) {
/* some apps use this to hide the cursor,
* we move it 1 char behind the visible part
*/
x = co;
y = li -1;
}
set_cursor_pos(page, x, y);
break;
case 0x03: /* get cursor pos/shape */
/* output start & end scanline even if the requested page is invalid */
LWORD(ecx) = READ_WORD(BIOS_CURSOR_SHAPE);
page = HI(bx);
if (page > 7) {
LWORD(edx) = 0;
i10_msg("get cursor pos: page > 7: %d\n", page);
} else {
LO(dx) = get_bios_cursor_x_position(page);
HI(dx) = get_bios_cursor_y_position(page);
}
i10_deb(
"get cursor pos: page %u, x.y %u.%u, shape %u-%u\n",
page, LO(dx), HI(dx), HI(cx), LO(cx)
);
break;
case 0x04: /* read light pen pos */
i10_msg("read light pen pos: NOT IMPLEMENTED\n");
HI(ax) = 0; /* "light pen switch not pressed" */
/* This is how my VGA BIOS behaves [rz] */
break;
case 0x05: /* set active display page */
#if USE_DUALMON
if (config.dualmon && IS_SCREENMODE_MDA) break;
#endif
page = LO(ax);
i10_deb("set display page: from %d to %d\n", READ_BYTE(BIOS_CURRENT_SCREEN_PAGE), page);
if(page > max_page) {
i10_msg("set display page: bad page %d\n", page);
break;
}
page_size = READ_WORD(BIOS_VIDEO_MEMORY_USED);
address = page_size * page;
crt_outw(0xc, address/(using_text_mode() ? 2 : 1));
WRITE_BYTE(BIOS_CURRENT_SCREEN_PAGE, page);
WRITE_WORD(BIOS_VIDEO_MEMORY_ADDRESS, address);
x = get_bios_cursor_x_position(page);
y = get_bios_cursor_y_position(page);
set_cursor_pos(page, x, y);
break;
case 0x06: /* scroll up */
reset_idle(0);
i10_deb(
"scroll up: %u lines, area %u.%u-%u.%u, attr 0x%02x\n",
LO(ax), LO(cx), HI(cx), LO(dx), HI(dx), HI(bx)
);
if(using_text_mode()) {
bios_scroll(LO(cx), HI(cx), LO(dx), HI(dx), LO(ax), HI(bx));
}
else {
vgaemu_scroll(LO(cx), HI(cx), LO(dx), HI(dx), LO(ax), HI(bx));
}
break;
case 0x07: /* scroll down */
reset_idle(0);
i10_deb(
"scroll dn: %u lines, area %u.%u-%u.%u, attr 0x%02x\n",
LO(ax), LO(cx), HI(cx), LO(dx), HI(dx), HI(bx)
);
if(using_text_mode()) {
bios_scroll(LO(cx), HI(cx), LO(dx), HI(dx), -LO(ax), HI(bx));
}
else {
vgaemu_scroll(LO(cx), HI(cx), LO(dx), HI(dx), -LO(ax), HI(bx));
}
break;
case 0x08: /* read character & attr at x,y */
page = HI(bx);
if (page > max_page) {
i10_msg("read char: invalid page %d\n", page);
break;
}
if(using_text_mode()) {
sm = screen_adr(page);
LWORD(eax) = vga_read_word(sm + (co * get_bios_cursor_y_position(page)
+ get_bios_cursor_x_position(page)) * 2);
}
else {
LWORD(eax) = 0;
}
i10_deb(
"read char: char(%d.%d) = 0x%02x, attr 0x%02x\n",
get_bios_cursor_x_position(page), get_bios_cursor_y_position(page),
LO(ax), HI(ax)
);
break;
/* these two put literal character codes into memory, and do
* not scroll or move the cursor...
* the difference is that 0xA ignores color for text modes
*/
case 0x09: /* write char & attr */
reset_idle(0);
i10_deb(
"rep char: page %u, char 0x%02x '%c', attr 0x%02x\n",
HI(bx), LO(ax), LO(ax) > ' ' && LO(ax) < 0x7f ? LO(ax) : ' ', LO(bx)
);
if (config.dumb_video) {
FILE *f = config.tty_stderr ? stderr : stdout;
int i;
if (no_local_video && !config.tty_stderr)
break;
for (i = 0; i < LWORD(ecx); i++)
fputc(LO(ax), f);
/* cursor must not move when printing */
fputc('\r', f);
} else {
vgaemu_repeat_char_attr(LO(ax), HI(bx), LO(bx), LWORD(ecx));
}
break;
case 0x0a: /* write char */
reset_idle(0);
i10_deb(
"rep char: page %u, char 0x%02x '%c'\n",
HI(bx), LO(ax), LO(ax) > ' ' && LO(ax) < 0x7f ? LO(ax) : ' '
);
vgaemu_repeat_char(LO(ax), HI(bx), LO(bx), LWORD(ecx));
break;
case 0x0b: /* set palette/bg/border color */
{
unsigned char currentpalette = READ_BYTE(BIOS_VDU_COLOR_REGISTER);
i10_msg("set palette or bg/border, bx=%x\n",LWORD(ebx));
if (HI(bx) == 0) {
currentpalette &= ~0x1f;
currentpalette |= LO(bx) & 0x1f;
} else if (HI(bx) == 1) {
if (LO(bx))
currentpalette |= 0x20;
else
currentpalette &= ~0x20;
} else {
break;
}
Misc_set_color_select(currentpalette);
WRITE_BYTE(BIOS_VDU_COLOR_REGISTER, currentpalette);
}
break;
case 0x0c: /* write pixel */
reset_idle(0);
if(!using_text_mode())
vgaemu_put_pixel(LWORD(ecx), LWORD(edx), HI(bx), LO(ax));
break;
case 0x0d: /* read pixel */
LO(ax) = 0;
if(!using_text_mode()) {
LO(ax) = vgaemu_get_pixel(LWORD(ecx), LWORD(edx), HI(bx));
i10_msg("read pixel: 0x%02x\n", LO(ax));
}
break;
case 0x0e: /* print char */
reset_idle(0);
if(using_text_mode()) {
i10_deb(
"tty put char: page %u, char 0x%02x '%c'\n",
HI(bx), LO(ax), LO(ax) > ' ' && LO(ax) < 0x7f ? LO(ax) : ' '
);
tty_char_out(LO(ax), READ_BYTE(BIOS_CURRENT_SCREEN_PAGE), -1);
}
else {
i10_deb(
"tty put char: page %u, char 0x%02x '%c', attr 0x%02x\n",
HI(bx), LO(ax), LO(ax) > ' ' && LO(ax) < 0x7f ? LO(ax) : ' ', LO(bx)
);
tty_char_out(LO(ax), READ_BYTE(BIOS_CURRENT_SCREEN_PAGE), LO(bx));
}
break;
case 0x0f: /* get video mode */
/* EGA, VGA, and UltraVision return either AL=03h (color) or AL=07h
(monochrome) in all extended-row text modes */
if (using_text_mode() && li > 25)
LO(ax) = (using_mono_mode() ? 7 : 3);
else
LO(ax) = READ_BYTE(BIOS_VIDEO_MODE) |
(READ_BYTE(BIOS_VIDEO_INFO_0) & 0x80);
HI(ax) = co & 0xff;
HI(bx) = READ_BYTE(BIOS_CURRENT_SCREEN_PAGE);
i10_deb(
"get video mode: mode 0x%02x, page %u, %u columns\n",
LO(ax), HI(bx), HI(ax)
);
break;
case 0x10: /* ega/vga palette functions */
i10_deb("ega/vga palette: sub function 0x%02x\n", LO(ax));
/* root@zaphod */
/* Palette register stuff. Only for the VGA emulator */
{
int i, count;
unsigned int src;
unsigned char index, m;
DAC_entry rgb;
switch(LO(ax)) {
case 0x00: /* Set Palette Register */
Attr_set_entry(LO(bx), HI(bx));
break;
case 0x01: /* Set Overscan Color */
Attr_set_entry(0x11, HI(bx));
break;
case 0x02: /* Set Palette & Overscan Color */
src = SEGOFF2LINEAR(SREG(es), LWORD(edx));
for(i = 0; i < 0x10; i++) Attr_set_entry(i, READ_BYTE(src + i));
Attr_set_entry(0x11, READ_BYTE(src + i));
break;
case 0x03: /* Toggle Intensity/Blinking Bit */
m = Attr_get_entry(0x10) & ~(1 << 3);
m |= (LO(bx) & 1) << 3;
Attr_set_entry(0x10, m);
break;
case 0x07: /* Read Palette Register */
HI(bx) = Attr_get_entry(LO(bx));
break;
case 0x08: /* Read Overscan Color */
HI(bx) = Attr_get_entry(0x11);
break;
case 0x09: /* Read Palette & Overscan Color */
src = SEGOFF2LINEAR(SREG(es), LWORD(edx));
for(i = 0; i < 0x10; i++) WRITE_BYTE(src + i, Attr_get_entry(i));
WRITE_BYTE(src + i, Attr_get_entry(0x11));
break;
case 0x10: /* Set Individual DAC Register */
DAC_set_entry(LO(bx), HI(dx), HI(cx), LO(cx));
break;
case 0x12: /* Set Block of DAC Registers */
index = LO(bx);
count = LWORD(ecx);
src = SEGOFF2LINEAR(SREG(es), LWORD(edx));
for(i = 0; i < count; i++, index++)
DAC_set_entry(index, READ_BYTE(src + 3*i),
READ_BYTE(src + 3*i + 1), READ_BYTE(src + 3*i + 2));
break;
case 0x13: /* Select Video DAC Color Page */
m = Attr_get_entry(0x10);
switch(LO(bx)) {
case 0: /* Select Page Mode */
m &= ~(1 << 7);
m |= (HI(bx) & 1) << 7;
Attr_set_entry(0x10, m);
break;
case 1: /* Select Page */
if(m & (1 << 7))
Attr_set_entry(0x14, HI(bx) & 0xf);
else
Attr_set_entry(0x14, (HI(bx) & 0x3) << 2);
break;
}
break;
case 0x15: /* Read Individual DAC Register */
DAC_get_entry(&rgb, LO(bx));
HI(dx) = rgb.r; HI(cx) = rgb.g; LO(cx) = rgb.b;
break;
case 0x17: /* Read Block of DAC Registers */
index = LO(bx);
count = LWORD(ecx);
src = SEGOFF2LINEAR(SREG(es), LWORD(edx));
for(i = 0; i < count; i++, index++) {
DAC_get_entry(&rgb, index);
WRITE_BYTE(src + 3*i, rgb.r);
WRITE_BYTE(src + 3*i + 1, rgb.g);
WRITE_BYTE(src + 3*i + 2, rgb.b);
}
break;
case 0x18: /* Set PEL Mask */
DAC_set_pel_mask(LO(bx));
break;
case 0x19: /* Read PEL Mask */
LO(bx) = DAC_get_pel_mask();
break;
case 0x1a: /* Get Video DAC Color Page */
LO(bx) = m = (Attr_get_entry(0x10) >> 7) & 1;
HI(bx) = (Attr_get_entry(0x14) & 0xf) >> (m ? 0 : 2);
break;
case 0x1b: /* Convert to Gray */
for(index = LO(bx), count = LWORD(ecx); count--; index++)
DAC_rgb2gray(index);
break;
default:
i10_msg("ega/vga palette: invalid sub function 0x%02x\n", LO(ax));
break;
}
}
break;
case 0x11: /* character generator functions */
{
int vga_font_height;
unsigned ofs, seg;
unsigned rows, char_height;
i10_msg("char gen: func 0x%02x, bx 0x%04x\n", LO(ax), LWORD(ebx));
switch(LO(ax)) {
case 0x03:
/* For EGA: Select fonts xx and yy for attrib bit 3 1/0 */
/* For VGA: Same, but using Xxx and Yyy. BL is 00XYxxyy */
/* *** see also vgaemu_put_char() *** */
/* the X/Y bits mean 8k offset, xx/yy use 16k units */
/* see: sequencer, map select, data[3] ... sequemu.c */
Seq_set_index(3);
Seq_write_value(LO(bx));
i10_msg("sequencer char map select: 0x%02x\n", LO(bx));
break;
case 0x01: /* load 8x14 charset */
case 0x11:
vga_font_height = 14;
vga_ROM_to_RAM(14, LO(bx));
goto more_lines;
case 0x02: /* load 8x8 charset */
case 0x12:
vga_font_height = 8;
vga_ROM_to_RAM(8, LO(bx));
goto more_lines;
case 0x04: /* load 8x16 charset */
case 0x14:
vga_font_height = 16;
vga_ROM_to_RAM(16, LO(bx));
goto more_lines;
/* load a custom font */
case 0x00:
case 0x10:
vga_font_height = HI(bx);
/* *************************************************************
* func 00 would not change as much as func 0x10, which would *
* reprogram registers 9 = bh-1 (mode 7 only, max scan line), *
* a = bh-2 (cursor start), b = 0 (cursor end), *
* 12 = (rows+1) / (bh-1) (vertical display end), *
* 14 = bh-1 (underline loc). Recalcluates CRT buffer length. *
* Max character rows is also recalculated, as well as by/char *
* ... and page 0 must be active *
* ES:BP -> table CX = count of chars DX = from which char on *
* BL = which block to load into map2 BH = bytes / char *
************************************************************* */
vga_RAM_to_RAM(HI(bx), LO(dx), LWORD(ecx),
SREG(es), LWORD(ebp), LO(bx));
i10_msg("some user font data loaded\n");
goto more_lines;
more_lines:
/* Also activating the target bank - some programs */
/* seem to assume this. Sigh... */
Seq_set_index(3); /* sequencer: character map select */
/* available: x, y, c...!? transforming bitfields... */
x = (LO(bx) & 3) | ((LO(bx) & 4) << 2);
x |= ((LO(bx) & 3) << 2) | ((LO(bx) & 4) << 3);
Seq_write_value(x); /* set bank N for both fonts */
i10_msg("activated font bank %d (0x%02x)\n",
(LO(bx) & 7), x);
if(LO(ax) >= 0x10 && !adjust_font_size(vga_font_height))
v_printf("Problem changing font height %d->%d\n",
READ_WORD(BIOS_FONT_HEIGHT), vga_font_height);
break;
case 0x20: /* set 8x8 gfx chars */
SETIVEC(0x1f, SREG(es), LWORD(ebp));
i10_deb("set 8x8 gfx chars: addr 0x%04x:0x%04x\n", ISEG(0x1f), IOFF(0x1f));
break;
case 0x21: /* load user gfx chars */
case 0x22: /* load 14x8 gfx chars */
case 0x23: /* load 8x8 gfx chars */
case 0x24: /* load 16x8 gfx chars */
/* BL=0/1/2/3 for DL/14/25/43 rows */
/* CX is byte / char, ES:BP is pointer for case 0x21 */
seg = 0xc000;
switch(LO(ax)) {
case 0x21:
ofs = LWORD(ebp);
seg = SREG(es);
char_height = LWORD(ecx);
break;
case 0x22:
ofs = vgaemu_bios.font_14;
char_height = 14;
break;
case 0x23:
ofs = vgaemu_bios.font_8;
char_height = 8;
break;
default: /* case 0x24 */
ofs = vgaemu_bios.font_16;
char_height = 16;
break;
}
rows = LO(dx); /* gets changed for BL != 0 now: */
switch(LO(bx)) {
case 1:
rows = 14;
break;
case 2:
rows = 25;
break;
case 3:
rows = 43;
break;
}
SETIVEC(0x43, seg, ofs);
WRITE_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1, rows - 1);
WRITE_WORD(BIOS_FONT_HEIGHT, char_height);
/* Does NOT load any video font RAM */
/* This is for GRAPHICS mode only. No vga_R*... */
/* No setting of the CRTC font size either... */
i10_msg(
"load gfx font: height %u, rows %u, addr 0x%04x:0x%04x\n",
char_height, rows, seg, ofs
);
break;
case 0x30: /* get current character generator info */
LWORD(ecx) = READ_WORD(BIOS_FONT_HEIGHT);
LO(dx) = READ_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1);
seg = 0xc000;
switch((HI(bx))) {
case 0:
ofs = IOFF(0x1f);
seg = ISEG(0x1f);
break;
case 1:
ofs = IOFF(0x43);
seg = ISEG(0x43);
break;
case 2:
ofs = vgaemu_bios.font_14;
break;
case 3:
ofs = vgaemu_bios.font_8;
break;
case 4:
ofs = vgaemu_bios.font_8 + 128 * 8;
break;
case 5:
ofs = vgaemu_bios.font_14_alt;
break;
case 6:
ofs = vgaemu_bios.font_16;
break;
case 7:
ofs = vgaemu_bios.font_16_alt;
break;
default:
seg = ofs = 0;
}
LWORD(ebp) = ofs;
SREG(es) = seg;
i10_deb(
"font info: char height: %u, rows %u, font 0x%x, addr 0x%04x:0x%04x\n",
LWORD(ecx), LO(dx) + 1, HI(bx), SREG(es), LWORD(ebp)
);
break;
default:
i10_msg("char gen: func 0x%02x NOT IMPLEMENTED\n", LO(ax));
}
}
break;
case 0x12: /* alternate function select */
switch(LO(bx)) {
case 0x10: /* get ega info */
HI(bx) = READ_WORD(BIOS_VIDEO_PORT) == 0x3b4;
LO(bx) = 3;
HI(cx)=0xf; /* feature bits (no feature controller) */
LO(cx)=READ_BYTE(BIOS_VIDEO_INFO_1) & 0xf;
i10_deb("get ega info: bx 0x%04x, cx 0x%04x\n", LWORD(ebx), LWORD(ecx));
break;
case 0x20: /* select alternate print screen */
/* Would install a handler of the video */
/* BIOS that can do more modes than the main BIOS */
/* handler (which is often limited to 80x25 text) */
i10_deb("select alternate prtsc: NOT IMPLEMENTED\n");
break;
case 0x30: /* select vertical resolution */
{
static int scanlines[4] = {200, 350, 400, 480};
/* 480 (undocumented) (int 0x10 AH=12 BL=30) */
if((unsigned) LO(ax) < 4) {
int text_scanlines = scanlines[LO(ax)];
set_text_scanlines(text_scanlines);
LO(ax) = 0x12;
i10_deb("select vert res: %d lines", text_scanlines);
}
else {
i10_msg("select vert res: invalid arg 0x%02x", LO(ax));
}
}
break;
case 0x32: /* enable/disable cpu access to video ram */
/* FIXME: implement this XXX */
/* would probably modify port 0x3c2, misc output, which */
/* also is responsible for 3b4<->3d4, pixelclock, sync */
/* polarity, odd/even page selection, ... */
if(LO(ax) == 0)
i10_deb("disable cpu access to video (ignored)\n");
else
i10_deb("enable cpu access to video (ignored)\n");
break;
#if 0
case 0x34: /* enable/disable cursor emulation */
cursor_emulation = LO(ax);
LO(ax) = 0x12;
i10_deb("cursor emulation: %s\n", cursor_emulation & 1 ? "disabled" : "enabled");
break;
#endif
case 0x36: /* video screen on/off */
/* VGAEMU oriented (do port 3c0 flag as well?) */
/* (probably not: port 0x3c0|=0x20 -> all overscan color) */
/* FIXME: only useful when using VGAEMU, add a #define */
/* Will influence vga.config.video_off -> X_update_screen */
Seq_set_index(1); /* sequencer: clocking mode */
if(LO(ax) == 0) {
i10_deb("turn video screen on (partially ignored)\n");
Seq_write_value(vga.seq.data[1] & ~0x20);
/* bit 0x20 is screen refresh off */
} else {
i10_deb("turn video screen off (partially ignored)\n");
Seq_write_value(vga.seq.data[1] | 0x20);
/* bit 0x20 is screen refresh off */
}
#if 0
LO(ax) = 0x12;
#endif
break;
default:
i10_msg("video subsys config: function 0x%02x NOT IMPLEMENTED\n", LO(bx));
}
break;
case 0x13: /* write string */
{
int with_attr = (LO(ax) & 2) >> 1;
unsigned page = HI(bx);
unsigned char attr = LO(bx);
unsigned len = LWORD(ecx);
unsigned int str = SEGOFF2LINEAR(SREG(es), LWORD(ebp));
unsigned old_x, old_y;
reset_idle(0);
old_x = get_bios_cursor_x_position(page);
old_y = get_bios_cursor_y_position(page);
set_cursor_pos(page, LO(dx), HI(dx));
i10_deb(
"write string: page %u, x.y %d.%d, attr 0x%02x, len %u, addr 0x%04x:0x%04x\n",
page, LO(dx), HI(dx), attr, len, SREG(es), LWORD(ebp)
);
#if DEBUG_INT10
i10_deb("write string: str \"");
{
unsigned u;
for(u = 0; u < len; u++)
v_printf("%c", READ_BYTE(str+u) >= ' ' &&
READ_BYTE(str+u) < 0x7f ? READ_BYTE(str+u) : ' ');
v_printf("\"\n");
}
#endif
if(with_attr) {
while(len--) {
tty_char_out(READ_BYTE(str), page, READ_BYTE(str+1));
str += 2;
}
}
else {
while(len--) tty_char_out(READ_BYTE(str++), page, attr);
}
if(!(LO(ax) & 1)) { /* no cursor update */
set_cursor_pos(page, old_x, old_y);
}
}
break;
case 0x1a: /* get/set display combo */
if(LO(ax) == 0) {
int active_dcc, alternate_dcc;
LO(ax) = 0x1a; /* valid function=0x1a */
get_dcc(&active_dcc, &alternate_dcc);
LO(bx) = active_dcc;
HI(bx) = alternate_dcc;
i10_deb("get display combo: active 0x%02x, alternate 0x%2x\n", LO(bx), HI(bx));
}
else {
i10_msg("set display combo: NOT IMPLEMENTED\n");
}
break;
case 0x1b: /* functionality/state information */
if(LWORD(ebx) == 0) {
i10_deb("get functionality/state info\n");
return_state(SEGOFF2LINEAR(SREG(es), LWORD(edi)));
LO(ax) = 0x1b;
} else {
i10_msg("unknown functionality/state request: 0x%04x", LWORD(ebx));
}
break;
case 0x1c: { /* save/restore video state */
unsigned base = _BX;
if (LO(ax) > 2)
break;
switch(LO(ax)) {
case 0: {
unsigned size = 0;
i10_msg("save/restore: return state buffer size, cl=%x\n", LO(cx));
if (LO(cx) & 1) {
/* video hardware */
size += 0x46;
}
if (LO(cx) & 2) {
/* BIOS */
size += 96;
}
if (LO(cx) & 4) {
/* DAC */
size += 0x304;
}
LWORD(ebx) = (size + 63)/64;
break;
}
case 1:
i10_msg("save/restore: save state, cl=%x\n", LO(cx));
if (LO(cx) & 1) {
unsigned char buf[0x46];
unsigned crtc, ind;
/* select crtc base address */
crtc = (port_inb(MISC_OUTPUT_R) & 1) ? 0x3d4 : 0x3b4;
buf[0x0] = port_inb(SEQUENCER_INDEX);
buf[0x1] = port_inb(crtc);
buf[0x2] = port_inb(GFX_INDEX);
/* feature control */
buf[0x4] = port_inb(FEATURE_CONTROL_R);
for (ind = 1; ind < 5; ind++) {
port_outb(SEQUENCER_INDEX, ind);
buf[0x4+ind] = port_inb(SEQUENCER_DATA);
}
port_outb(SEQUENCER_INDEX, 0);
buf[0x9] = port_inb(SEQUENCER_DATA);
for (ind = 0; ind < 25; ind++) {
port_outb(crtc, ind);
buf[0x0a+ind] = port_inb(crtc + 1);
}
/* reset flipflop ! */
port_inb(crtc + 0x6);
buf[0x3] = port_inb(ATTRIBUTE_INDEX);
for (ind = 0; ind < 20; ind++) {
port_inb(crtc + 0x6);
port_outb(ATTRIBUTE_INDEX, ind);
buf[0x23 + ind] = port_inb(ATTRIBUTE_DATA);
}
port_inb(crtc + 0x6);
port_outb(ATTRIBUTE_INDEX, buf[0x3]);
port_inb(crtc + 0x6);
for (ind = 0; ind < 9; ind++) {
port_outb(GFX_INDEX, ind);
buf[0x37+ind] = port_inb(GFX_DATA);
}
buf[0x40] = crtc & 0xff;
buf[0x41] = crtc >> 8;
/* VGA latches */
memcpy(&buf[0x42], vga.latch, 4);
MEMCPY_2DOS(SEGOFF2LINEAR(_ES, base), buf, sizeof(buf));
base += sizeof(buf);
}
if (LO(cx) & 2) {
MEMCPY_DOS2DOS(SEGOFF2LINEAR(_ES, base), 0x449, 96);
base += 96;
}
if (LO(cx) & 4) {
unsigned char buf[0x304];
unsigned ind;
buf[0] = port_inb(DAC_STATE);
buf[1] = port_inb(DAC_WRITE_INDEX);
buf[2] = port_inb(DAC_PEL_MASK);
port_outb(DAC_READ_INDEX, 0x00);
for(ind = 0; ind < 768; ind++)
buf[0x3 + ind] = port_inb(DAC_DATA);
buf[0x303] = port_inb(COLOR_SELECT);
MEMCPY_2DOS(SEGOFF2LINEAR(SREG(es), base), buf, sizeof(buf));
base += sizeof(buf);
}
break;
case 2:
i10_msg("save/restore: restore state, cl=%x\n", LO(cx));
if (LO(cx) & 1) {
unsigned char buf[0x46];
unsigned crtc, ind;
MEMCPY_2UNIX(buf, SEGOFF2LINEAR(SREG(es), base), sizeof(buf));
base += sizeof(buf);
crtc = buf[0x40] | (buf[0x41] << 8);
for (ind = 1; ind < 5; ind++)
port_outw(SEQUENCER_INDEX, ind | (buf[0x04+ind] << 8));
port_outw(SEQUENCER_INDEX, buf[0x09] << 8);
/* disable write protection to index 0-7 */
port_outw(crtc, 0x0011);
for (ind = 0; ind < 25; ind++)
port_outw(crtc, ind | (buf[0x0a+ind] << 8));
/* select crtc base address */
port_outb(MISC_OUTPUT_W, (port_inb(MISC_OUTPUT_R) & ~0x01) | (crtc == 0x3d4));
/* reset flipflop ! */
port_inb(crtc + 0x6);
for (ind = 0; ind < 20; ind++) {
port_outb(ATTRIBUTE_INDEX, ind);
port_outb(ATTRIBUTE_INDEX, buf[0x23+ind]);
}
port_outb(ATTRIBUTE_INDEX, buf[0x3]);
port_inb(crtc + 0x6);
for (ind = 0; ind < 9; ind++)
port_outw(GFX_INDEX, ind | (buf[0x37+ind] << 8));
port_outb(SEQUENCER_INDEX, buf[0x0]);
port_outb(crtc, buf[0x1]);
port_outb(GFX_INDEX, buf[0x2]);
/* feature control */
port_outb(crtc + 0x6, buf[0x4]);
/* VGA latches */
memcpy(vga.latch, &buf[0x42], 4);
}
if (LO(cx) & 2) {
MEMCPY_DOS2DOS(0x449, SEGOFF2LINEAR(_ES, base), 96);
base += 96;
}
if (LO(cx) & 4) {
unsigned char buf[0x304];
unsigned ind;
MEMCPY_2UNIX(buf, SEGOFF2LINEAR(_ES, base), sizeof(buf));
base += sizeof(buf);
port_outb(DAC_PEL_MASK, buf[2]);
port_outb(DAC_WRITE_INDEX, 0x00);
for(ind = 0; ind < 768; ind++)
port_outb(DAC_DATA, buf[0x3 + ind]);
port_outb(COLOR_SELECT, buf[0x303]);
if (buf[0] & 3)
port_outb(DAC_READ_INDEX, buf[1]);
else
port_outb(DAC_WRITE_INDEX, buf[1]);
}
break;
}
LO(ax) = 0x1c;
break;
}
case 0x4f: /* vesa interrupt */
do_vesa_int();
break;
case 0xcc: /* called from NC 5.0 */
_CX = 0; _AL = 0xff;
i10_deb("obscure function 0x%02x\n", HI(ax));
break;
case 0xfe: /* get shadow buffer..return unchanged */
case 0xff: /* update shadow buffer...do nothing */
i10_deb("obscure function 0x%02x\n", HI(ax));
break;
default:
i10_msg("unknown video int 0x%04x\n", LWORD(eax));
break;
}
return 1;
}
void video_mem_setup(void)
{
int co, li;
WRITE_BYTE(BIOS_CURRENT_SCREEN_PAGE, 0);
li = LI;
co = CO;
if (config.term)
gettermcap(0, &co, &li);
WRITE_WORD(BIOS_SCREEN_COLUMNS, co); /* chars per line */
WRITE_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1, li - 1); /* lines on screen - 1 */
WRITE_WORD(BIOS_VIDEO_MEMORY_USED, TEXT_SIZE(co,li)); /* size of video regen area in bytes */
WRITE_WORD(BIOS_CURSOR_SHAPE, (bios_configuration&MDA_CONF_SCREEN_MODE)?0x0A0B:0x0607);
#if 0
/* This is needed in the video stuff. Grabbed from boot(). */
if ((bios_configuration & MDA_CONF_SCREEN_MODE) == MDA_CONF_SCREEN_MODE) {
WRITE_WORD(BIOS_VIDEO_PORT, 0x3b4); /* base port of CRTC - IMPORTANT! */
video_mode = 7;
} else {
WRITE_WORD(BIOS_VIDEO_PORT, 0x3d4); /* base port of CRTC - IMPORTANT! */
video_mode = 3;
}
WRITE_BYTE(BIOS_VIDEO_MODE, video_mode);
WRITE_BYTE(BIOS_VDU_CONTROL, 9); /* current 3x8 (x=b or d) value */
WRITE_WORD(BIOS_VIDEO_MEMORY_ADDRESS, 0);/* offset of current page in buffer */
WRITE_WORD(BIOS_FONT_HEIGHT, 16);
#endif
/* XXX - these are the values for VGA color!
should reflect the real display hardware. */
WRITE_BYTE(BIOS_VIDEO_INFO_0, 0x60);
WRITE_BYTE(BIOS_VIDEO_INFO_1, 0xF9);
WRITE_BYTE(BIOS_VIDEO_INFO_2, 0x51);
/* XXX - This usage isn't quite correct: this byte should be an index
into a table. Requires modification of our BIOS, and setting up
lots of tables*/
WRITE_BYTE(BIOS_VIDEO_COMBO, video_combo);
if (!config.vga) {
WRITE_DWORD(BIOS_VIDEO_SAVEPTR, 0); /* pointer to video table */
/* point int 1f to the default 8x8 graphics font for high characters */
SETIVEC(0x1f, 0xc000, vgaemu_bios.font_8 + 128 * 8);
}
else if (!config.vbios_post) {
Bit32u p, q;
Bit16u vc;
i10_msg("Now initialising 0x40:a8-ab\n");
WRITE_DWORD(BIOS_VIDEO_SAVEPTR, int_bios_area[BIOS_VIDEO_SAVEPTR/4]);
/* many BIOSes use this: take as fallback value */
WRITE_BYTE(BIOS_VIDEO_COMBO, 0xb);
/* correct BIOS_VIDEO_COMBO value */
p = READ_DWORD(BIOS_VIDEO_SAVEPTR) + 0x10;
/* [VGA only] ptr to Secondary Save Pointer Table, must be valid */
p = rFAR_PTR(Bit32u, p);
p = READ_DWORD(p) + 0x2;
/* ptr to Display Combination Code Table, must be valid */
p = rFAR_PTR(Bit32u, p);
p = READ_DWORD(p) + 0x4;
/* Each pair of bytes gives a valid display combination */
q = p = rFAR_PTR(Bit32u, p);
do {
vc = READ_WORD(q);
if (vc == video_combo || vc == (video_combo << 8)) {
WRITE_BYTE(BIOS_VIDEO_COMBO, (q-p)/2);
i10_msg("found video_combo: %x\n", (q-p)/2);
break;
}
q += 2;
} while ((vc & 0xff) < 0xd && vc < 0xd00);
}
}