还是十几年前的老代码,一个完整的DOS下文本方式直接写屏C++代码,本代码是在Borland C++ 3.1下编译的,因为其中有插入汇编码,其它C++编译器能否通过,就不得而知了,下面是代码:
#ifndef __CRTIO_HPP
#define __CRTIO_HPP
#define LINEBYTES 160
typedef unsigned char byte ;
typedef unsigned word;
typedef unsigned long dword;
class Crtio {
private :
static byte OldMode; // 初始化前的屏显模式
static byte CrtMode; // 当前屏显模式
static word CrtMemSeg; // 屏显内存段地址
static word CrtMemOff; // 屏显内存偏移地址
static byte CursorType; // 当前光标类型(改写 0, 插入非 0)
protected :
void Scroll( int , int , int , int , int , int );
// 计算保存屏幕矩形所需内存长度.
word RectSize( int , int , int , int );
// 保存屏幕矩形内容. 参数: 屏幕左上角行,列,右下角行,列, 缓冲区
void SaveRect( int , int , int , int , void far * );
// 恢复屏幕矩形内容到屏幕. 参数: 屏幕左上角行,列, 缓冲区
void RestRect( int , int , void far * );
// 显示字符串. 参数: 行, 起始列, 字符串, 结束列, 显示属性. 如显示字符超过
// 结束列截断, 返回实际显示的字符个数
int PutChars( int , int , char * , int , int );
// 返回所给行列位置的屏显偏移地址
static word CrtOff( int , int );
// 取字符属性. 参数: 彩显字符属性(如单显返回对应的单色字符属性, 否则返回参数本身)
static int GetAttr( int );
public :
// 初始化屏幕, 参数 = 0 清屏, 否则不清屏
static void Init( int = 0 );
// 恢复程序调用前的模式
void Close();
// 返回屏显段地址
word CrtSeg();
// 返回当前的屏显模式
byte Mode();
// 设置光标类型, 如当前为改写, 换为插入, 反之亦然
void SetCursorType();
// 返回当前光标类型
int GetCursorType();
// 移动光标. 参数: 行, 列
void SetPos( int , int );
// 取当前光标行列. 参数: 行, 列
void GetPos( int & , int & );
// 清屏. 参数: 屏幕左上角行,列,右下角行,列, 颜色
void Clear( int , int , int , int , int );
// 移行. 参数: 行数(0 清屏; >0 上移; <0 下移), 左上角行,列,右下角行,列, 空行属性
void RowRoll( int , int , int , int , int , int );
// 移列. 参数: 列数(0 清屏; >0 左移; <0 右移), 左上角行,列,右下角行,列, 空行属性
void ColRoll( int , int , int , int , int , int );
// 在当前光标处写字符. 参数: 字符, 显示个数, 显示属性
void PutChar( int , int , int );
// 返回所给行列位置的字符及属性, 低字节=字符, 高字节=属性. 参数: 行, 列
int GetChar( int , int );
// 隐蔽光标
void HideCursor();
};
inline word Crtio::CrtSeg()
{
return CrtMemSeg;
}
inline byte Crtio::Mode()
{
return CrtMode;
}
inline word Crtio::RectSize( int Row1, int Col1, int Row2, int Col2 )
{
return ((((Row2 - Row1 + 1 ) * (Col2 - Col1 + 1 )) << 1 ) + 4 );
}
inline int Crtio::GetCursorType()
{
return ( int )CursorType;
}
inline void Crtio::HideCursor()
{
SetPos( 25 , 0 );
}
#endif
// CRTIO.CPP
#pragma inline
#include " crtio.hpp "
#include " asmrules.h "
#include < dos.h >
byte Crtio::OldMode = 3 ;
byte Crtio::CrtMode = 3 ;
word Crtio::CrtMemSeg = 0xb800 ;
word Crtio::CrtMemOff = 0 ;
byte Crtio::CursorType = 0 ;
void Crtio::Init( int flag )
{
asm mov ax, 40h
asm mov es, ax
asm mov al, es:[49h]
asm mov DGROUP:@Crtio@OldMode, al
asm mov al, es:[ 61 ]
asm cmp al, 0
asm je initcur
asm mov al, 1
initcur:
asm mov DGROUP:@Crtio@CursorType, al
asm mov al, es:[10h]
asm and al, 00110000b
asm cmp al, 00110000b
asm je mda_yes
asm mov ax, 3
asm mov bx, 0b800h
asm jmp initcrtend
mda_yes:
asm mov ax, 7
asm mov bx, 0b000h
initcrtend:
asm mov DGROUP:@Crtio@CrtMemSeg, bx
asm mov DGROUP:@Crtio@CrtMode, al
asm cmp word ptr flag, 0
asm jne initcrtend1
asm cmp al, byte ptr DGROUP:@Crtio@OldMode
asm jne initcrtend0
asm push ds
asm xor si, si
asm mov di, 1000h
asm mov es, DGROUP:@Crtio@CrtMemSeg
asm push es
asm pop ds
asm mov cx, 2000
asm cld
asm rep movsw
asm pop ds
initcrtend0:
asm int 10h
initcrtend1:
}
void Crtio::Close()
{
asm mov al, DGROUP:@Crtio@OldMode
asm cbw
asm cmp al, byte ptr DGROUP:@Crtio@CrtMode
asm jne closecrtend
asm push ds
asm xor di, di
asm mov si, 1000h
asm mov es, DGROUP:@Crtio@CrtMemSeg
asm push es
asm pop ds
asm mov cx, 2000
asm cld
asm rep movsw
asm pop ds
asm xor bh, bh
asm mov ah, 2
asm mov dx, 1700h
closecrtend:
asm int 10h
if ( ! CursorType) SetCursorType();
}
void Crtio::SetCursorType()
{
CursorType = ! CursorType;
asm mov ah, 1
asm mov ch, 0
asm cmp byte ptr DGROUP:@Crtio@CursorType, 0
asm je cursortype1
asm mov ch, 0ch
cursortype1:
asm mov cl, 0dh
asm int 10h
}
void Crtio::SetPos( int row, int col )
{
CrtMemOff = CrtOff( row, col );
asm xor bh, bh
asm mov dh, byte ptr row
asm mov dl, byte ptr col
asm mov ah, 2
asm int 10h
}
void Crtio::GetPos( int & row, int & col )
{
row = CrtMemOff / LINEBYTES;
col = (CrtMemOff % LINEBYTES) >> 1 ;
}
void Crtio::PutChar( int chs, int n, int attr )
{
GetAttr( attr );
asm mov es, DGROUP:@Crtio@CrtMemSeg
asm mov di, DGROUP:@Crtio@CrtMemOff
asm mov cx, n
asm mov ah, al
asm mov al, byte ptr chs
asm cld
asm rep stosw
}
#pragma warn -rvl
word Crtio::CrtOff( int row, int col )
{
asm mov ax, row
asm mov cl, 4
asm shl ax, cl
asm mov bx, ax
asm shl ax, 1
asm shl ax, 1
asm add ax, bx
asm add ax, col
asm shl ax, 1
}
int Crtio::PutChars( int row, int startcol, char * s, int endcol, int attr )
{
SetPos( row, startcol );
GetAttr( attr );
asm mov es, DGROUP:@Crtio@CrtMemSeg
asm mov di, DGROUP:@Crtio@CrtMemOff
asm mov cx, endcol
asm sub cx, startcol
asm inc cx
asm push cx
asm mov ah, al
asm cld
pushDS_
asm LDS_ si, s
nextchs:
asm lodsb
asm cmp al, 32
asm jb putcharsend
asm stosw
asm loop nextchs
putcharsend:
popDS_
asm pop ax
asm sub ax, cx
}
int Crtio::GetChar( int row, int col )
{
CrtOff(row, col);
asm mov bx, ax
asm mov es, DGROUP:@Crtio@CrtMemSeg
asm mov ax, es:[bx]
}
int Crtio::GetAttr( int attr )
{
asm mov ax, attr
asm cmp word ptr DGROUP:@Crtio@CrtMemSeg, 0b000h
asm jne GetAttr1
asm mov cl, 4
asm shr al, cl
asm cmp al, 3
asm jbe GetAttr2
asm mov ax, 70h
asm jmp short GetAttr1
GetAttr2:
asm mov ax, 7
GetAttr1:
}
#pragma warn .rvl
void Crtio::SaveRect( int row1, int col1, int row2, int col2, void far * buf )
{
CrtOff( row1, col1 );
asm cld
asm mov si, ax
asm les di, dword ptr buf
asm mov ax, row2
asm sub ax, row1
asm inc ax
asm mov cx, ax
asm stosw
asm mov ax, col2
asm sub ax, col1
asm inc ax
asm mov dx, ax
asm stosw
asm push ds
asm mov ds, DGROUP:@Crtio@CrtMemSeg
getnextrow:
asm push cx
asm push si
asm mov cx, dx
asm rep movsw
asm pop si
asm pop cx
asm add si, LINEBYTES
asm loop getnextrow
asm pop ds
}
void Crtio::RestRect( int row, int col, void far * buf )
{
CrtOff( row, col );
asm mov di, ax
asm push ds
asm mov es, DGROUP:@Crtio@CrtMemSeg
asm lds si, dword ptr buf
asm cld
asm lodsw
asm add ax, row
asm cmp ax, 25
asm jle restcrt1
asm mov ax, 25
restcrt1:
asm sub ax, row
asm mov cx, ax
asm lodsw
asm mov dx, col
asm add dx, ax
asm cmp dx, 50h
asm jle restcrt2
asm mov dx, 50h
restcrt2:
asm sub dx, col
asm shl ax, 1
asm mov col, ax
putnextrow:
asm push cx
asm push si
asm push di
asm mov cx, dx
asm rep movsw
asm pop di
asm pop si
asm pop cx
asm add di, LINEBYTES
asm add si, col
asm loop putnextrow
asm pop ds
}
void Crtio::RowRoll( int n, int row1, int col1, int row2, int col2, int attr )
{
if ( n && row2 > row1 )
Scroll( 0 , row1, col1, row2, col2, n );
if ( n > 0 )
row1 = row2 + 1 - n;
else if ( n < 0 )
row2 = row1 - n - 1 ;
Clear( row1, col1, row2, col2, attr );
}
void Crtio::ColRoll( int n, int row1, int col1, int row2, int col2, int attr )
{
if ( n && col2 > col1 )
Scroll( 1 , row1, col1, row2, col2, n );
if ( n > 0 )
col1 = col2 + 1 - n;
else if ( n < 0 )
col2 = col1 - n - 1 ;
Clear( row1, col1, row2, col2, attr );
}
void Crtio::Scroll( int flag, int row1, int col1, int row2, int col2, int n )
{
int off1, off2, n1;
if ( ! flag )
{
if ( n < 0 ) n1 = row2;
else n1 = row1;
off1 = CrtOff( n1, col1 );
off2 = CrtOff( n1 + n, col1 );
}
else
{
if ( n < 0 ) n1 = col2;
else n1 = col1;
off1 = CrtOff( row1, n1 );
off2 = CrtOff( row1, n1 + n );
}
asm mov cx, row2
asm sub cx, row1
asm inc cx
asm mov dx, col2
asm sub dx, col1
asm inc dx
asm cld
asm mov ax, n
asm mov bx, LINEBYTES
asm cmp word ptr flag, 0
asm jne scroll0
asm cmp ax, 0
asm jg scroll1
asm neg ax
asm neg bx
scroll1:
asm sub cx, ax
asm jmp short scroll3
scroll0:
asm cmp ax, 0
asm jg scroll2
asm neg ax
asm std
scroll2:
asm sub dx, ax
scroll3:
asm mov es, DGROUP:@Crtio@CrtMemSeg
asm push ds
asm push es
asm pop ds
asm mov si, off2
asm mov di, off1
nextroll:
asm push cx
asm push si
asm push di
asm mov cx, dx
asm rep movsw
asm pop di
asm pop si
asm pop cx
asm add si, bx
asm add di, bx
asm loop nextroll
asm pop ds
}
void Crtio::Clear( int row1, int col1, int row2, int col2, int color )
{
CrtOff( row1, col1 );
asm mov di, ax
asm mov cx, row2
asm sub cx, row1
asm jns _clear1
asm neg cx
_clear1:
asm inc cx
asm mov dx, col2
asm sub dx, col1
asm jns _clear2
asm neg dx
_clear2:
asm inc dx
asm cld
asm mov es, DGROUP:@Crtio@CrtMemSeg
asm mov ah, color
asm xor al, al
nextcls:
asm push cx
asm push di
asm mov cx, dx
asm rep stosw
asm pop di
asm pop cx
asm add di, LINEBYTES
asm loop nextcls
}
Crtio类可以工作在彩显文本和单显文本模式下,当然,现在想找个单色显示器恐怕很难了,但是十几年前还是比比皆是,主要代码还是采用插入汇编。为了便于理解代码,这里对其中Crtio::Init函数做一些解释,该函数在判断当前显卡和显示模式以及设置文本方式时没有使用int 10h,而是直接使用了BIOS的数据,BIOS的数据区在段地址0040h处,其中的0040:0049地址处的一个字节保存了当前屏幕的显示模式,通过它可以获取和设置当前的屏显模式;0040:0010地址保存了计算机的一些设备信息,也是一个字节,其表示法如下:
1位,协处理器安装信息:0未安装,1安装
2位,PC / AT机保留位,PS / 2机为点设备安装信息
3位,PC机内存安装信息,其它机型保留
4 - 5位,显示方式, 00 :保留, 01 : 40 X 25彩色; 10 : 80 X 25彩色; 11 : 80 X 25单色
6 - 7位,软盘驱动器数
所以Crtio::Init函数通过以下代码对0040:0010字节的4-5位进行判断,是否单显或彩显,如果是单显,显示模式设置为7,屏显地址设置为0x0b00,否则显示模式设置为3,屏显地址设置为0x0800:
asm mov al, es:[10h]
asm and al, 00110000b
asm cmp al, 00110000b
直接写屏时,一个字符为2个字节,高字节为显示属性,即字符的前、背景颜色,低字节为要显示的字符。可能有人问,文本直接写屏能显示汉字吗?一般情况下,文本显示模式只能显示ASCIII码及扩展的ASCII码,但是,如果有影射写屏方式的中文DOS支持,如UCDOS,是可以显示汉字的,因为这种中文DOS表面看是使用的文本模式,内部却是使用的图形模式,如VGA彩显,内部就是使用12h图形模式,并对屏显地址进行了修改,看上去我们是对文本屏显地址0xb800直接写屏,其实都被影射到图形显存地址,使用它的图形字符画上去的。
声明,文章的代码是1995年前的东西,只能供初学者们借鉴参考。有错误或建议,请来信:maozefa@hotmail.com