DOS 下多任务系统的设计与实现 (1995-7)

DOS 下多任务系统的设计与实现 (1995-7)

作者:wxzself

一、引言:
  将DOS扩展为多任务系统,在理论和实践中都有很重要的意义。但由于DOS系统单用户,单任务的设计局限,在实现中有不少难点。 本文提出一种实现方法及其具体设计。
  现有的多任务DOS系统,可以分为两类。 一类是在其它多任务系统(如OS/2、UNIX、WINDOWS NT等)的支持下的DOS多任务。在这类系统中,有着严格的,良好的任务保护和隔离机制,DOS 等于是一个虚拟机器环境或一个客户服务器,不包括在机器内核之中,因此,在内核之上运行多个DOS任务比较容易实现,但是,大量的DOS 上的运行软件要求在运行时完全控制计算机资源,这样的做法势必与操作系统的资源管理机制相冲突。为此,许多系统又设计为在运行DOS程序时可以选择独占系统资源,即便如此,对DOS程序也有诸多限制, 这类任务切换系统的优点是功能强大,可靠性高。
  第二类系统是在DOS系统本身的支持下,通过对DOS内核的改造,使的DOS具有一定的多任务能力,典型的如DOSSHELL等。由于一般用户对DOS的多任务能力要求并不是很高,如后台并发进程,设备驱动排队等并不需要,而仅是希望能够在程序间方便的进行任务切换, 同时不影响被切换的程序,而这类系统对用户的最大方便是用户程序在执行时可以独占机器,同时,这类系统建立在DOS系统之上, 对用户程序兼容性好,因此仍有一定的实用价值。
  相比较之下,第一类占有很大的技术和性能优势,但它对机器硬件要求高,系统设计复杂,并且不能作到对现有程序的全兼容,第二类在近几年内还有一定的市场,特别会流行于低档PC机上, 它的设计较之第一类是很简单的,本文提出的设计方案就基于第二类。
二、设计思想:
  许多的DOS应用程序在运行时要求全部的内存空间,一般要求达到400K以上。所以在640K内存中运行多个任务是不现实的, 只能将不使用的任务对换出常规内存,暂时存入硬盘或扩展内存(本文将1088K以上空间泛指扩展内存),在任务再次执行时再换入常规内存 。当然,这种方法是以增加任务切换时间为代价的。
  作为任务切换程序必须很好解决DOS重入问题,由于DOS 内核设计上的单任务串行执行性,DOS是不支持多任务的。一些程序通过一些其它方法(如检测IN_DOS信号量)来进行重入,这类方法不但局限性大(许多情况下不能使用),而且只能重入一次, 在多任务环境下无法使用。目前,使DOS内核可以执行多任务代码的最佳方法是SDA 对换重入法,DOS自3.1至6.2均提供稳定支持,但由于许多细节没有澄清, 一些使用此办法的DOS重入程序可靠性不高,程序运行结果似是而非。只有对SDA对换技术作深入研究之后,才能正确使用 。关于这方面的资料,详见资料[1]。
  需要指出的是,使用SDA对换技术之后,DOS可以支持多个任务。由于应用程序可能接管中断向量(如TSR或某些字处理软件),如果系统在这方面缺乏控制,当多个任务在内存中相互作用, 形成复杂的中断链时,几乎肯定要死机,因此从系统的安全性,健壮性的角度出发,应当控制全部的中断向量。
  从单任务转化到多任务,要求对一些系统资源也要进行处理,如屏幕,当前目录等,使它能与系统当前任务相对应。 作为任务切换软件,必须解决以上的问题,本文试给出一种解决方案,具体如下:
  1、内存对换技术
  由于DOS的许多应用程序要求大量内存,因此将活动任务装入内存,而将其它任务暂存于磁盘或扩展内存中, 就能将整个内存分配给单个任务以满足内存需要,在用户请求任务切换时, 将现有任务暂存于磁盘或扩展内存中,并将新的任务装入内存运行,在切换过程中,所有的任务都具有相同的优先级,而由用户决定运行哪一个任务, 这个过程可以表示如下:
将所有的任务看作一个大的轮盘,在任务切换时,将现有任务存于轮盘上,并将新的任务从轮盘装入内存运行,在这种情况下,每个任务都认为自己拥有全部的计算机资源,这种多任务方式可以称作轮盘式多任务.考虑多方面的因素,没有必要支持任务在后台执行.

  2.避免中断向量的复杂链接:

  考虑到应用程序不可避免的要挂接中断向量, 在任务对换出去后,若中断向量还指向原处,在新的任务装入后,中断向量便会指向不可知区域,所以,要保存各个任务的中断向量表,使中断向量表局部于任务.在任务切换时,保存任务的中断向量表,在任务交换到磁盘之前将内存中所有指向此任务的中断向量改为此任务运行之前的值,即强行摘除任务挂接的中断向量,当任务再次运行时, 将保存的中断向量恢复,即"安装"它的中断处理程序,这样,任务挂接的中断仅在任务在内存中执行时起作用,当它被对换出内存后,中断向量被复原.这使的在多个任务均挂接中断向量时,不会形成复杂的, 无法解开的中断链机制.

  3.与其它程序的相容性:

  作为任务切换程序,必须和用户程序共驻内存,这就需要任务切换程序对用户应用程序的影响要尽可能的小,但是对有些实在无法避免冲突的情况,则不予考虑, 这虽然在软件工程中被看作是无法容忍的,但却是理论和实践相结合的很好例子. 只要能够相容于绝大多数普通应用软件,及一部份的TSR,就可以了.而与其它任务切换软件(如DOSSHELL)的相容性则不予考虑,理由是:没有必要在内存中存在两个任务切换程序,同时,不考虑与DOS 5.0的任务切换API的接口问题,但将努力使两个任务切换程序能在内存中相容,不发生冲突.
  相容性是一个软件能够存在和发展的重要因素, 从软件设计思想到具体技术实现,都因当首先考虑,把它贯穿于设计过程之中.首先,应在设计思想中重视相容性,不使用相容性差的思想及技术, 并且要考虑在今后的发展中会遇到的不相容的因素.其次, 在设计过程中要与现有软件作对比和测试,尽最大可能避免冲突.

  需要指出的是,相容性是在系统设计时考虑的重点,但在设计完成之后,不再考虑.

  4.软件设计中的其它考虑:

  出于单任务改造为多任务的需要,我们必须使屏幕,驱动器, 当前目录也局部于具体任务,在任务切换时做相应的保存恢复工作.

  虽然汇编语言是任务切换软件设计的直接选择,但在权衡速度,效率等方面之后,决定采用C语言来编写,毕竟,采用C语言编写系统软件已有许多成功的先例,值得一试.

  由于任务切换软件不可避免的会遇到直接使用DOS 数据结构及未公开的DOS功能调用,因此一定要保证在各个 DOS版本下的兼容性,程序何时与DOS交互,何时深入DOS编程,都应作仔细考虑,特别是直接存取DOS数据结构及使用未公开的DOS功能调用对兼容性影响最大,应使用DOS所稳定支持的,并且将来也不大可能改变的功能,在软件中直接使用的DOS数据结构有:

    1. MCB: 内存控制块(Memory Ctrl Block).
    2. SDA: DOS 可对换数据区(DOS Swap Date Area).
使用未公开功能调用有:
    1. INT 21H,5D06H 取DOS 可对换数据区(SDA)信息.
        入口:
          AX=5D06H
          (DPL 参数有争议,这里按大多数书上所写,将它略去)
        出口:
          DS:SI=SDA 地址
          CX= INDOS 对换区大小
          DX= 一直对换区大小
    2. INT 21H,50H 设置当前PSP.
        入口:
          AH=50H
这些数据结构和功能调用都是DOS自3.1至6.2所稳定支持的( 未考虑DOS4.X下的特殊情况),估计在未来的版本中也不大可能发生变化,否则,不但任务切换软件无法运行,众多的DOS应用软件也将不能运行.
  应当指出,DOS在这种既提供多任务支持, 又不建立多任务内核的矛盾情况下 ,它的内核代码也是有许多反映这种矛盾情况的地方.碰到这种情况意谓着编程者要小心"地雷",不要从上面踩过去.

三.设计技术:
  1.内存对换:
  一般的内存映象如下图:
  ┌──────┐
  │ D O S   │
  ├──────┤
  │ COMMAND │
  ├──────┤
  │ T A S K   │ First swap seg
  ├──────┤ ── ┌─────┐ ┌─────┐
  │         │     │       │ │       │
  │         │     │       │ │       │
  │   USER 1 │     │ USER 2 │ │ USER 3 │
  │         │     │       │ │       │
  │         │     │       │ │       │
  │         │     │       │ │       │
  │         │     │       │ │       │
  └──────┘     └─────┘ └─────┘
                           
  如果用单板机上的存储体切换技术来看这张图 , 就比较清楚,TASK及它以前的内存属于不切换块, 而用户空间则随着具体任务的变化而选中相应的存储体, 流行的 VROOM (覆盖) 技术也与此类似.
  用户空间随着系统配置而稍有变化,一般在450-550K左右 , 当DOS被装入HMA后,用户内存可达600K左右, 这些内容在切换时均被写入磁盘,因写入量特别大,用户最好配备扩展内存,以加快切换速度.

  2.任务信息:

  为了尽量压缩任务切换程序所占内存空间, 几乎所有的任务信息均被写入磁盘,任务信息主要包括现场,SDA,中断向量表,内存, 屏幕等等.这样,大大压缩了任务切换程序所占内存空间,但也要求更多的磁盘空间.

  3.关于信号量:

  在多任务环境下,代码间的同步,互锁显得非常重要, 有许多的信号量标志(包括与DOS进行通信和交互), 应当正确处理好这些信号量.如当热键激活后,在容许再次按热键之前,用户按下热键不应当被再次激活,以避免切换程序本身的重入,伪代码为:

INVECT IntrHook proc far
    if not pop_up then
      pop_up=true
        // do anything in here
      pop_up=false
    else
    iret

  4.C语言编程:

  采用C语言编程给程序设计带来了一些新的问题,这些问题必须在编程中很好的解决 , 举一个例子 , 在中断服务代码中无法使用fprintf函数,为什么呢?因为fprintf 在函数内部使用了内存分配函数,而在中断服务程序执行时无法预料能否调用内存分配函数( 这里牵涉到是否遇到DOS重入,当前有无堆可供分配等等 ) . 在不能使用fprintf的前提下,用什么函数来代替,和fprintf 相类似的函数在库函数中还有哪些等等.同时,高级语言如何保护现场,如何进行任务切换,如何切换堆栈等问题,这在资料[2],资料[3]中已被论及. 这里给出一个在屏幕上打出时间的中断服务程序,在此可以看出库函数如何使用:

void interrupt NewInt1C(void)
{
static int nIsEnter=0;
static char sStr[12],*pStr;
static char far *p;
static unsigned char h,m,s;

      (*OldInt1C)();
    if(nIsEnter++<4)
          return;       /* 每隔200ms打印一次 */
    else
    {
          nIsEnter=0;   /* 互锁信号量 */
          _AH=2;
          geninterrupt(0x1a);   /* get time from
real clock */
          h=_CH;
          m=_CL;
          s=_DH;
#ifndef NDEBUG       /* 调试信息: ERROR_PTR,INDOS_PTR */

          sprintf(sStr,"%d %d ",*(pDosSwapArea),
                *(pDosSwapArea+1));
#else             /* 任务号,时,分,秒 */
          sprintf(sStr,"%02d %02x:%02x:%02x",
                uTask,h,m,s);
#endif
      /* Can not use standard I/O library function */
    /* 虽然可以用cprintf函数,但不推荐这样做 */
          pStr=sStr;
          p=MK_FP(0xb800,160-22); /* Video Buffer at right_top */
          while(*pStr)
          {
                *(p++)=*(pStr++);       /*   OME char */
                *(p++)=RED;         /* Attrib*/
          }
    }
}
  这个程序虽然很简单,但还有一些需要解释的地方,nIsEnter声明为static是因为多次进入NewInt1C时需要知道此信号量的一贯值,而其它的值声明为static是为了防止过多的局部变量引起堆栈溢出,在调用sprintf时,要在编译时保证DS!=SS,否则参数压栈和引用参数无法得到正确结果(请注意,在NewInt1C执行时,堆栈可能是属于当前内存中的任意进程所有,而不光是你的程序),程序不使用cprintf 是因为它有可能与前台任务的文本函数相互干扰,并有可能造成BIOS重入,最重要的是,它的堆栈占用太大(尽管它可以在程序中工作, 但这并不是使用它的理由).

四.设计实现:

  1.程序设计:
  为了实现强制任务切换,程序必须挂接INT 9H,以达到必要的快速响应.同时,应实现以下流程:

┌─────┐
│ 任务 1 │  
├─────┤
│ 任务 2 │       INT 9H 服务程序
├─────┤   ┌─────────┐
│ 任务 3 │   │             │
├─────┤   │   调度程序     │
│ 任务 4 │   │             │
├─────┤   └─────────┘
│ 任务 5 │
└─────┘
  就绪队列
 
INT 9H程序伪代码为:

if not our hotkey
  {
      old_int9 proc
      return;
  }
  if also POPUP or DOS_busy or crit_error
      return;

  POPUP=1;
      enter scheldure

  POPUP=0;
  return

  scheldure程序伪代码为:

  save oldSDA   保存老的SDA

  save oldVECT     保存老的中断向量

  save oldmemory   保存老的内存

  get newTASK     取下一个任务  

  load newmemory   装入新的内存

  load newVECT     装入新的中断向量

  load newSDA     装入新的内存

  return

  2.关键技术的解决办法:

  内存的保存涉及MCB(memory contrl block)

  struct MCB {
      char attr;     ''''M'''': normal,''''Z'''':last MCB
      unsigned size;    
      unsigned owner;   owner psp
  }

  MCB 近乎于单链表,为段对齐,下一块MCB 的段地址等于本块的段地址加本块的长度,一直到最后一个MCB,它的attr为''''Z''''.保存/ 恢复内存时,从用户的MCB开始,一直到 A000H.以下为伪代码:

  char *p;
  for(p=firstaddress;p<MK_FP(0xa000,0);)
  {
      write(file,p,BLOCKSIZE);
      p+=BLOCKSIZE;
      p=(char huge *)p+0;
  }

    当DOS在关键区时,不容许任务切换,因此建立CRIT_ERROR 信号量来与DOS通信(详见资料[1]):

当DOS进入或退出关键区会通知用户:

  INT 2AH
      AH=80:   DOS 通知用户进入关键区
      AH=81:   DOS 通知用户退出关键区
      AH=82:   DOS 通知用户退出关键区

用户挂接INT 2A 后就可知道DOS是否在关键区.

  INT2A proc far
      switch(ax&0xff00)
      {
          case 8000:      
            CRIT_ERROR++;
            break;
          case 8001:
          case 8002:
            if(CRIT_ERROR)
                CRIT_ERROR--;
            break;
          default:
            old_INT2A
      }
      iret
  endp

  屏幕的保存伪代码如下:

  save display mode
  if mode<8 save textbuffer
  else save graphicsbuffer

  save txetbuffer
  {
  char far *p=MK_FP(0xb800,0);
      write(fp,p,4000);
  }

  save graphicsbuffer /* 图形下必需保存4个页面 */
  {
  char far *p=MK_FP(0xa000,0);
      select page0
          write(fp,p,38400);
      select page1
          write(fp,p,38400);
      select page2
          write(fp,p,38400);
      select page3
          write(fp,p,38400);
  }  
 
由于使用C语言编程,C语言中断程序实际上是:

  void interrupt a(void)
  {
  do any code in here
  }
 
  相应的汇编语言为:

  a proc far
  push all
  mov ds,DATA
  do any code in here
  pop all
  iret
  endp

  而手工汇编为:
  a proc far
  iret
  endp

  了解这些有助于我们的程序设计,C语言相对于汇编语言还存在一定的差距,体积和效率都太差。

  对话框是程序设计中用到的一个对象, 全部风格仿照afxwin.h的式样,代码如下:

/* 画一个比窗口略大的框 */


#define SINGLELINE     1
#define DOUBLELINE   2

void Box(RECTSTRUCT *p,int type,char *title) {
int x1,y1,x2,y2;
int i;
int a,b,c,d,h,l;
int titlelen;
    if(type==SINGLELINE) {
        a=218,b=191,c=217,d=192,h=179,l=196;
    }
    if(type==DOUBLELINE) {
        a=201,b=187,c=188,d=200,h=186,l=205;
    }
    x1=p->nXBegin;
    y1=p->nYBegin;
    x2=x1+p->nLength;
    y2=y1+p->nHigh;
    x1--,y1--,x2++,y2++;
  window(1,1,80,25);
    SetColor(ColorStruct.BoxColor,ColorStruct.BoxBackColor);
    gotoxy(x1,y1);
    titlelen=strlen(title)+2;
    cprintf("%c",a);
    for(i=x1+1;i<x2;i++)
        cprintf("%c",l);
    cprintf("%c",b);
    gotoxy(x1+(x2-x1+1-titlelen)/2+1,y1);
    cprintf("%s",title);
    gotoxy(x1,y2);
    cprintf("%c",d);
    for(i=x1+1;i<x2;i++)
        cprintf("%c",l);
    cprintf("%c",c);
    for(i=y1+1;i<y2;i++) {
        gotoxy(x1,i);
        cprintf("%c",h);
        gotoxy(x2,i);
        cprintf("%c",h);
    }
}


五.设计检验:

  程序设计及调试是在一台 Legend LX E3/40 上进行的,采用的DOS版本为OME版 DOS 3.31,C语言编译器为Turbo C 2.0.源程序长约13K,可执行文件比源文件略小,说明C语言编程是比较成功的.

    主要指标为:
┌────────┬──────┬───────────┐
│项 目       │指 标     │ 最   好   值     │
├────────┼──────┼───────────┤
│任务数       │ 6个     │               │
├────────┼──────┼───────────┤
│切换速度     │ 3秒     │ 1秒(使用扩展内存)   │
├────────┼──────┼───────────┤
│任务显示模式支持│ 13H以下 │ 320*400 256色     │
├────────┼──────┼───────────┤
│对换空间占用   │ 900K以下 │               │
└────────┴──────┴───────────┘

  在调试完成之后,软件在 DOS 3.31,DOS 5.0,DOS 6.2下进行了运行测试,主要目的是观察它与其它软件的相容性,同时,也与DOS 5.0下的DOSSHELL进行了对比.

  DOS 3.31下的结果如下:

  此机上所装大部分软件均运行良好,清单如下: CCED 5.0, TC 2.0,TURBO PASCAL 5.0, PCTOOLS 5.0, FOXBASE 2.1, DBASE 3 PLUS, BGIDEMO(TC下的图形演示软件).WPS 和 SPT 运行正常,但任务切换无法进行,原因是它们在挂接INT9H 后均未调用原来的中断处理程序.使用CHINA(一个地图漫游软件)时在装入过程中死机.TT( 一个采用320*200 256色显示模式及模拟语言发声的指法练习软件)使用正常.
  SPDOS 5.0 下使用中效果良好,甚至将SPDOS作为普通任务进行切换也没有什么问题.
  UCDOS 3.0 在使用中效果良好,但由于它的图形模式特别,偶而在切换过程中屏幕保存/恢复出现问题,但程序并未死机,适当处理后可继续运行(UC_DOS必须先装入).
  CodeView 2.0在切换中严重死机,但DEBUG和Turbo Debugger 3.0运行正常.
  当任务数超过4个时,有时有死机现象,主要是因为DOS 3.31 下的设置为 FILES=20,任务数增加时打开文件出错造成的.
  综上所述,大部分的应用软件均可良好相容,但游戏软件相容性比较差,这与设计初的设想一致.
  CodeView 2.0的严重死机可能是由于它再加载进程运行调试的同时,它自己也在内存中进行着覆盖对换, 与切换系统产生冲突而引起.

  DOS 6.2下的结果如下:

    由于没有DOS 6.2下的DOSSHELL,因此测试项目与DOS 3.31相同,结果也相同,但由于设置为FILES=30,所以很少出现死机.

  DOS 5.0下的结果如下:

  DOS 5.0下的设置为FILES=60,测试项目与DOS 3.31相同, 结果也相同,因此主要的测试转向与DOSSHELL(DOS 5.0下的任务切换软件)的对比,对比结果如下(TASK 为作者所设计的任务切换软件):

  1.指标对比:

┌─────────┬─────────┬──────┐
│ 程序表现       │   DOSSHELL   │   TASK   │
│ 项   目       │             │         │
├─────────┼─────────┼──────┤
│ 编写语言       │   汇 编       │   TC 2.0   │
├─────────┼─────────┼──────┤
│ 内存占用       │   38K         │   22K   │
├─────────┼─────────┼──────┤
│ 界面友好程度   │   好         │   差     │
├─────────┼─────────┼──────┤
│ 任务数       │   多 个       │   多 个   │
├─────────┼─────────┼──────┤
│ 切换速度(一般)   │   2秒以内     │ 3秒以内 │
├─────────┼─────────┼──────┤
│ 挂接中断向量   │   约20个       │ 三个   │
├─────────┼─────────┼──────┤
│ 其它功能       │   文件,进程管理 │   很少   │
├─────────┼─────────┼──────┤
│ 应用软件相容性   │   见下面的分析   │         │
└─────────┴─────────┴──────┘

  2.应用软件相容性(切换效果)对比:

程   序       DOSSHELL           TASK

TC 2.0         好               好
TURBO PASCAL 5.0 好               好
PCTOOLS 5.0     好               好
CCED 5.0       西文好             好
DEBUG         好               好
FOXBASE 2.1     西文好             好
DBASE 3 PLUS     西文好             好
BGIDEMO       可以运行           好
            无法切换
SPDOS 5.0       使用冲突           好
UCDOS 3.0       部分软件使用冲突     好,
                            但须先加载UCDOS
                            否则不能切换
WPS 2.1       SPDOS不能运行       可以运行
            所以无法运行         无法切换
SPT 1.2       SPDOS不能运行       可以运行
            所以无法运行         无法切换
CodeView 2.0     不能切换           不能切换
TURBODEBUGGER 3.0 不能切换           好
TT           无法运行           好

注:
  好:               运行切换均正常.
  西文好:           无法装载汉字系统,西文下正常.
  可以运行,无法切换:     运行正常, 但无法切换或切换时程序退出.
  不能切换:           运行正常,切换时引起死机.
  使用冲突:           运行时死机,无法装入内存.

  运行结果可以总结为:

  DOSSHELL 在普通任务切换效果上比TASK好,但它不适合于图形切换,并且与汉字系统和TSR程序很不相容,它的另一个优点是优秀的文本界面.
  TASK 在设计上比 DOSSHELL 粗糙,但它的代码短小, 并且相容性比较好,特别适合于图形及汉字下的任务切换.
  TASK 自始至终没有考虑与DOSSHELL的相容性问题,但两者之间有着良好的相容性,两个程序甚至可以在内存中共同进行任务切换而互不影响.

六.其它问题:

  任务切换软件还有一些使用技巧, 例如可以用它来任意保存屏幕图象,卸下TSR程序等等,下面列出几个:
  1.保存/恢复屏幕:
  TASK在任务切换时保存了用户屏幕,所以当需要得到屏幕图像时,进行任务切换,然后拷贝屏幕影像文件保存,以后可以利用恢复屏幕功能来重新显示图像 .
  2.卸下TSR程序:
  由于TASK在任务切换时会"摘除"用户程序挂接的中断向量, 所以在TASK以后驻留的TSR程序均可在任务切换中被"卸下".

七.小结:

    TASK在近几年内还有一定的使用价值,随着技术的进步,16位程序将向32位全面过渡,出于兼容的考虑,大量的应用程序将能够 以二进制兼容的方式继续使用,但TASK不属于这类程序.


参考文献:

资料[1]: <<DOS 重入问题的探讨与实现>>,赵廷哲.1995
资料[2]: <<未公开的DOS核心技术>>,ANDREW SCHULMAN等.清华大学出版社 1992
资料[3]: <<C语言高级实用教程>>,尹彦芝.清华大学出版社 1991

  在设计过程中,作者始终得到了计算机系老师们的大力帮助,王让定副教授给予了热情指导,提出了许多宝贵意见和建议, 建立了本系统的思想框架,并对本文作了详细的审阅; 王小牛老师作为作者的指导老师,贯穿于论文设计之中提出了许多思想,观点,尤其是他介绍给作者的多任务下的多堆栈切换机制使作者深受启发,并使作者最后下决心将简单的前后台任务改为真正的多任务系统,在设计的整个过程中他都给予了具体指导;索国瑞老师则给作者讲述了显示卡上的许多知识,解决了屏幕保存/恢复的难题; 还有许多老师和同学们则在不同方面提出了见解和建议,它们都是论文思想的来源, 在此一并表示感谢.



DOS 下多任务系统的设计与实现 -- 源码 -- 主程序

1、TASK.C

/*

TASK.C
Teacher: Wang Xiaoniu
program by Wang Xiaozhi & Zhao Tingzhe

CopyRight Northwest Normal Univeristy Computer Department 1995.4


*/



/*


TCC -mc -1 -f- -d -k- -O -Z task.c
Usage:task [DRIVE:/PATH/]


*/



#include <stdlib.h>
#include <io.h>
#include <fcntl.h>
#include <dos.h>
#include <mem.h>
#include <conio.h>
#include <process.h>

/*

Use BIOS correspondence areas
0:0x4f0 Software flag
0:0x4f4 Menu Return

*/

/*

Change Vector includes 1CH,2AH,9H,5H,7DH,
1CH:Timer;
2AH:DOS critical section;
9H:Keyboard hardware interrupt;

*/

#define SOFTWAREFLAG 0x575a


#define NDEBUG


#ifndef NDEBUG

void _Assert(char *,int);
void DeleteTask(void);

#define Assert(f) /
if(f) /
  ; /
else /
  _Assert(__FILE__,__LINE__)

void _Assert(char *s,int line)
{
cprintf("Assert failed :FILE :%s LINE:%d",
s,line);
DeleteTask();
}

#else

#define Assert(f)

#endif


#define INTERRUPT void interrupt
typedef unsigned int WORD;
typedef unsigned char BYTE;


#define STACKSIZE 2048       /* Itself stack */

char sStack[STACKSIZE];

#define MAXUSERS 6     /* Max Task Number */


#define F10_KEY 68 /* This is F10 */     /* Hot Key */
#define CTRL_ALT_KEY 12 /* This is ctrl 0x8 && alt 0x4*/ /* Crtl_ALT Key */


#define SDA_SWAPSIZE 0x7c0

extern WORD _stklen=2048;


int WantPopUp=0;               /* Menu pop_up flag */
int Int2ABusy=0;   /* Dos Enter flag */

int nExe=0;   /* Now Task Id */
int uTask=0;


INTERRUPT (*OldInt9)();
INTERRUPT NewInt9();
/*
INTERRUPT (*OldInt1C)();
INTERRUPT NewInt1C();
*/
INTERRUPT (*OldInt2A)();
INTERRUPT NewInt2A();


void main(int argc,char *argv[]);
void TaskMain(void);
void ChangeVect(void);
void TestInstall(void);
void End(void);
void TaskChange(void);
void Menu(void);
void SaveAllMem(void);
void SaveMem(WORD seg);
void SetMemFree(WORD seg);
void LoadMem(WORD seg);
WORD GetMem(void);
void DeleteTask(void);
void SubDos(void);
int MakeMapName(char *s,int mode);

#define READ 0
#define READ_AND_NOT_DIRECT_PUT 2
#define WRITE 1

void IOFile(char *s,char *buf,WORD size,int ctrl);


typedef struct {
WORD es,ds,di,si,bp,sp,bx,dx,cx,ax,ip,cs,flags;
} INTERRUPT_REGS;               /* Interrupt parameters */


typedef struct {
WORD psp;
struct SAVE_REGS {
WORD sp,ss; /* Task RESG */
} save_regs;
char swapsave[SDA_SWAPSIZE]; /* DosSwapArea */
} PCB;                       /* PCB For Task */

PCB nPCB[2];
BYTE IsTask[MAXUSERS+1]; /* For Task 1 to 6 */
  /* 0 noused */
int fp;


#define VECTMAP "vect"
#define SDAMAP "sda"

#define BLOCKSIZE 512
#define MEMORYMAP "mem" /* memory map file */

typedef struct {
char attr;
WORD owner;
WORD size;
} MCB;   /* Memory Crtl Block */

MCB mcbdesc={ /* Last,size,nouse */
'Z',0,0
};


char sPath[32];         /* Owner's path and swapdisk */
char sDisk[32];
char sNameBuffer[82];
char temp2[48];

/*_______________________________________________________________________*/
/*
DOSSWAP.C-Functions to manage DOS swap areas
*/



/*

INT21h 5D06

DS:SI -points to DOS swappable data area
DX     -size of area to swap when InDOS>0
CX -size of area to always swap

*/


#define GET_DOSSWAP3 0X5D06

/* variables for 3.x swap work*/

char far *pDosSwapArea;               /* DosSwapArea Addr */
int DosSwapSize;

/*
Function:InitDosSwap
Initialize pointers and sizes of DOS swap area.
*/

void InitDosSwap(void)
{
WORD save_ds;
WORD tmp_ds,tmp_si,tmp_cx;

save_ds=_DS;
_AX=GET_DOSSWAP3;
geninterrupt(0x21); /* Must save DS */
tmp_cx=_CX;
tmp_ds=_DS;
tmp_si=_SI;
_DS=save_ds;
pDosSwapArea=MK_FP(tmp_ds,tmp_si);

Assert(pDosSwapArea);
DosSwapSize=tmp_cx;

}

/*
Function:SaveDosSwap
This function will save the dos swap area to a local buffer
*/

void SaveDosSwap(void)
{
movedata( FP_SEG(pDosSwapArea),
  FP_OFF(pDosSwapArea),
  _DS,
  FP_OFF(nPCB[nExe].swapsave),
  DosSwapSize);
}

/*
Function:RestoreDosSwap
This function will restore a previously swapped dos data area
*/

void RestoreDosSwap(void)
{
movedata( _DS,
  FP_OFF(nPCB[nExe].swapsave),
  FP_SEG(pDosSwapArea),
  FP_OFF(pDosSwapArea),
  DosSwapSize);
}

/*_______________________________________________________________________*/
/*
MAIN.C-Main program for Task DOS
*/

void TaskMain(void)
{
TestInstall();
InitDosSwap();
SaveDosSwap();
ChangeVect();
/* Save raw vector table */
IOFile(VECTMAP,(char far *)0l,1024,WRITE);
uTask=1;
IsTask[1]=1;
SubDos();
End();
}

void ChangeVect(void) /* Set owner hook intr */
{
/*
OldInt1C=getvect(0x1c);
*/
OldInt2A=getvect(0x2a);
OldInt9=getvect(0x9);
/*
setvect(0x1c,NewInt1C);
*/
setvect(0x2a,NewInt2A);
setvect(0x9,NewInt9);
}

/*
The function TestInstall first check is also installed!
Then makeup pop_up stack,save task's DTA for use.

*/


void TestInstall(void)
{
if(peek(0,0x4f0)==SOFTWAREFLAG) /* If also exist, exit */
exit(1);
else
poke(0,0x4f0,SOFTWAREFLAG); /* Exist flag */

nPCB[0].save_regs.ss=_DS;
/* We not sure if sp is a odd */
nPCB[0].save_regs.sp=FP_OFF(&sStack[STACKSIZE-2]);
nPCB[0].psp=_psp;

}

void End(void) /* Restore old intr and clear */
{
IOFile(VECTMAP,(char far *)0l,1024,READ_AND_NOT_DIRECT_PUT);
/*
setvect(0x1c,OldInt1C);
*/
setvect(0x2a,OldInt2A);
setvect(0x9,OldInt9);
poke(0,0x4f0,0); /* Clear softflag */
}

INTERRUPT NewInt9() /* CTRL_ALT_F10 to pop up. */
{
static char far *pKey=MK_FP(0,0x417);
static int key;
if(inportb(0x60)==F10_KEY) /* Hot key */
{   /* CTRL and ALT */
if((*pKey&CTRL_ALT_KEY)==CTRL_ALT_KEY)
{
  key=inportb(0x61);
  outportb(0x61,key);
  outportb(0x20,0x20);
/*
if also pop up, or if dos_error,or dos busy,
can not pop up.
*/
if(WantPopUp||*pDosSwapArea||Int2ABusy)
return;
else
{
WantPopUp=1;
disable();
nPCB[1].save_regs.ss=_SS;   /* save old stack */
nPCB[1].save_regs.sp=_SP;
_SP=nPCB[0].save_regs.sp; /* set new stack */
_SS=nPCB[0].save_regs.ss;
enable();
TaskChange();   /* enter task */
disable();
_SP=nPCB[1].save_regs.sp; /* restore old stack */
_SS=nPCB[1].save_regs.ss;
enable();
WantPopUp=0;
}

}
else
  (*OldInt9)();
}
else
(*OldInt9)();
}

/*

INTERRUPT NewInt1C()
{
static int i=0;
static char str[12],*sptr;
char far *p;
static char h,m,s;

(*OldInt1C)();

if(i++<4)
return;
else
{
i=0;
_AH=2;
geninterrupt(0x1a);
h=_CH;
m=_CL;
s=_DH;
#ifndef NDEBUG
sprintf(str,"%d %d ",*(pDosSwapArea),
  *(pDosSwapArea+1));
#else
sprintf(str,"%02d %02x:%02x:%02x",
  uTask,h,m,s);
#endif

sptr=str;
p=MK_FP(0xb800,160-22);
while(*sptr)
{
  *(p++)=*(sptr++);
  *(p++)=RED;
}
}
}

*/

INTERRUPT NewInt2A(INTERRUPT_REGS r) /* Check DOS busy. */
{
switch(r.ax&0xff00)
{
case 0x8000:
  Int2ABusy++;
  break;
case 0x8001:
case 0x8002:
  if(Int2ABusy)
  Int2ABusy--;
  break;
default:
  break;
}
_AX=r.ax;
(*OldInt2A)();
}


void TaskChange(void) /* Task Change Procedure */
{
nExe=1;
SaveDosSwap(); /* save SDA */
nExe=0;
RestoreDosSwap(); /* set new SDA */

IOFile(SDAMAP,(char *)&nPCB[1].psp,sizeof(PCB),WRITE);
Menu();   /* sub route */
IOFile(SDAMAP,(char *)&nPCB[1].psp,sizeof(PCB),READ);

SaveDosSwap();
nExe=1;
RestoreDosSwap(); /* restore old SDA */
}


void cccp(char *temp)
{
sprintf(sNameBuffer,"%s%s",sPath,temp);
spawnl(P_WAIT,sNameBuffer,sNameBuffer,temp2,NULL);
}

/* Define Sub program */


#define MENU   "menu"
#define SAVEDISP "savedisp"
#define LOADDISP "loaddisp"


/* Define display filename */

#define DISPMAP "disp"


void Menu(void)
{
int nTmpTask;

/* Save Task's Vector */
IOFile(VECTMAP,(char far *)0l,1024,WRITE);

nTmpTask=uTask;
uTask=0;

/* restore raw vect for another task */

IOFile(VECTMAP,(char far *)0l,1024,READ_AND_NOT_DIRECT_PUT);
uTask=nTmpTask;

SaveAllMem();   /* save and clear memory */
sprintf(temp2," %s%s%d.map",sDisk,DISPMAP,uTask);/* sDisk is SwapDisk */
cccp(SAVEDISP); /* save and load display for menu to do.*/

sprintf(temp2," %s",sDisk);
poke(0,0x4f4,uTask);
cccp(MENU);
if((uTask=(int)peek(0,0x4f4))==-1) /* menu return value in this address.*/
DeleteTask(); /* all task exit memory */

Assert(uTask<=MAXUSERS);

if(IsTask[uTask]==0) /* Build a new task. */
{
IsTask[uTask]=1;
WantPopUp=0;
SubDos();
}
sprintf(temp2," %s%s%d.map",sDisk,DISPMAP,uTask);
cccp(LOADDISP); /* restore display */
LoadMem(GetMem());     /* restore memory */

/* Restore Task Vector */
IOFile(VECTMAP,(char far *)0l,1024,READ_AND_NOT_DIRECT_PUT);
}

/*
  Memory MAP

  ___________
  | intr   |
  | bios   |
  | dos   |
  | command |
  | t a s k | _______ first swap seg
  |       |
  |   .   |
  |   .   |
  |   .   |
  |   .   |
  |   .   |
  |   .   |
  ___________ A000h

*/


void SaveAllMem(void)
{
WORD firstseg;

firstseg=GetMem();
SaveMem(firstseg);
SetMemFree(firstseg);

}

void SaveMem(WORD seg) /* from seg to 0xa000 save to file */
{
char huge *tmpptr;

tmpptr=(char huge *)MK_FP(seg,0);
MakeMapName(MEMORYMAP,1);

Assert(fp!=-1);

while(seg<=0xa000)
{
_write(fp,(char far *)tmpptr,BLOCKSIZE);
tmpptr+=BLOCKSIZE;
seg+=BLOCKSIZE/16;
}
_close(fp);
}


void SetMemFree(WORD seg) /* Let from seg to 0xa000 is */
{
mcbdesc.size=0xa000-seg-1; /* a free MCB */
movedata(_DS,FP_OFF(&(mcbdesc.attr)),
seg,0,16);
}


void LoadMem(WORD seg) /* load memory map from file */
{
char huge *tmpptr;

MakeMapName(MEMORYMAP,0);
tmpptr=(char huge *)MK_FP(seg,0);

Assert(fp!=-1);

while(seg<0xa000)
{
if((0xa000-seg)<32)
  _read(fp,(char far *)tmpptr,(0xa000-seg)*16);
else
  _read(fp,(char far *)tmpptr,BLOCKSIZE);
tmpptr+=BLOCKSIZE;
seg+=BLOCKSIZE/16;
}
_close(fp);
}


/*
Cation :
The next MCB is
first swap memory seg.
*/
/*
GetMem return's seg is next MCB,
It belone's sub command.com.
*/

WORD GetMem()
{
MCB far *p;
p=MK_FP(nPCB[0].psp-1,0);
return(nPCB[0].psp+p->size);
}


/*
Cation:
DeleteTask free DOS memory,restore raw vector,
but it not close other task's opening files. You
can use it, But remember this.
*/
void DeleteTask(void)
{
uTask=0;
SetMemFree(GetMem());
End();
exit(1);
}



#define WELLCOMEFILE "sptmap" /* PAGE */
#define HELPFILE "help" /* HELP */


void main(int argc,char *argv[])
{
char *s,*t;

Assert(argv[0]);

s=argv[0]; /* get task's path */
t=0;
for(;*s;s++)
{
if(*s=='//')
  t=s;
}
*(t+1)=0;
sprintf(sPath,"%s",argv[0]);
*t=0;
if(argc==2) /* get task's swapdisk */
sprintf(sDisk,"%s",argv[1]);
else
sprintf(sDisk,"%s",sPath);

temp2[0]=0;
cccp(WELLCOMEFILE);   /* Page */


TaskMain();         /* MAIN ROUTE */
}

int MakeMapName(char *s,int mode) /* open a task's file */
{
sprintf(sNameBuffer,"%s%s%d.map",sDisk,s,uTask);
if(mode)
fp=_creat(sNameBuffer,FA_ARCH);
else
fp=_open(sNameBuffer,O_RDWR);
}


/* ctrl 0:read,1:write */

void IOFile(char *s,char *buf,WORD size,int ctrl)
{
int i;
long vector;
Assert(size<0x800);
if(ctrl==READ_AND_NOT_DIRECT_PUT)
{
i=1;
ctrl=READ;
}
else
i=0;

MakeMapName(s,ctrl);
Assert(fp!=-1);

if(ctrl==WRITE)
{
_write(fp,buf,size);
}
else
{
if(i)   /* Can't direct put to Vectable */
{
  for(i=0;i<256;i++)
  {
  _read(fp,(char far *)(&vector),4);
  setvect(i,vector);
  }
}
else
  _read(fp,buf,size);
}
_close(fp);
}

void SubDos(void)
{
char *p;
p=getenv("COMSPEC");
spawnl(P_WAIT,p,p,"/p",NULL);
}




DOS 下多任务系统的设计与实现 -- 源码 -- 系统控制界面

1、MENU.C


/*
MENUMAIN.C
*/

/*
tcc -mh menumain

*/


#include <string.h>
#include <dir.h>
#include <dos.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <conio.h>
#include <mem.h>
#include <string.h>
#include <process.h>
#include <stdlib.h>
#include <assert.h>

extern directvideo=1;
extern stklen=32768;



typedef struct {
int TextColor;
int BackColor;
int BackGroundColor;
int BoxColor;
int BoxBackColor;
int TitleColor;
int MenuMainColor;
int MenuSubColor;
int MenuBarColor;
int StatusColor;
int StatusBackColor;
int PromptColor;
int PromptBackColor;
} COLORSTRUCT;

COLORSTRUCT ColorStruct={
WHITE,
DARKGRAY,
BLACK,
WHITE,
LIGHTBLUE,
YELLOW,
YELLOW,
WHITE,
BLACK,
BLACK,
WHITE,
BLACK,
LIGHTGRAY,
};

typedef struct {
int nXBegin;
int nYBegin;
int nLength;
int nHigh;
} RECTSTRUCT;

RECTSTRUCT MAINWINDOW={
6,4,68,18
};


void RePaint(void);
void main(int argc,char *argv[]);
int MenuSelect(void);
void SetWindow(RECTSTRUCT *pWindow);
void DrawPage(void);
void PrintStatusLine(void);
void PrintPromptLine(void);
void SetColor(int,int);
void SetBack16Colors(void);

#define SINGLELINE 0
#define DOUBLELINE 1
void Box(RECTSTRUCT *p,int type,char *title);

#define ENGLISH 0
#define CHINESES 1

int NowLanguage;
int GetLanguage(void);
void SetLanguage(int new);

void PutTitle(char *title);
void PutPrompt(char *prompt);
void PutBoxMessage(int type);
void PutMess(char *p);

void SaveScreen(RECTSTRUCT *s,char *buf);
void RestoreScreen(RECTSTRUCT *s,char *buf);

void FlushBIOSBuf(void);

void MenuWindow(void);
void PrintMenu(void);

int SUBMenuBox(void);
int PopSUBMenuBox();
void PrintSUBMenu(void);

void HelpWindow(void);

char *EditBox(char *sEditBuf);
char *PopEditBox(char *s);
char *LineEdit(char *s);

void TmpBox(void);
void ABOUT(void);
int ExitBox(void);
void LanguageBox(void);
void TimeBox(void);
void MusicBox(void);
void SetColorBox(void);
void HelpBox();
void TestBox();

void cccp(char *s);
void ResetScreen(void);

void DrawScreen(void);
void DrawUserScreen(void);
void DrawUserTaskScreen(void);
void WriteToFiles(char *name);

void Init(void);
int Proc(void);
void End(void);

void StoreDiskAndPath(void);
void ReadDiskAndPath(void);

void RePaint(void)
{
textmode(C80);
SetBack16Colors();
textbackground(ColorStruct.BackGroundColor);
clrscr();
DrawPage();
}

void DrawPage(void)
{
static char *str[2]={
"   TASK plus V1.0   ",
"   多任务 DOS V1.0   ",
};

Box(&MAINWINDOW,DOUBLELINE,str[GetLanguage()]);
    SetColor(ColorStruct.TextColor,ColorStruct.BackColor);
SetWindow(&MAINWINDOW);
clrscr();
PrintStatusLine();
PrintPromptLine();
}

void SetColor(int TextColor,int TextBackColor)
{
int color;
color=((TextBackColor<<4)+TextColor);
textattr(color);
}


void SetWindow(RECTSTRUCT *pWindow)
{
int x,y,xx,yy;
x=pWindow->nXBegin;
y=pWindow->nYBegin;
xx=x+pWindow->nLength;
yy=y+pWindow->nHigh;
window(x,y,xx,yy);
}

void SetBack16Colors(void)
{
_BL=0;
_AH=0x10;
_AL=3;
geninterrupt(0x10);
}

void RestoreBackColor(void)
{
_BL=1;
_AH=0x10;
_AL=3;
geninterrupt(0x10);
}


void Box(RECTSTRUCT *p,int type,char *title) {
int x1,y1,x2,y2;
int i;
int a,b,c,d,h,l;
int titlelen;

if(type==SINGLELINE) {
a=218,b=191,c=217,d=192,h=179,l=196;
}
if(type==DOUBLELINE) {
a=201,b=187,c=188,d=200,h=186,l=205;
}

x1=p->nXBegin;
y1=p->nYBegin;
x2=x1+p->nLength;
y2=y1+p->nHigh;

x1--,y1--,x2++,y2++;

window(1,1,80,25);
SetColor(ColorStruct.BoxColor,ColorStruct.BoxBackColor);

gotoxy(x1,y1);
titlelen=strlen(title)+2;

cprintf("%c",a);
for(i=x1+1;i<x2;i++)
cprintf("%c",l);

cprintf("%c",b);

gotoxy(x1+(x2-x1+1-titlelen)/2+1,y1);
cprintf("%s",title);

gotoxy(x1,y2);
cprintf("%c",d);
for(i=x1+1;i<x2;i++)
cprintf("%c",l);

cprintf("%c",c);
for(i=y1+1;i<y2;i++) {
gotoxy(x1,i);
cprintf("%c",h);
gotoxy(x2,i);
cprintf("%c",h);
}
}

void PrintStatusLine(void)
{
static char *str[2]={
" Menu     Tool     Options     Help",
" 菜单     工具     设置     帮助"
};

extern int ncurTask;

static char *str2[2]={
"Now Task:",
"当前任务:"
};

SetColor(ColorStruct.StatusColor,ColorStruct.StatusBackColor);
gotoxy(1,1);

clreol();
cprintf("%s",str[GetLanguage()]);
cprintf("           %s %d",str2[GetLanguage()],ncurTask);
}


void PrintPromptLine(void)
{
int n;
char *p;
static char *str[2]={
" ALT M: Menu T: Tool O: Options H: Help ",
" ALT M: 菜单 T: 工具 O: 设置 H: 帮助 "
};

SetColor(ColorStruct.PromptColor,ColorStruct.PromptBackColor);
gotoxy(1,MAINWINDOW.nHigh+1);
n=GetLanguage();
    if(n==ENGLISH)
p=str[0];
else
if(n==CHINESES)
  p=str[1];
else
  assert(0);

clreol();
cprintf("%s",p);
}


int GetLanguage(void)
{
return(NowLanguage);
}

void SetLanguage(int new)
{
assert((new>=0)&&(new<=1));
NowLanguage=new;
RePaint();
}

RECTSTRUCT MESSAGEBOX;

#define ID_OK   1
#define ID_YESNO     2
#define ID_YESNOCANCEL 3
#define ID_CANCEL     4


#define MB_OK 1
#define MB_YES 2
#define MB_NO 3
#define MB_CANCEL 4

RECTSTRUCT MBRECT={
30,10,24,5
};

#define BOXCOLOR WHITE
#define BOXBACKCOLOR LIGHTBLUE


int MessageBox(char *title,char *prompt,int type)
{
char *pBuf;
int size;
int nReturnValue;
size=GetRectSize(&MBRECT);
pBuf=malloc(size);
assert(pBuf);
SaveScreen(&MBRECT,pBuf);
nReturnValue=PopMessageBox(title,prompt,type);
RestoreScreen(&MBRECT,pBuf);
free(pBuf);
return(nReturnValue);
}

int PopMessageBox(char *title,char *prompt,int type)
{
static char *str[2]={
" Message Box ",
" 对 话 框 ",
};
RECTSTRUCT MBRECT2;

MBRECT2=MBRECT;
MBRECT2.nXBegin+=2;
MBRECT2.nYBegin+=1;
MBRECT2.nLength+=2;
MBRECT2.nHigh+=1;

SetColor(BLUE,BLUE);

SetWindow(&MBRECT2);
clrscr();

Box(&MBRECT,DOUBLELINE,str[GetLanguage()]);
SetWindow(&MBRECT);
SetColor(BOXCOLOR,BOXBACKCOLOR);
clrscr();

PutTitle(title);
PutPrompt(prompt);
return(BoxKey(type));
}

int BoxKey(int type)
{
int key;
PutBoxMessage(type);
while(1)
{
switch(type)
{
  case ID_OK:
  GetKey();
  return(MB_OK);
  case ID_YESNO:
  key=GetKey();
  if((key=='Y')||(key=='y'))
  return(MB_YES);
  if((key=='N')||(key=='n'))
  return(MB_NO);
  break;
  case ID_YESNOCANCEL:
  key=GetKey();
  if((key=='Y')||(key=='y'))
  return(MB_YES);
  if((key=='N')||(key=='n'))
  return(MB_NO);
  if((key==27)||(key=='C')||(key=='c'))
  return(MB_CANCEL);
  break;
  case ID_CANCEL:
  key=GetKey();
  if((key==27)||(key=='C')||(key=='c'))
  return(MB_CANCEL);
  break;
  default:
  assert(0);
  break;
}
}
}

int GetKey(void)
{
int key,lo,hi;
key=bioskey(0);
lo=key&0x00ff;
hi=(key&0xff00)>>8;
return(lo==0?hi+256:lo);
}

void PutTitle(char *title)
{
int len;
int i;
len=strlen(title);
assert(len<MBRECT.nLength);
i=(MBRECT.nLength-len)/2+1;
gotoxy(i,2);
cprintf("%s",title);
}

void PutPrompt(char *prompt)
{
int len;
int i;
len=strlen(prompt);
assert(len<MBRECT.nLength);
i=(MBRECT.nLength-len)/2+1;
gotoxy(i,3);
cprintf("%s",prompt);
}

void PutBoxMessage(int type)
{
char **p;
static char *str1[2]={
"OK!",
"确认"
};
static char *str2[2]={
"Yes No",
"是   否"
};
static char *str3[2]={
"Yes No Cancel",
"是   否 放弃"
};
static char *str4[2]={
"Cancel",
"放弃",
};
switch(type)
{
case ID_OK:
  p=str1;
  break;
case ID_YESNO:
  p=str2;
  break;
case ID_YESNOCANCEL:
  p=str3;
  break;
case ID_CANCEL:
  p=str4;
  break;
default:
  assert(0);
  break;
}
PutMess(p[GetLanguage()]);
}

#define MESSCOLOR   LIGHTGRAY
#define MESSBACKCOLOR   BLACK

void PutMess(char *p)
{
int i;
int len;
char buf[64],*s;
len=strlen(p);
strcpy(buf,p);
assert(len<MBRECT.nLength);
i=(MBRECT.nLength-len)/2+2;
gotoxy(i,5);
SetColor(MESSCOLOR,MESSBACKCOLOR);
s=strtok(buf," /t");
while(s)
{
cprintf("%s",s);
i=i+strlen(s)+2;
gotoxy(i,5);
s=strtok(NULL," /t");
}
}


int GetRectSize(RECTSTRUCT *s)
{
int x,y,xx,yy;
int size;
x=s->nXBegin-1;
y=s->nYBegin-1;
xx=s->nXBegin+s->nLength+4;
yy=s->nYBegin+s->nLength+4;
size=(yy-y+1)*(xx-x+1);
return(size);
}


void SaveScreen(RECTSTRUCT *s,char *buf)
{
gettext(s->nXBegin-1,s->nYBegin-1,
s->nXBegin+s->nLength+4,
s->nYBegin+s->nHigh+4,buf);
}
void RestoreScreen(RECTSTRUCT *s,char *buf)
{
puttext(s->nXBegin-1,s->nYBegin-1,
s->nXBegin+s->nLength+4,
s->nYBegin+s->nHigh+4,buf);
}

RECTSTRUCT MENURECT={
9,7,16,12
};


#define NORMAL 1
#define NOTALLOWED     2


typedef struct {
char *name[2];
int attr;
} MENUSTRUCT;

#define MENUMAXSIZE 22

MENUSTRUCT MenuStruct[MENUMAXSIZE]={
{
"TASK",
"任务",
NOTALLOWED,
},
{
"   RETURN",
"   返回",
NORMAL,
},
{
"   USER TASK",
"   用户任务",
NORMAL,
},
{
"   EXIT",
"   退出",
NORMAL,
},
{
"   ABOUT",
"   信息",
NORMAL,
},
{
"TOOL",
"工具",
NOTALLOWED,
},
{
"   OS SHELL",
"   DOS 命令",
NORMAL,
},
{
"   MEM WALK",
"   内存检查",
NORMAL,
},
{
"   TASK SCREEN",
"   用户屏幕",
NORMAL,
},
{
"   TCALC",
"   运算器",
NORMAL,
},
{
"   EDIT",
"   编辑器",
NORMAL,
},
{
"   TIME",
"   时间",
NORMAL,
},
{
"   MUSIC",
"   音乐",
NORMAL,
},
{
"OPTIONS",
"设置",
NOTALLOWED,
},
{
"   REPAINT",
"   重画屏幕",
NORMAL,
},
{
"   LANGUAGE",
"   语言",
NORMAL,
},
{
"   SET COLOR",
"   颜色设置",
NORMAL,
},
{
"   TEST SYSTEM",
"   系统测试",
NORMAL,
},
{
"HELP",
"帮助",
NOTALLOWED,
},
{
"   COMMAND",
"   命令",
NORMAL,
},
{
"   SYSTEM SET",
"   系统配置",
NORMAL,
},
{
"",
"",
0,
},
};

#define MENUBACKCOLOR LIGHTBLUE
#define MENUMAINCOLOR   YELLOW
#define MENUSUBCOLOR   WHITE
#define MENUBARCOLOR   WHITE
#define MENUBARBACKCOLOR BLACK

int nMenuTopLine=0;
int nMenuCurLine=0;
int nMenuMaxLine=MENUMAXSIZE;

void MenuWindow(void)
{
static char *str[2]={
" MENU ",
" 菜单 ",
};
HelpWindow();
Box(&MENURECT,SINGLELINE,str[GetLanguage()]);
SetWindow(&MENURECT);
SetColor(MENUSUBCOLOR,MENUBACKCOLOR);
clrscr();
PrintMenu();
}


void PrintMenu(void)
{
int i,j;
MENUSTRUCT *p;
SetWindow(&MENURECT);
for(i=nMenuTopLine,j=1;j<=MENURECT.nHigh;i++,j++)
{
p=&MenuStruct;
if(p->attr==NORMAL)
  SetColor(MENUSUBCOLOR,MENUBACKCOLOR);
else if(p->attr==NOTALLOWED)
  SetColor(MENUMAINCOLOR,MENUBACKCOLOR);
else;
if(i==nMenuCurLine+nMenuTopLine)
  SetColor(MENUBARCOLOR,MENUBARBACKCOLOR);
gotoxy(1,j);
clreol();
cprintf("%s",p->name[GetLanguage()]);
if(!(p->name[0][0]))
  break;
}
gotoxy(1,MENURECT.nHigh+1);
}


RECTSTRUCT HELPRECT={
32,7,36,12,
};

#define HELPCOLOR WHITE
#define HELPBACKCOLOR LIGHTBLUE

char *HelpStr[][2]={
{
"   TASK plus V1.0/n/r"
"/n/r"
"CopyRight Northwest Normal University/n/r"
"Computer Department WXZ & ZTZ 1995.5"
,
"   多任务软件 TASK plus V1.0/n/r"
"/n/r"
"版权所有,西北师范大学计算机科学系/n/r"
"作者: 王晓智,赵廷哲/n/r"
"P.1995.5"
,
},
{
"   RETURN is RETURN Now TASK/n/r"
"/n/r"
"If you want to return lastest TASK,/n/r"
"Use this./n/r"
"If the screen get in trouble,you can/n/r"
"also try it."
""
,
"   返回当前任务/n/r"
"/n/r"
"返回刚才的前台任务,如果屏幕的保存与/n/r"
"恢复发生问题,可以试一试此功能./n/r"
""
,
},
{
"   USER TASK/n/r"
    "/n/r"
"Choice a TASK to return,if no this/n/r"
"task,System will auto create DOS/n/r"
"SHELL when you enter this TASK./n/r"
""
,
"   返回任一任务./n/r"
    "/n/r"
"选择一个任务返回,如果没有此任务,系统/n/r"
"将自动加载DOS SHELL./n/r"
""
,
},
{
"   Exit ALL TASK/n/r"
"/n/r"
"Make ALL TASK exit memory,remember/n/r"
"this function can delete any task n/r"
"loading memory after TASK.It return/n/r"
"to DOS./n/r"
"/n/r"
"Warning: This function didn't close/n/r"
"files./n/r"
""
,
"   所有任务退出/n/r"
    "/n/r"
"使所有的任务退出内存,它可以删除所有/n/r"
"在TASK之后加载的任务,然后返回DOS./n/r"
"/n/r"
"警告: 本功能不关闭其它任务打开的文件./n/r"
""
,
},
{
"   ABOUT/n/r"
    "/n/r"
""
,
"   版本声明./n/r"
    "/n/r"
""
,
},
{
"   TOOL/n/r"
    "/n/r"
"These tools are used./n/r"
""
,
"   工具/n/r"
    "/n/r"
"以下为系统所带工具软件/n/r"
""
,
},
{
"   OS SHELL/n/r"
    "/n/r"
"DOS SHELL and run you command,/n/r"
"if you want to quit,enter EXIT./n/r"
""
,
"   DOS 命令/n/r"
    "/n/r"
"执行DOS命令,键入EXIT返回./n/r"
""
,
},
{
"   MEM WALK/n/r"
    "/n/r"
"Check MCB, PSP, ENV, HOOK INTR,but /n/r"
"it can not found TASK, for TASK also/n/r"
"swap in disk./n/r"
"/n/r"
""
,
"   内存检查/n/r"
"/n/r"
"检查内存分配情况,任务加载情况及中断/n/r"
"向量挂接情况,但用户任务已被对换到磁/n/r"
"盘上,所以无法看到./n/r"
"/n/r"
""
,
},
{ "   TASK SCREEN/n/r"
    "/n/r"
"It let you select a TASK's screen to/n/r"
"load, you can get this image ever./n/r"
"/n/r"
""
,
"   任务屏幕/n/r"
"/n/r"
"可以选择一个任务屏幕复现,如果在TASK/n/r"
"之前加载GRAPHICS,可以得到硬拷贝./n/r"
"/n/r"
""
,
},
{ "   TCALC/n/r"
"/n/r"
"Aavadce calc./n/r"
"/n/r"
""
,
"   计算器/n/r"
"/n/r"
"高级计算器./n/r"
"/n/r"
""
,
},
{ "   EDIT/n/r"
"/n/r"
"A little editor./n/r"
"/n/r"
""
,
"   编辑器/n/r"
"/n/r"
""
,
},
{ "   TIME/n/r"
    "/n/r"
"Show time./n/r"
"/n/r"
""
,
"   时间/n/r"
"/n/r"
"/n/r"
"/n/r"
""
,
},
{ "   MUSIC/n/r"
    "/n/r"
"A little piano,you also can use it/n/r"
"write music file./n/r"
"/n/r"
""
,
"   音乐/n/r"
"/n/r"
"小钢琴,同时还可以将乐谱自动演奏,你可/n/r"
"使用任意的文本编辑器来编写曲目,音乐/n/r"
"文件格式见HELP./n/r"
"/n/r"
""
,
},
{ "   OPTIONS/n/r"
"/n/r"
"/n/r"
""
,
"   设置/n/r"
    "/n/r"
""
,
},
{ "   REPAINT/n/r"
    "/n/r"
""
,
"   重画屏幕/n/r"
"/n/r"
"在屏幕被破坏的情况下可以重画./n/r"
""
,
},
{ "   LANGUAGE/n/r"
"/n/r"
"ENGLISH and CHINESES./n/r"
    "/n/r"
""
,
"   语言/n/r"
"/n/r"
"在这个软件中,你可以使用英汉两种语言/n/r"
"的界面,以方便使用./n/r"
"/n/r"
"注意:没有加载汉字系统时,中文界面无法/n/r"
"使用,请切换回英文./n/r"
    "/n/r"
""
,
},
{ "   SET COLOR/n/r"
    "/n/r"
""
,
"   设置颜色/n/r"
    "/n/r"
""
,
},
{ "   TEST SYSTEM/n/r"
    "/n/r"
""
,
"   系统检测/n/r"
"/n/r"
"测试系统的性能./n/r"
"/n/r"
""
,
},
{ "   HELP/n/r"
    "/n/r"
""
,
"   帮助/n/r"
    "/n/r"
""
,
},
{ "   COMMAND/n/r"
    "/n/r"
""
,
"   命令帮助/n/r"
    "/n/r"
""
,
},
{ "   SYSTEM SET/n/r"
    "/n/r"
""
,
"   系统配置/n/r"
"/n/r"
"详细列出系统性能以及更好的性能优化手/n/r"
"段,如CONFIG.SYS的修改,使用VDISK.SYS/n/r"
"虚盘及FASTOPEN加快磁盘存取,和汉字系/n/r"
"统的配合使用,与应用程序的相容性等,还/n/r"
"包括一些工具的使用说明./n/r"
"/n/r"
""
,
},
{ "/n/r"
""
,
""
,
},
{ "/n/r"
""
,
""
,
},
};

void HelpWindow(void)
{
static char *str[2]={
" HELP ",
" 帮助 ",
};
Box(&HELPRECT,SINGLELINE,str[GetLanguage()]);
SetWindow(&HELPRECT);
SetColor(HELPCOLOR,HELPBACKCOLOR);
clrscr();
cprintf("%s",
HelpStr[nMenuTopLine+nMenuCurLine][GetLanguage()]);
}


void FlushBIOSBuf(void)
{
while(bioskey(1))
bioskey(0);
}


#define UPKEY 328
#define DOWNKEY 336

#define ALT_M   306
#define ALT_T   276
#define ALT_O 280
#define ALT_H   291



int MenuSelect(void)
{
int key;
while(1)
{
MenuWindow();
FlushBIOSBuf();
key=GetKey();
switch(key)
{
  case DOWNKEY:
  if((nMenuTopLine+nMenuCurLine)>=(nMenuMaxLine-2))
  break;
  if(nMenuCurLine>=(MENURECT.nHigh-1))
  nMenuTopLine++;
  else
  nMenuCurLine++;
  break;
  case UPKEY:
  if(nMenuTopLine+nMenuCurLine<=0)
  break;
  if(nMenuCurLine==0)
  nMenuTopLine--;
  else
  nMenuCurLine--;
  break;
  case ALT_M:
  nMenuTopLine=0;
  nMenuCurLine=1;
  break;
  case ALT_T:
  nMenuTopLine=0;
  nMenuCurLine=6;
  break;
  case ALT_O:
  nMenuTopLine=9;
  nMenuCurLine=5;
  break;
  case ALT_H:
  nMenuTopLine=9;
  nMenuCurLine=10;
  break;


  case 13:
  return(nMenuTopLine+nMenuCurLine);
  case 27:
  break;
  default:
  break;
}
}
}

extern char path[64];

void cccp(char *s) {
char name[80];
if(*s) {
sprintf(name,"%s%s",path,s);
spawnl(P_WAIT,name,name,NULL);
}
else {
sprintf(name,"%s/c",getenv("COMSPEC"));
spawnl(P_WAIT,getenv("COMSPEC"),
name,NULL);
}
}


char path[64];
char disk[64];

char namebuf[128];
char tmp[64];

int ncurTask;

#define DISKSWAP


#ifdef DISKSWAP

int curdisk[7];
char curdir[7][MAXDIR];
#else
#endif

char tmp2[128];


int RePaintWindow=0;


int Proc(void)
{
int nReturnValue;
int ret;
char buf[48];

while(1)
{
if(RePaintWindow)
{
  Init();
  RePaintWindow=0;
}

nReturnValue=MenuSelect();

switch(nReturnValue)
{
/*
Task : return,user task,exit,about
*/
case 0:
        TmpBox();
  break;
case 1:
  return(ncurTask);
case 2:
  if((ret=SUBMenuBox())==0)
  break;
  else
  return(ret);
case 3:
  if(ExitBox())
  return(-1);
  else
  break;
case 4:
  ABOUT();
  break;
/*
Tool : os shell,memwalk,user screen,tcalc,edit,time,music
*/
case 5:
        TmpBox();
  break;
case 6:
  ResetScreen();
  cccp("");
  break;
case 7:
  ResetScreen();
  cccp("mem");
  break;
case 8:
  DrawScreen();
  break;
case 9:
  ResetScreen();
  cccp("tcalc");
  break;
case 10:
  ResetScreen();
  cccp("edit");
  break;
case 11:
  TimeBox();
  break;
case 12:
  MusicBox();
  break;
/*
Options :   Repaint,language,set color,test system
*/
case 13:
  TmpBox();
  break;
case 14:
  RePaintWindow=1;
  break;
case 15:
  RePaintWindow=1;
  LanguageBox();
  break;
case 16:
  SetColorBox();
  break;
case 17:
  TestBox();
  break;
/*
help: command,advance
*/
case 18:
  TmpBox();
  break;
case 19:
  TmpBox();
  break;
case 20:
  HelpBox();
  break;
default:
  break;
}
}
}

void SetColorBox(void)
{
static char *str[2]={
"Invaid",
"无效",
};
static char *str2[2]={
"Color",
"前景",
};
static char *str3[2]={
"Back",
"背景"
};
MessageBox(str[GetLanguage()],
str2[GetLanguage()],ID_CANCEL);
MessageBox(str[GetLanguage()],
str3[GetLanguage()],ID_CANCEL);
}



void TestBox()
{
TmpBox();
}


void HelpBox()
{
static char *name[2]={
"help.doc",
"readme.doc",
};
static char buf[64],buf2[64];
sprintf(buf,"%shelp",path);
sprintf(buf2,"%s%s",path,name[GetLanguage()]);
ResetScreen();
spawnl(P_WAIT,buf,buf,buf2,NULL);
Init();
}


int ExitBox(void)
{
static char *str[2]={
"Want to EXIT?",
"是否退出?",
};
if(MessageBox("",str[GetLanguage()],ID_YESNOCANCEL)==MB_YES)
return(1);
else
return(0);
}


void LanguageBox(void)
{
static char *str[2]={
"ENGLISH",
"汉字",
};
SetLanguage(1-GetLanguage());
MessageBox("",str[GetLanguage()],ID_OK);
}

void TmpBox(void)
{
static char *str[2]={
"NO DEFINE",
"未定义",
};
MessageBox("",str[GetLanguage()],ID_OK);
}


void TimeBox(void)
{
static char *str[2]={
" Now time is ",
" 当前时间是 ",
};
char buf[16];
int h,m,s;

_AH=2;
geninterrupt(0x1a); /* get real time */
h=_CH;
m=_CL;
s=_DH;
sprintf(buf,"%02x:%02x:%02x",h,m,s);

MessageBox(str[GetLanguage()],buf,ID_OK);
}

void DrawScreen(void)
{
int ret;
static char *str[2]={
"TASK's screen?",
"任务屏幕?"
};
if((ret=MessageBox("",str[GetLanguage()],
ID_YESNOCANCEL))==MB_YES)
DrawUserTaskScreen();
else
if(ret==MB_NO)
  DrawUserScreen();
else;
}

void DrawUserScreen(void)
{
static char *str[2]={
"No such file",
"无此文件",
};
static char buf[48]="SCREEN.SCR";
static char buf2[64];
static char buf3[64];

EditBox(buf);
if(!buf[0])
return;
if(!TestFile(buf))
{
MessageBox("",str[GetLanguage()],ID_CANCEL);
return;
}
sprintf(buf2,"%sloaddisp ",path);
sprintf(buf3,"%s",buf);
ResetScreen();
spawnl(P_WAIT,buf2,buf2,buf3,NULL);
GetKey();
Init();
}

void DrawUserTaskScreen(void)
{
int ret;
static char buf[64],buf2[64];
ret=SUBMenuBox();
if(!ret)
return;
sprintf(buf,"%sloaddisp ",path);
sprintf(buf2,"%sdisp%d.map",disk,ret);
ResetScreen();
spawnl(P_WAIT,buf,buf,buf2,NULL);
GetKey();
Init();
WriteToFiles(buf2);
}

void WriteToFiles(char *name)
{
static char *str[2]={
"Save screen?",
"存入文件吗?",
};
static char *str2[2]={
"No this screen",
"无此屏幕",
};
static char *str3[2]={
"Overwrite",
"覆盖已有文件",
};
static char cmdbuf[82];

char buf[40]="SCREEN.SCR";
if(!TestFile(name))
{
MessageBox("",str2[GetLanguage()],ID_CANCEL);
return;
}
if(MessageBox("",str[GetLanguage()],
ID_YESNOCANCEL)!=MB_YES)
return;
EditBox(buf);
if(!buf[0])
return;
if(TestFile(buf))
{
if(MessageBox("",str3[GetLanguage()],
  ID_YESNOCANCEL)!=MB_YES);
return;
}
sprintf(cmdbuf,"copy %s %s",name,buf);
system(cmdbuf);
}

int TestFile(char *s)
{
FILE *fp;
fp=fopen(s,"r");
if(fp!=NULL)
{
fclose(fp);
return(1);
}
else
return(0);
}



void ResetScreen(void)
{
RestoreBackColor();
RePaintWindow=1;
SetColor(WHITE,BLACK);
window(1,1,80,25);
clrscr();
}

void ABOUT(void)
{
char *str[2]={
"TASK Version 1.0",
"多任务 DOS V1.0"
};
char *str2[2]={
" WXZ & ZTZ 1995",
"王晓智 赵廷哲"
};
MessageBox(str[GetLanguage()],str2[GetLanguage()],ID_OK);
}

void MusicBox(void)
{
int ret;
static char *str[2]={
"Piano?",
"弹奏?",
};
static char buf[64],buf2[64];

ret=MessageBox("",str[GetLanguage()],ID_YESNOCANCEL);
if(ret==MB_CANCEL)
return;
if(ret==MB_YES)
{
ResetScreen();
cccp("music");
return;
}
sprintf(buf,"%syzwq.mus",path);
if(!EditBox(buf))
return;

sprintf(buf2,"%smusic ",path);
ResetScreen();
spawnl(P_WAIT,buf2,buf2,buf,NULL);
Init();
}


void Init(void)
{
RePaint();
}


void End(void)
{
ResetScreen();
}

#ifdef DISKSWAP

#define CONFIGMAP "config"


void StoreDiskAndPath(void)
{
int i;
FILE *p;
sprintf(tmp2,"%s%s.dos",disk,CONFIGMAP);
p=fopen(tmp2,"w");
fwrite(curdisk,sizeof(int),7,p);
fwrite(curdir[0],sizeof(char),7*MAXDIR,p);
fclose(p);
}

void ReadDiskAndPath(void)
{
int i;
FILE *p;
sprintf(tmp2,"%s%s.dos",disk,CONFIGMAP);
p=fopen(tmp2,"a+");
if(p==NULL)
return;
rewind(p);
fread(curdisk,sizeof(int),7,p);
fread(curdir[0],sizeof(char),7*MAXDIR,p);
fclose(p);
}

#else
#endif



void main(int argc,char *argv[]) {
int i;
int ret;
char *s,*t;
s=argv[0];
t=0;
for(;*s;s++) {
if(*s=='//')
  t=s;
}
*(t+1)=0;
sprintf(path,"%s",argv[0]);

if(argc!=1)
strcpy(disk,argv[1]);
else
strcpy(disk,path);

ncurTask=peek(0,0x4f4);
#ifdef DISKSWAP
ReadDiskAndPath();
curdisk[ncurTask]=getdisk();
getcurdir(curdisk[ncurTask]+1,curdir[ncurTask]);

#else
#endif


Init();
ret=Proc();
End();


#ifdef DISKSWAP

if(curdisk[ret]>=2)
{
setdisk(curdisk[ret]);
sprintf(tmp2,"%c://%s",curdisk[ret]+'A',curdir[ret]);
chdir(tmp2);
}
else
{
curdisk[ret]=curdisk[ncurTask];
strcpy(curdir[ret],curdir[ncurTask]);
}
StoreDiskAndPath();

#else
#endif
poke(0,0x4f4,ret);
exit(ret);
}


RECTSTRUCT SUBMENURECT={
30,10,10,6
};

#define SUBMENUCOLOR WHITE
#define SUBMENUBACKCOLOR LIGHTBLUE


int SUBMenuBox(void)
{
char *pBuf;
int size;
int nReturnValue;
size=GetRectSize(&SUBMENURECT);
pBuf=malloc(size);
assert(pBuf);
SaveScreen(&SUBMENURECT,pBuf);
nReturnValue=PopSUBMenuBox();
RestoreScreen(&SUBMENURECT,pBuf);
free(pBuf);
return(nReturnValue);
}

int PopSUBMenuBox()
{
static char *str[2]={
" TASK LIST ",
" 任务列表 ",
};
RECTSTRUCT SUBMENURECT2;

SUBMENURECT2=SUBMENURECT;
SUBMENURECT2.nXBegin+=2;
SUBMENURECT2.nYBegin+=1;
SUBMENURECT2.nLength+=2;
SUBMENURECT2.nHigh+=1;

SetColor(BLUE,BLUE);

SetWindow(&SUBMENURECT2);
clrscr();

Box(&SUBMENURECT,DOUBLELINE,str[GetLanguage()]);
SetWindow(&SUBMENURECT);
SetColor(BOXCOLOR,BOXBACKCOLOR);
clrscr();
return(GetSUBMenu());
}


char *SUBMenuStr[2]={
"TASK",
"任务",
};

#define SUBMENUCOLOR   WHITE
#define SUBMENUBACKCOLOR LIGHTBLUE
#define SUBMENUBARCOLOR   BLACK

int nSUBMenuCurLine=0;

void PrintSUBMenu(void)
{
int i;
char *p;
p=SUBMenuStr[GetLanguage()];
for(i=0;i<6;i++)
{
SetColor(SUBMENUCOLOR,SUBMENUBACKCOLOR);
if(i==nSUBMenuCurLine)
  SetColor(SUBMENUCOLOR,SUBMENUBARCOLOR);
gotoxy(1,i+1);
clreol();
cprintf(" %s %d ",p,i+1);
}
gotoxy(1,6);
}

int GetSUBMenu(void)
{
int key;
while(1)
{
PrintSUBMenu();
FlushBIOSBuf();
key=GetKey();
switch(key)
{
  case DOWNKEY:
  if(++nSUBMenuCurLine>=6)
  nSUBMenuCurLine=0;
  break;
  case UPKEY:
  if(--nSUBMenuCurLine<0)
  nSUBMenuCurLine=5;
  break;
  case 13:
  return(nSUBMenuCurLine+1);
  case 27:
  return(0);
  default:
  break;
}
}
}








/*
Edit buf MUST be more than 32 bytes
*/


RECTSTRUCT EDITRECT={
30,14,36,0
};

#define EDITCOLOR WHITE
#define EDITBACKCOLOR LIGHTBLUE
#define EDITBARCOLOR BLACK


char *EditBox(char *sEditBuf)
{
char *pBuf;
int size;
char *pReturnValue;
size=GetRectSize(&EDITRECT);
pBuf=malloc(size);
assert(pBuf);
SaveScreen(&EDITRECT,pBuf);
pReturnValue=PopEditBox(sEditBuf);
RestoreScreen(&EDITRECT,pBuf);
free(pBuf);
return(pReturnValue);
}

char *PopEditBox(char *s)
{
static char *str[2]={
" Edit Window ",
" 编辑窗口 ",
};
RECTSTRUCT EDITRECT2;

EDITRECT2=EDITRECT;
EDITRECT2.nXBegin+=2;
EDITRECT2.nYBegin+=1;
EDITRECT2.nLength+=1;
EDITRECT2.nHigh+=1;

SetColor(BLUE,BLUE);

SetWindow(&EDITRECT2);
clrscr();

Box(&EDITRECT,SINGLELINE,str[GetLanguage()]);
SetWindow(&EDITRECT);
SetColor(EDITCOLOR,EDITBACKCOLOR);
clrscr();
return(LineEdit(s));
}

char *LineEdit(char *s)
{
static char buf[82];
int key;
int i;
int col=0;

SetColor(EDITCOLOR,EDITBARCOLOR);
cprintf("%s",s);
SetColor(EDITCOLOR,EDITBACKCOLOR);

key=GetKey();
if(key==27)
{
s[0]=0;
return(NULL);
}
if(key==13)
{
strcpy(buf,s);
return(buf);
}
if(key>0xff)
{
strcpy(buf,s);
col+=strlen(s);
}
else
{
buf[0]=key;
buf[1]=0;
col++;
}
while(1)
{
gotoxy(1,1);
clreol();
cprintf("%s",buf);
key=GetKey();

switch(key)
{
  case 8:
  if(col>0)
  {
  movmem(&buf[col],&buf[col-1],strlen(&buf[col])+1);
  col--;
  }
  break;
  case 27:
  buf[0]=0;
  strcpy(s,buf);
  return(buf);
  case 13:
  strcpy(s,buf);
  return(buf);
  default:
  break;
}
if(col+1>EDITRECT.nLength)
{
  cprintf("/a");
  continue;
}
if((key>=' ')&&(key<=126))
{
  for(i=0;i<col;i++)
  if(buf<' ')
  buf=' ';
  else;
  if(buf[col]==0)
  {
  buf[col]=key;
  col++;
  buf[col]=0;
  }
  else
  buf[col]=key;
}
}
}







DOS 下多任务系统的设计与实现 -- 源码 -- 屏幕装入保存

1、LOADDISP.C

#include <fcntl.h>
#include <io.h>
#include <dos.h>

#define DISPMAP "disp.map"
#define GRAPHSIZE 65534

char far *dispbuf=MK_FP(0xb800,0);
char buf[20][256];
int handle;
char cur[2];


loadega(char *sfile) {
unsigned char i,j;

handle=_open(sfile,O_RDWR);
if(handle==-1)
return 1;
_read(handle,&i,1);
_read(handle,cur,2);

_AH=0;
_AL=i;
geninterrupt(0x10);

_read(handle,buf[0],5117);
if(i>8)
{
for(j=1;j<8;j*=2) {
  _ES=FP_SEG(buf[j]);
  _BX=FP_OFF(buf[j]);
  _CX=j;
  _AX=0x1c02;
  geninterrupt(0x10);
}
}
_DL=cur[0];
_DH=cur[1];
_BH=0;
_AH=2;
geninterrupt(0x10);
if(i<8)
readtext();
else
readgraph();

_close(handle);
}

char path[64];
char name[64];

main(int argc,char *argv[]) {
char *s,*t;
s=argv[0];
t=0;
for(;*s;s++) {
if(*s=='//')
  t=s;
}
*(t+1)=0;
sprintf(path,"%s",argv[0]);

if(argc>1)
sprintf(name,"%s",argv[1]);
else
sprintf(name,"%s%s",path,DISPMAP);
loadega(name);
}


readgraph() {
unsigned char far *buffer;
char tmp[2];

buffer=MK_FP(0xa000,0);
tmp[0]=inportb(0x3c4);
outportb(0x3c4,2);
tmp[1]=inportb(0x3c5);
outportb(0x3c5,8);

_read(handle,(char *far)buffer,GRAPHSIZE);
outportb(0x3c5,4);
_read(handle,(char *far)buffer,GRAPHSIZE);
outportb(0x3c5,2);
_read(handle,(char *far)buffer,GRAPHSIZE);
outportb(0x3c5,1);
_read(handle,(char *far)buffer,GRAPHSIZE);

outportb(0x3c5,tmp[1]);
outportb(0x3c4,tmp[0]);
}
readtext() {
int i,a,b;
int c,r;
int x,y;

_read(handle,(char far *)dispbuf,4000);
}


2、SAVEDISP.C

#include <fcntl.h>
#include <io.h>
#include <dos.h>

#define DISPMAP "disp.map"
#define GRAPHSIZE 65534

char far *dispbuf=MK_FP(0xb800,0);

char buf[20][256];
int handle;
char cur[2];

saveega(char *sfile) {
unsigned char i;
for(i=1;i<8;i*=2) {
_ES=FP_SEG(buf);
_BX=FP_OFF(buf);
_CX=i;
_AX=0x1c01;
geninterrupt(0x10);
}
_AH=0xf;
geninterrupt(0x10);
i=_AL;

_BH=0;
_AH=3;
geninterrupt(0x10);
cur[0]=_DL;
cur[1]=_DH;

handle=_creat(sfile,FA_ARCH);
if(handle==-1)
return 1;
_write(handle,&i,1);
_write(handle,cur,2);
_write(handle,buf[0],5117);
if(i<8)
_write(handle,dispbuf,4000);

else
writegraph();

_close(handle);

}
char path[64];
char name[64];

main(int argc,char *argv[]) {
char *s,*t;

s=argv[0];
t=0;
for(;*s;s++) {
if(*s=='//')
  t=s;
}
*(t+1)=0;
sprintf(path,"%s",argv[0]);

if(argc>1)
sprintf(name,"%s",argv[1]);
else
sprintf(name,"%s%s",path,DISPMAP);
saveega(name);
}

writegraph() {
unsigned char far *buffer;
buffer=MK_FP(0xa000,0);

outportb(0x3ce,5);
outportb(0x3cf,0);
outportb(0x3ce,4);
outportb(0x3cf,3);
_write(handle,(void *far)buffer,GRAPHSIZE);
outportb(0x3cf,2);
_write(handle,(void *far)buffer,GRAPHSIZE);
outportb(0x3cf,1);
_write(handle,(void *far)buffer,GRAPHSIZE);
outportb(0x3cf,0);
_write(handle,(void *far)buffer,GRAPHSIZE);

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: DOS系统下的Red Board是什么?Red Board是一种基于DOS操作系统的开发板。它具有易于使用和简单的设计,旨在帮助开发者快速构建和测试基于DOS的应用程序。Red Board的硬件配置包括处理器、内存和存储器等基本组件,以及用于扩展功能的接口和插槽。 在DOS系统下,Red Board提供了一种方便且高效的方式来开发DOS应用程序。它具有友好的用户界面和命令行界面,使开发者可以轻松地编写、调试和执行程序。红板还配备了一些常用的工具和库,以加速开发过程。 Red Board可用于各种应用场景,包括嵌入式系统开发、学术研究和实验等。它可以与外部设备和传感器进行连接,从而实现更复杂的功能。在DOS系统下,Red Board可以实现数据采集、控制设备、通讯等任务。 红板的优点在于它的稳定性和兼容性。作为基于DOS系统的开发板,它具有较低的硬件需求,可以在各种旧版和新版的计算机上运行。此外,使用Red Board开发的应用程序与其他DOS系统兼容,因此可以在更多的计算机上运行。 总之,DOS系统下的Red Board是一款用于开发和测试基于DOS的应用程序的开发板。它提供了一种简单而高效的方式来编写和执行程序,并具有广泛的应用领域和兼容性。 ### 回答2: DOS系统是一种基于磁盘操作系统的计算机操作系统,它是早期个人计算机系统中最为常见的操作系统之一。而红板是指红色基板电路板,它通常用于Arduino开发板。下面是关于DOS系统下红板的一些信息。 在DOS系统下使用红板,通常需要连接红板到计算机的串行端口上,使用串口通信进行数据传输。通过串口,DOS系统可以向红板发送控制指令和数据,以实现与外部设备的交互。 使用红板开发硬件项目时,DOS系统可以作为编程环境。用户可以在DOS系统中使用汇编语言或C语言等编程语言编写代码,通过红板控制外部设备的输入输出。例如,可以编写程序控制红板连接的传感器采集数据,并将数据传输到计算机上进行处理和分析。 此外,DOS系统下常用的集成开发环境(IDE)如Turbo Pascal和Borland C++等也可以用于开发红板项目。这些IDE提供了丰富的功能和工具,使得在DOS系统下开发红板项目更加便捷。 总的来说,使用DOS系统下的红板可以实现与外部设备的交互,进行硬件开发和编程。它为用户提供了一种简单而灵活的开发环境,可以进行各种实验和项目的探索,对于学习和理解计算机硬件和软件之间的关系也具有一定的帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值