China DOS Union

-- Unite DOS · Advance DOS · Grow DOS --

Union site: www.cn-dos.net Forum site: www.cn-dos.net/forum
DOS stands for freedom, openness and progress. Let us work hard, learn from the openness and GNU spirit of FreeDOS and Linux, and together build and grow a free GNU GPL world!

中国DOS联盟论坛
The time now is 2026-07-05 00:28
中国DOS联盟论坛 » DOS开发编程 & 发展交流 (开发室) » Fast Chinese Character Display Using Extended Memory (repost) View 987 Replies 3
Original Poster Posted 2003-10-09 00:00 ·  中国 湖北 武汉 联通
高级用户
★★
Credits 677
Posts 194
Joined 2003-09-13 00:00
22-year member
UID 9778
Gender Male
Status Offline
Nowadays, in order to handle Chinese characters, many graphics programs use either direct reading of Chinese font library files or the small-font-library technique to display Chinese characters. Although this method is effective, it always has many shortcomings: direct reading of Chinese font library files makes the display speed too slow and causes too much disk wear; and although the small-font-library technique improves the display speed to a certain extent, it does not fundamentally solve the problem of "fast display", and it is also limited to handling only a small number of Chinese characters. Actually, today's microcomputers no longer have only 1MB of memory space. As long as we read HZK16 into extended memory and then fetch the glyphs from extended memory, we can fundamentally solve the problem of "fast display".

  The earliest PCs used the 8086/8088 as the CPU. Because it has a 20-bit address bus, it can manage at most 1MB of address space. In the later 80X86 series, the address bus is no longer limited to 20 bits, and the CPU uses two working modes: real mode and protected virtual mode. In real mode, for example, the 80286 uses only 20 of the 24 address bus bits, so addressing is within 1MB; DOS works in this mode. In protected mode, however, the 80286 can reach 16MB.

  Accessing memory in protected mode is much more complicated than in real mode. In real mode, it is enough to specify the segment address and offset address, while in protected mode, it is also necessary to establish the global descriptor table GDT, which consists of 6 kinds of descriptors. Their structure and description are as follows:


  And each kind of descriptor consists of 8B, with the meanings as follows:

  0-1B: this word specifies the number of bytes in the segment length
  2-3B: low word of the base address.
  4B :high byte of the base address.

  In protected mode, the 80286 can acccess 16MB (224) of space. This byte is the high 8 bits of the base address, while the low 16 bits are determined by the 16 bits of the low word of the base address.

  5B :this byte is called the memory segment access-rights byte, and is generally set to 93H.
  6-7B: these two bytes are both reserved and are always set to 0.

  For the convenience of later explanation, we define its structure in C language as follows:

  #define WORD unsigned int
  #define BYTE unsigned char
  #define LONG unsigned long
  typedef struct Des{
  WORD size; /*data segment length */
  WORD BaseLow16; /*low 16 bits of base address*/
  BYTE BaseHigh8; /*high 8 bits of base address*/
  BYTE attr; /*access-rights or attribute value*/
  WORD NoUse; /*reserved*/
  } DES;
  typedef struct GDTHead
  { DES BlankDsc; /*blank descriptor*/
  DES GDTDsc; /*descriptor of this GDT*/
  DES SrcDsc; /*source data block descriptor*/
  DES DstDsc; /*destination data block descriptor*/
  DES BiosCs; /*BIOS code segment descriptor*/
  DES BiosSs; /*BIOS stack segment descriptor*/
  } GDT;

  In our program, in order to access extended memory above 1MB, we use subfunction 87H of BIOS interrupt INT15H. This subfunction can exchange data among various memories between 0 ̄崐16M. The usage is as follows:

  Input parameters: AH=87H
  CX=the number of data blocks to transfer at one time, in words
  ES:SI=segment address: offset address of the global descriptor table GDT.

  Implemented in C, it is:

  void EmsMoveData(GDT *mcb,WORD size)
  { /*Function: use BIOS INT 15H to transfer data
  from the source data to the destination
  data*/
  struct REGPACK r;
  r.r_es=FP_SEG(mcb);
  r.r_si=FP_OFF(mcb);
  r.r_cx=size>>1;
  r.r_ax=0x8700;
  intr(0x15,&r);
  }

  Once we understand the above method of using extended memory, when we need to access extended memory, we only need to change the transfer segment length and base address of the source and destination blocks, and then call function 87H. Among them, the source code for changing the descriptors of the source and destination blocks is as follows:

  void SetSrcAddr(GDT *e,LONG addr,WORD size)
  { /**********************************
  Function : set the source data block descriptor value
  Input params:e pointer to GDT
  addr base address
  size byte size
  ***********************************/
  e->SrcDsc.BaseLow16=addr & 0x0ffff;
  /*set the low 16 bits of the base address*/
  e->SrcDsc.BaseHigh8=addr>>16;
  /*set the high 8 bits of the base address*/
  e->SrcDsc.size=size;
  }
  void SetDstAddr(GDT *e,LONG addr,WORD size)
  { /*********************************
  Function : set the destination data block descriptor value
  Input params:e pointer to GDT
   addr base address
   size byte size
  *********************************/
  e->DstDsc.BaseLow16=addr & 0x0ffff;
  e->DstDsc.BaseHigh8=addr >> 16;
  e->DstDsc.size=size;
  }

  In actual use, the given size of extended memory does not always meet the requirements, so it is necessary for us to obtain the size of extended memory in the program and handle various situations flexibly. At this time, subfunction 88H of INT 15H is needed. This function means to obtain the capacity of extended memory above 1MB (in units of 1K), and it is called as follows:

  Input parameter:AH= 88H
  Return value:AX=number of extended memory blocks (each block=1K)

  It is worth explaining that if the user configures emm386.exe in the DOS config.sys file, then this function number is masked, but the INT87H function 42 provided by EMM386.exe can be used to obtain the number of available pages (16K per page).

  WORD GetEMSSize(void)
  /*Get the current size of extended memory, return the value in K*/{ _AH=0x88;
  geninterrupt(0x15);
  if (_AX==0)/*if the current function number is occupied by the EMM386 program*/
  { _AH=0x42;/*call EMM386 function 42H to get the number of available pages*/
  geninterrupt(0x67);
  return(_BX*16);
  }
  else
  return(_AX);}

  Therefore, if we want to use extended memory for fast Chinese character display, we must do the following things.

  ⑴、Read HZK16 into extended memory. We might as well read HZK16 into the extended memory specified by HZK16ADDR. The implementation is as follows:

  int LoadHZK16ToEms(void)
  { /*************************************
  Function: send the contents of the file HZK16 into the specified extended memory
  ***********************/
  FILE *fp;
  LONG FileSize,EmsSize,SrcAddr;
  WORD size,num;
  char *buf;
  fp=fopen("HZK16","rb";/*open the HZK16 file*/
  if(fp==NULL)/*if the file cannot be opened, return file opening error*/
  return(OpenFileError);
  EmsSize=((LONG)GetEMSSize())*1024;
  /*get the current size of extended memory and convert it to bytes*/
  fseek(fp,0L,SEEK_END); /*move the file pointer to the end of the file
  to test the size of the file*/
  FileSize=ftell(fp);
  rewind(fp);
  if (EmsSize<FileSize)
  /*if the file size exceeds the current size of extended memory,
  then return the error message that the file is too large*/
  { fclose(fp);
  return(FileToBigger);}
  if(EmsSize-(HZK16ADDR-0x10000)<0) /*if the space from the starting address to the maximum extended memory is not enough to load this file, then return the error that the specified base address is too large*/
  { fclose(fp);
  return(AddrToBigger);}
  buf=(char *)malloc(0x8000); /*allocate memory for transferring file contents*/
  if (buf==NULL) /*if memory allocation fails, return conventional memory allocation error*/
   { fclose(fp);
  return(MallocError);}
  SrcAddr=FP_SEG(buf); /*calculate source data block address*/
  SrcAddr=(SrcAddr<<4)+FP_OFF(buf);
  SetSrcAddr(&mes,SrcAddr,0x8000);
  do /*each time read 8000H bytes into base memory, then transfer them into extended memory*/
   { num=fread(buf,1,0x8000,fp);
   SetDstAddr(&mes,addr,num);
   EmsMoveData(&mes,num);
   addr+=0x8000;
   } while(num==0x8000);
  free(buf);
  fclose(fp);
  return(NoError);/*return no error on success*/
  }

  The above program uses some macro definitions and a global variable mes of structure type GDT. They can be defined in the header file, and the initialization code is as follows:

  #define HZK16ADDR 0x110000L
  /*starting address of the Chinese font library in extended memory*/
  enum ErrMsg{ /*error message definitions*/
   NoError , /*no error*/
   MallocError , /*memory allocation error*/
   FileToBigger , /*file too large, cannot be loaded into extended memory*/
   OpenFileError , /*file opening error*/
   AddrToBigger };


  GDT mes={{0,0,0,0 ,0},/*pseudo descriptor initially set to 0*/
  {0,0,0,0 ,0},/*descriptor of GDT*/
  {0,0,0,0x93,0},/*source and destination block descriptors*/
   {0,0,0,0x93,0},/*access rights both set to 93H*/
   {0,0,0,0 ,0},/*BIOS code segment descriptor set to 0*/
  {0,0,0,0,0}};/*BIOS stack segment descriptor set to 0*/

  ⑵、After reading the Chinese font library into extended memory, we then need to take out the glyph corresponding to the given Chinese character quwei code. This process is relatively simple: first calculate the offset address of the Chinese character in the font library, then add the starting address of the font library in extended memory, which is the storage address of the Chinese character in extended memory. Next, use subfunction 87H of INT 15H to transfer it into the required base memory.

  void GetHzMode(WORD c,WORD *buf)
  { /********************************
  Function: fetch the glyph of a Chinese character to BUF,
  Input params:c quwei code of the full-width character
  buf address where the glyph is stored
  ********************************/
  LONG addre,offset;
  register int i;
  addre=FP_SEG(buf);/*convert the address storing the glyph to the required base address*/
  addre=(addre<>8)-0xa1)*94L+((c&0xff)-0xa1))*32l;
  /*calculate the offset address of the character in the whole Chinese font library*/
  offset+=HZK16ADDR;
  /*add the base offset address to the starting address when the font library was loaded, which is the address of the character in extended memory*/
  SetSrcAddr(&mes,offset,32);
  /*read 32 bytes from extended memory and store them into the memory specified by buf*/
  SetDstAddr(&mes,addre,32);
  EmsMoveData(&mes,32);
  }

  ⑶、According to the fetched glyph, there are many ways to display Chinese characters on the screen. Common ones include the dot-plotting method, the line-drawing method, the method of modifying the IMG structure, direct screen writing, and so on. Each of these methods has its own merits. Here, the author only gives the line-drawing method for displaying Chinese characters. The line-drawing method cleverly uses the line type definition function setlinestyle(int linestyle,usigned upatten,int thickness). When linstyle is USERBIT_LINE (user-defined 崐line type), its parameter patten is an unsigned integer. If a certain bit is 1, it means a dot is plotted on the screen. Since what is generally used for screen display is a 16×16 dot-matrix Chinese character, we can regard it as a figure made up of 16 lines of different line types.

  /********************************************
  Input params:cwords string to display
  x,y coordinates for display
  color display color
  *********************************************/
  void ListCstring(unsigned char *cwords,
  int x,int y,int color)
  { WORD mode;
  BYTE w;
  WORD c;
  register int i;
  int xx,yy;
  struct linesettingstype OldLineSetting;
  getlinesettings(&OldLineSetting);
  /*save the original line type setting value*/
  setcolor(color);
  xx=x;yy=y;
  for(;*cwords
  if(*cwords<0x7f) /*if it is an English character, then use the C function outtextxy to output it*/
   { w=*(cwords++); w='\0';
   outtextxy(xx,yy,w);
   xx+=8;} else /*it is a full-width character*/
  { c=(*cwords<<8) | *(cwords+1);
   GetHzMode(c,mode);/*fetch the glyph of the character*/
  for(i=0;i>8&0x00ff )
  |(mode<<8&0xff00);
   for(j=0;ibase=SaveScreenAddr;
  addr=0xa0000L+s->y1*80L+s->x1/8;
  for(y=s->y1;yy2;y++,addr+=80L)
  for(i=0;ix2-s->x1)/8+3);
  SetSrcAddr(&mes,addr,(s->x2-s->x1)/8+3);
  EmsMoveData(&mes,(s->x2-s->x1)/8+3);
  SaveScreenAddr+=(s->x2-s->x1)/8+3;
  }
  outportb(0x3ce,4); /*allow plane 0 to be read*/
  outportb(0x3cf,0);
  }
  
  void RestoreMsgToScreen(SSH *s)
  /*restore the screen contents in 640*480*16-color mode from the extended memory at base address
  addr back to the screen*/
  { BYTE i;
  WORD y;
  LONG addr,ToAddr;
  addr=s->base;
  ToAddr=0xa0000L+s->y1*80L+s->x1/8;
  for(y=s->y1;yy2;y++,ToAddr+=80L)
  {for(i=1;i<=8;i<x2-s->x1)/8+3);
  SetDstAddr(&mes,ToAddr,
  (s->x2-s->x1)/8+3);
  EmsMoveData(&mes,(s->x2-s->x1)/8+3);
  addr+=(LONG)((s->x2-s->x1)/8+3);
  }
  }
  outportb(0x3c4,2); /*allow plane 4 to be written*/
  outportb(0x3c5,0x0f);
  SaveScreenAddr=s->base;
  }

  Although this method can effectively handle saving and restoring the contents of large screen areas, when using it, please be sure to pay attention to this point: the region saved later must be restored first; this is the same as a stack. If the region saved later is not needed, it may simply be ignored.

  When using these functions, the user can put them all into one file, and then add #include in the header of his own program.
Floor 2 Posted 2003-10-10 00:00 ·  中国 重庆 永川区 电信
初级用户
Credits 122
Posts 6
Joined 2003-09-26 00:00
22-year member
UID 10364
Gender Male
Status Offline
Hehe, I still prefer using GJGPP. It's convenient and efficient for memory operations. Whether it's working with fonts or images, there's no need to write so much code.
Floor 3 Posted 2003-10-10 00:00 ·  中国 湖北 武汉 联通
银牌会员
★★★
Credits 1,681
Posts 512
Joined 2003-08-02 00:00
22-year member
UID 7953
Gender Male
Status Offline
Write it as a LIB, okay? I still don't know how to use C...
Troubled in',,,
Somehow somewhere I've got to choose.
No matter if it is win or lose.
Floor 4 Posted 2003-11-18 00:00 ·  中国 陕西 西安 电信
初级用户
★★
忍者
Credits 376
Posts 86
Joined 2003-10-16 00:00
22-year member
UID 11361
Gender Male
Status Offline
Awesome, I support you!
以C语言软件开发为主:http://sunny1979.icpcn.com
Forum Jump: