还是十几年前的老代码,一个完整的DOS下文本方式直接写屏C++代码,本代码是在Borland C++ 3.1下编译的,因为其中有插入汇编码,其它C++编译器能否通过,就不得而知了,下面是代码:
#ifndef__CRTIO_HPP
#define __CRTIO_HPP
#define LINEBYTES160
typedefunsigned char byte ;
typedefunsignedword;
typedefunsigned long dword;
class Crtio{
private :
static byte OldMode; // 初始化前的屏显模式
static byte CrtMode; // 当前屏显模式
static wordCrtMemSeg; // 屏显内存段地址
static wordCrtMemOff; // 屏显内存偏移地址
static byte CursorType; // 当前光标类型(改写0,插入非0)
protected :
void Scroll( int , int , int , int , int , int );
// 计算保存屏幕矩形所需内存长度.
wordRectSize( 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 wordCrtOff( int , int );
// 取字符属性.参数:彩显字符属性(如单显返回对应的单色字符属性,否则返回参数本身)
static int GetAttr( int );
public :
// 初始化屏幕,参数=0清屏,否则不清屏
static void Init( int = 0 );
// 恢复程序调用前的模式
void Close();
// 返回屏显段地址
wordCrtSeg();
// 返回当前的屏显模式
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();
};
inlinewordCrtio::CrtSeg()
{
return CrtMemSeg;
}
inline byte Crtio::Mode()
{
return CrtMode;
}
inlinewordCrtio::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 ;
wordCrtio::CrtMemSeg = 0xb800 ;
wordCrtio::CrtMemOff = 0 ;
byte Crtio::CursorType = 0 ;
void Crtio::Init( int flag)
{
asmmovax,40h
asmmoves,ax
asmmoval,es:[49h]
asmmovDGROUP:@Crtio@OldMode,al
asmmoval,es:[ 61 ]
asmcmpal, 0
asmjeinitcur
asmmoval, 1
initcur:
asmmovDGROUP:@Crtio@CursorType,al
asmmoval,es:[10h]
asmandal,00110000b
asmcmpal,00110000b
asmjemda_yes
asmmovax, 3
asmmovbx,0b800h
asmjmpinitcrtend
mda_yes:
asmmovax, 7
asmmovbx,0b000h
initcrtend:
asmmovDGROUP:@Crtio@CrtMemSeg,bx
asmmovDGROUP:@Crtio@CrtMode,al
asmcmpwordptrflag, 0
asmjneinitcrtend1
asmcmpal, byte ptrDGROUP:@Crtio@OldMode
asmjneinitcrtend0
asmpushds
asmxorsi,si
asmmovdi,1000h
asmmoves,DGROUP:@Crtio@CrtMemSeg
asmpushes
asmpopds
asmmovcx, 2000
asmcld
asmrepmovsw
asmpopds
initcrtend0:
asm int 10h
initcrtend1:
}
void Crtio::Close()
{
asmmoval,DGROUP:@Crtio@OldMode
asmcbw
asmcmpal, byte ptrDGROUP:@Crtio@CrtMode
asmjneclosecrtend
asmpushds
asmxordi,di
asmmovsi,1000h
asmmoves,DGROUP:@Crtio@CrtMemSeg
asmpushes
asmpopds
asmmovcx, 2000
asmcld
asmrepmovsw
asmpopds
asmxorbh,bh
asmmovah, 2
asmmovdx,1700h
closecrtend:
asm int 10h
if ( ! CursorType)SetCursorType();
}
void Crtio::SetCursorType()
{
CursorType = ! CursorType;
asmmovah, 1
asmmovch, 0
asmcmp byte ptrDGROUP:@Crtio@CursorType, 0
asmjecursortype1
asmmovch,0ch
cursortype1:
asmmovcl,0dh
asm int 10h
}
void Crtio::SetPos( int row, int col)
{
CrtMemOff = CrtOff(row,col);
asmxorbh,bh
asmmovdh, byte ptrrow
asmmovdl, byte ptrcol
asmmovah, 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);
asmmoves,DGROUP:@Crtio@CrtMemSeg
asmmovdi,DGROUP:@Crtio@CrtMemOff
asmmovcx,n
asmmovah,al
asmmoval, byte ptrchs
asmcld
asmrepstosw
}
#pragma warn-rvl
wordCrtio::CrtOff( int row, int col)
{
asmmovax,row
asmmovcl, 4
asmshlax,cl
asmmovbx,ax
asmshlax, 1
asmshlax, 1
asmaddax,bx
asmaddax,col
asmshlax, 1
}
int Crtio::PutChars( int row, int startcol, char * s, int endcol, int attr)
{
SetPos(row,startcol);
GetAttr(attr);
asmmoves,DGROUP:@Crtio@CrtMemSeg
asmmovdi,DGROUP:@Crtio@CrtMemOff
asmmovcx,endcol
asmsubcx,startcol
asminccx
asmpushcx
asmmovah,al
asmcld
pushDS_
asmLDS_si,s
nextchs:
asmlodsb
asmcmpal, 32
asmjbputcharsend
asmstosw
asmloopnextchs
putcharsend:
popDS_
asmpopax
asmsubax,cx
}
int Crtio::GetChar( int row, int col)
{
CrtOff(row,col);
asmmovbx,ax
asmmoves,DGROUP:@Crtio@CrtMemSeg
asmmovax,es:[bx]
}
int Crtio::GetAttr( int attr)
{
asmmovax,attr
asmcmpwordptrDGROUP:@Crtio@CrtMemSeg,0b000h
asmjneGetAttr1
asmmovcl, 4
asmshral,cl
asmcmpal, 3
asmjbeGetAttr2
asmmovax,70h
asmjmp short GetAttr1
GetAttr2:
asmmovax, 7
GetAttr1:
}
#pragma warn.rvl
void Crtio::SaveRect( int row1, int col1, int row2, int col2, void far * buf)
{
CrtOff(row1,col1);
asmcld
asmmovsi,ax
asmlesdi,dwordptrbuf
asmmovax,row2
asmsubax,row1
asmincax
asmmovcx,ax
asmstosw
asmmovax,col2
asmsubax,col1
asmincax
asmmovdx,ax
asmstosw
asmpushds
asmmovds,DGROUP:@Crtio@CrtMemSeg
getnextrow:
asmpushcx
asmpushsi
asmmovcx,dx
asmrepmovsw
asmpopsi
asmpopcx
asmaddsi,LINEBYTES
asmloopgetnextrow
asmpopds
}
void Crtio::RestRect( int row, int col, void far * buf)
{
CrtOff(row,col);
asmmovdi,ax
asmpushds
asmmoves,DGROUP:@Crtio@CrtMemSeg
asmldssi,dwordptrbuf
asmcld
asmlodsw
asmaddax,row
asmcmpax, 25
asmjlerestcrt1
asmmovax, 25
restcrt1:
asmsubax,row
asmmovcx,ax
asmlodsw
asmmovdx,col
asmadddx,ax
asmcmpdx,50h
asmjlerestcrt2
asmmovdx,50h
restcrt2:
asmsubdx,col
asmshlax, 1
asmmovcol,ax
putnextrow:
asmpushcx
asmpushsi
asmpushdi
asmmovcx,dx
asmrepmovsw
asmpopdi
asmpopsi
asmpopcx
asmadddi,LINEBYTES
asmaddsi,col
asmloopputnextrow
asmpopds
}
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);
}
asmmovcx,row2
asmsubcx,row1
asminccx
asmmovdx,col2
asmsubdx,col1
asmincdx
asmcld
asmmovax,n
asmmovbx,LINEBYTES
asmcmpwordptrflag, 0
asmjnescroll0
asmcmpax, 0
asmjgscroll1
asmnegax
asmnegbx
scroll1:
asmsubcx,ax
asmjmp short scroll3
scroll0:
asmcmpax, 0
asmjgscroll2
asmnegax
asmstd
scroll2:
asmsubdx,ax
scroll3:
asmmoves,DGROUP:@Crtio@CrtMemSeg
asmpushds
asmpushes
asmpopds
asmmovsi,off2
asmmovdi,off1
nextroll:
asmpushcx
asmpushsi
asmpushdi
asmmovcx,dx
asmrepmovsw
asmpopdi
asmpopsi
asmpopcx
asmaddsi,bx
asmadddi,bx
asmloopnextroll
asmpopds
}
void Crtio::Clear( int row1, int col1, int row2, int col2, int color)
{
CrtOff(row1,col1);
asmmovdi,ax
asmmovcx,row2
asmsubcx,row1
asmjns_clear1
asmnegcx
_clear1:
asminccx
asmmovdx,col2
asmsubdx,col1
asmjns_clear2
asmnegdx
_clear2:
asmincdx
asmcld
asmmoves,DGROUP:@Crtio@CrtMemSeg
asmmovah,color
asmxoral,al
nextcls:
asmpushcx
asmpushdi
asmmovcx,dx
asmrepstosw
asmpopdi
asmpopcx
asmadddi,LINEBYTES
asmloopnextcls
}
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 X25彩色; 10 : 80 X25彩色; 11 : 80 X25单色
6 - 7位,软盘驱动器数
所以Crtio::Init函数通过以下代码对0040:0010字节的4-5位进行判断,是否单显或彩显,如果是单显,显示模式设置为7,屏显地址设置为0x0b00,否则显示模式设置为3,屏显地址设置为0x0800:
asmmoval, es:[10h]
asmandal, 00110000b
asmcmpal, 00110000b
直接写屏时,一个字符为2个字节,高字节为显示属性,即字符的前、背景颜色,低字节为要显示的字符。可能有人问,文本直接写屏能显示汉字吗?一般情况下,文本显示模式只能显示ASCIII码及扩展的ASCII码,但是,如果有影射写屏方式的中文DOS支持,如UCDOS,是可以显示汉字的,因为这种中文DOS表面看是使用的文本模式,内部却是使用的图形模式,如VGA彩显,内部就是使用12h图形模式,并对屏显地址进行了修改,看上去我们是对文本屏显地址0xb800直接写屏,其实都被影射到图形显存地址,使用它的图形字符画上去的。
声明,文章的代码是1995年前的东西,只能供初学者们借鉴参考。有错误或建议,请来信:maozefa@hotmail.com