当前位置:首页 >> IT/计算机 >>

Pascal高级编程技术:第一章 TURBO PASCAL高级编程技术


第一章 TURBO PASCAL 高级编程技术
TURBO PASCAL 是美国 BORLAND 国际公司的产品,在微机 PASCAL 市场上占有绝对 优势.它克服了往常 PASCAL 编译系统占用大量内存的缺陷,并对标准 PASCAL 作了许多有益的扩充,如它具有与低层软件和硬件打交道的能力,具有强大的图 形图象功能,支持面向对象的程序设计方法,支持 WINDOWS 程序设计等等.它是 一个名副其实的通用系统程序设计语言,十分适合开发一些高级应用软件,数据 库管理系统,编译程序等.另外,TURBO PASCAL 还配备有一个高性能的集成软 件开发环境,包括编辑,编译,调试,文件管理等一系列功能. 本章就使用 TURBO PASCAL 开发高级软件的实用技术进行阐述,介绍如何使用一 些工具和技术,为 TURBO PASCAL 程序员提供方便.本章将讲述在程序设计时使 用单元的技术,TURBO PASCAL 与汇编语言和 C 语言混合编程技术,实现和使用 动态数组的技术,编写中断例程的方法,在程序中使用扩展内存(EMS)和扩充内 存(XMS)的方法以及将程序的标准数据作代码处理的方法等. §1.1 单元及其使用 单元是能与 TURBO PASCAL 程序分开编译的一组 TURBO PASCAL 过程和函数.因为 单元是单独编译的,所以使用单元的程序编译速度快.而且,一个独立的单元可 以为多个程序使用.充分利用单元的优点,不仅可以加快软件的开发速度,而且 可以提高程序可维护性. §1.1.1 单元的结构 一个单元有两部分组成——接口部分和实现部分.如: unit <标识符>; {单元头} interface {接口部分开始} uses <单元列表> {可选项} {公共说明部分} implementation {实现部分开始} {私有说明部分} {过程或函数的定义} begin {初始化部分开始} {初始化代码} end. 1.接口部分 单元的接口部分由保留字 interface 开始, 在单元头和实现部分之间. 在此部分, 说明公用的常量,类型,变量与过程和函数的头部.一个程序如果使用了一个单 元,那么它就能访问该单元的接口部分所定义的所有变量,数据类型,过程和函 数. 接口部分仅包含过程和函数的头部. 过程和函数的实现部分在单元的实现部分定 义.在程序中使用一个单元只需要知道怎样调用单元中的过程,而不需要知道过

程是怎样实现的. 2.实现部分 实现部分是由保留字 implementation 开始.实现部分定义所有在接口部分声明 的过程和函数的程序体.另外实现部分可以有自己的说明,这些说明是局部的, 外部程序是不知道它们的存在的,也不能调用它们. 因为在实现部分中声明的一切对象在作用域上是局部的, 所以实现部分的改变对 其它单元和程序来讲是不可见的.因此,修改一个单元的实现部分,并不需要重 新编译使用该单元的单元, 只需要编译这个修改单元和使用此单元的程序. 然而, 如果接口部分做了修改,所有使用该单元的单元和程序,均需要重新编译,甚至 需要修改. 在实现部分,如果有 uses 子句,则必须紧跟在保留字 implementation 之后. 如果过程说明为 external 类型,则需用{$L 文件名.OBJ}编译指令将其连入程 序. 在接口部分说明的函数或过程,除了 inline 类型之外,都必须在实现部分再现, 它们的头部必须和接口部分一致或用简写格式. 3.初始化部分 单元的整个实现部分通常包括在保留字 implementation 和 end 之间.然而,如 果把保留字 begin 放在 end 之前,在它们中间写一些语句,这些语句就是单元的 初始化部分. 在初始化部分可以初始化任何变量,这些变量可由单元使用,也可通过接口部分 由程序使用.可以在这部分打开文件供程序使用.例如,标准单元 Printer 用它 的初始化部分使所有输出调用都指向文本文件 Lst,这样在 write 语句中就可以 使用它. 当使用单元的程序执行时,在程序的主体执行之前,它所使用的所有单元的初始 化部分按 uses 子句中说明的先后依次被调用. §1.1.2 单元的使用 当使用单元时,需在 uses 语句中将使用的所有单元的名字列出来,单元与单元 之间用逗号(,)隔开.如: uses dos,crt; 当编译器扫描到 uses 子句时,它把每个单元的接口信息加到符号表中,同时又 把实现部分的机器码与程序代码连接起来. 1.单元的直接引用 一个模块(程序或单元)的 uses 子句只须列出该模块直接使用的单元名.例如: program prog; uses unit2; const a = b; begin writeln('a=',a); end. unit unit2; interface

uses unit1; const b = c; implementaion end. unit unit1; interface const c = 1; implementation const b = 2; end. unit2 用了 unit1,主程序用了 unit2,间接地使用了 unit1. 单元的接口部分如果有改动,则所有使用该单元的单元或程序必须重新编译.但 如果改动了单元的实现部分,则用到它的单元不必重新编译.在上例中,如果 unit1 的接口部分改动了(如 C=2),unit2 就必须重新编译;如果只改动实现部分 (b=1),则 unit2 不必重新编译. 编译一个单元时,TURBO PASCAL 计算出该单元的版本数,这个数是单元的接口 部分的校验和.上例中,在编译 unit2 时,unit1 的当前版本数存入 unit2 的编 译版本中,编译主程序时,unit1 的版本数就和存在 unit2 中的版本数比较,若 二者不同,说明 unit2 编译后,unit1 的接口部分改动过,编译器给出错误信息 并重新编译 unit2. 2.单元的循环引用 由于在实现部分使用的单元对用户是不可见的,因此把 uses 子句放在单元的实 现部分,进一步隐藏了单元的内部细节,而且有可能构造出相互依赖的单元. 下面的例子说明两个单元如何相互引用.主程序 Circular 使用 Display 单元, 而 Display 单元在接口部分说明了 Writexy 过程,它有 3 个参数:坐标值 x 和 y 和要显示的文本信息,若(x,y)在屏幕内,Writexy 移动光标到(x,y)并显示信 息,否则,调用简单的错误处理过程 ShowError,而 ShowError 过程反过来又调 用 Writexy 来显示错误信息,这样就产生了单元的循环引用问题. 主程序: program circular; uses crt,display; begin writexy(1,1,'Upper left corner of screen'); writexy(100,100,'Way of the screen'); writexy(81-length('Back to reality'),15,'Back to reality'); end. display 单元: unit display;

interface procedure Writexy(x,y:integer;Message:string); implementation uses CRT,Error; procedure Writexy; begin if (x in [1..80]) and (y in [1..25]) then begin gotoxy(x,y); writeln(message); end else ShowError('Invalid Writexy coordinates'); end; end. Error 单元: unit Error; interface procedure ShowError(ErrMessage); implementation uses display; procedure ShowError; begin Writexy(1,25,'Error: '+ErrMessage); end; end. Display 和 Error 单元的实现部分的 uses 子句互相引用,TURBO PASCAL 能完整 编译两个单元的接口部分,只要在接口部分不相互依赖,在实现部分可以相互调 用. §1.1.3 单元与大程序 单元是 TURBO PASCAL 模块化编程的基础,它用来创建能够为许多程序使用但不 需要源程序的过程和函数库,它是把大程序划分为多个相关的模块基础. 通常, 一个大程序可以划分为多个单元, 这些单元按过程的功能将其分组. 例如, 一个编辑程序可以划分成初始化, 打印, 读写文件, 格式化等若干个部分. 另外, 也可以有一个定义全局常量,数据类型,变量,过程及函数的"全局"单元,它 能被所有单元和主程序使用. 一个大程序的框架如下: program Editor; uses dos,crt,printer, EditGlobal; EditInit;

EditPrint; EditFile; EditFormat; ...... begin ... end. 在大程序开发中使用单元的另一个原因是与代码段的限制有关.8086 处理器要 求代码段长度最大为 64K.这意味着主程序及任何单元都不能超过 64K.TURBO PASCAL 将每个单元放在一个单独的段中来解决这个问题. §1.2 与汇编语言混合编程 TURBO PASCAL 以编译速度快,生成的目标代码高速和紧凑而著称.在大多数情 况下,只使用 TURBO PASCAL 即可以完成各种各样的程序编制,但是,在硬件接 口程序,实时控制程序及大规模浮点运算时,都需要用汇编语言来编程.虽然 TURBO PASCAL 提供了 INLINE 语句和命令,以及内嵌式汇编语言(TURBO PASCAL 6.00),但这是远远不够的.本节详细讨论 TURBO PASCAL 与汇编语言混合编程的 技术,并列举了大量的实例. §1.2.1 TURBO PASCAL 的调用协定 TURBO PASCAL 程序与外部汇编子程序混合编程时,要涉及到子程序的调用方式, 函数或过程的参数传递方法和函数如何返回值的问题,现分述如下. §1.2.1.1 调用子程序的方式和子程序的返回方式 TURBO PASCAL 程序在调用汇编子程序时,可以是近调用也可以是远调用,因此, TURBO PASCAL 程序在对汇编子程序进行调用时,根据调用方式的不同,有两种 不同的保存返回地址的方法:①近调用时,因是段内调用,仅将偏移地址 IP 入 栈,占 2 字节;②远调用时,因是段间调用,要将代码段值 CS 和偏移地址 IP 入栈,占 4 字节. 在主程序中直接调用汇编子程序时,一般采用近调用,汇编子程序采用近返回方 式,用 RET 指令;在 TURBO PASCAL 的单元中使用汇编子程序时分两种情况:① 在单元接口部分说明的子程序,在汇编子程序中要用远返回,用 RETF 指令;② 在单元解释部分说明的子程序,汇编子程序要用近返回方式,用 RET 指令. 汇编子程序在运行结束后,为了能正确地返回到调用程序,栈顶指针必须指向正 确的返回地址, 它通过在返回指令 RETF(或 RET)中给出参数入栈时所占的字节数 的方法实现的. §1.2.1.2 参数传递的方法 TURBO PASCAL 是利用堆栈向过程和函数传递参数的,参数按从左到右的顺序(说 明顺序)被压入堆栈中,例如调用过程 PROC(A,B,C : INTEGER; VAR D)时,其 堆栈情况见图 1-1.

殌 ┌————————————┐ │+0E│ 参数 A 的值 │ │ ├————————————┤ 参 │+0C│ 参数 B 的值 │ │ ├————————————┤ 数 │+0A│ 参数 C 的值 │ │ ├————————————┤ 压 │ +8│ 参数 D 的段地址 │ │ ├————————————┤ 栈 │ +6│ 参数 D 的偏移地址 │ │ ├————————————┤ 顺 │ +4│ 过程返回的段地址 │ │ ├————————————┤ 序 │ +2│ 过程返回的偏移地址│ ↓ ├————————————┤

↑ │ │参 │ │数 │ │出 │ │栈 │ │顺 │ │序 │

│BP 寄存器的值 │ └———————————————┘ 殣 图 1-1.TURBO PASCAL 远调用汇编程序 PROC 的堆栈情况 TURBO PASCAL 在调用子程序时,有两种传递参数的方法,即传值和传地址的方 法.下面分别说明这两种参数传递方法.各种类型参数入栈的方法见表 1-1. (1)传值方式 在 TURBO PASCAL 的过程或函数的形式参数表中,以值参形式定义的参数,且类 型是记录,数组,字符串,指针等复合类型以外的各种类型,如字节型(BYTE), 短整型(SHORTINT),整型(INTEGER),字型(WORD),长整型(LONGINT),字符型 (CHAR),布尔型(BOOLEAN),实数型(REAL)等,TURBO PASCAL 在调用子程序时, 直接将实参值依次从左到右顺序压入堆栈中, 汇编子程序可以直接从堆栈中取得 实参的值. (2)传地址方式 在 TURBO PASCAL 的过程或函数的形式参数表中,以变量形式定义的参数,及以 记录,字符串,数组,指针等复合类型定义的值参,TURBO PASCAL 在调用子程 序时,是将调用程序的实参地址依次按从左到右的顺序压入堆栈的.汇编子程序 从堆栈中取得实参的地址,即可得到参数的值.同样汇编子程序可以把运算结果 存放到对应的变量中,以便传回调用程序. 表 1-1.各种类型参数入栈的方法 殌

┌———————┬————┬—————┐ │形参类型 │传递方式│栈中字节数│ ├———————┼————┼—————┤ │char,boolean │ │ │ │byte,shortint,│ 传值 │ 2 │ │integer,word │ │ │ ├———————┼————┼—————┤ │longint,single│ 传值 │ 4 │ ├———————┼————┼—————┤ │real │ 传值 │ 6 │ ├———————┼————┼—————┤ │double │ 传值 │ 8 │ ├———————┼————┼—————┤ │string,pointer│ 传地址 │ 4 │ │变量 │ │ │ └———————┴————┴—————┘殣 §1.2.1.3 函数返回值的传递 TURBO PASCAL 函数返回值的传递方式根据函数返回值类型的不同而异,有采用 传地址的方式进行,也有采用寄存器方式进行,如采用传地址的方式,其地址(4 字节)首先入栈,然后才压入函数参数,最后压入函数的返回地址.各种函数返 回类型的传递方式见表 1-2. 表 1-2.各种函数返回类型的传递方式 ┌———————┬———————————┬——————┐ │ 函数返回类型 │ 返 回 方 式 │ 所占字节数 │ ├———————┼———————————┼——————┤ │boolean,byte, │ 在寄存器 AL 中 │ 1 │ │char,shortint │ │ │ ├———————┼———————————┼——————┤ │word,integer │ 在寄存器 AX 中 │ 2 │ ├———————┼———————————┼——————┤ │longint │ 高位在 DX,低位在 AX │ 4 │ ├———————┼———————————┼——————┤ │real │由高到低在 DX,BX,AX 中 │ 6 │ ├———————┼———————————┼——————┤ │pointer │段地址在 DX,偏移在 AX │ 4 │ ├———————┼———————————┼——————┤ │string │在 DS:SI 指的地址中 │ 不限 │ └———————┴———————————┴——————┘

§1.2.2 汇编子程序的编写格式 根据 TURBO PASCAL 的调用协定,外部汇编子程序的通用编写格式如下: Title 程序名 Dosseg Locals @@ .Model TPascal .Code Assume Cs:@Code Public 过程或函数名 过程或函数名: Push BP MOV BP,SP … POP BP Retf 参数占堆栈字节数 END 上述汇编子程序是 TURBO ASSEMBLER 的格式,本文汇编子程序均采用这种格式. 对此汇编子程序格式说明如下: . 汇编模块要采用 TPASCAL 模式; . 在汇编模块中,必须把 TURBO PASCAL 调用的过程或函数说明为 PUBLIC 属性; . 子程序返回指令视具体情况而定,近调用用 RET,远调用用 RETF; . 返回指令后的参数是指该子程序形式参数表中所有参数入栈后所占堆栈的字 节数; . 汇编模块结束要写 END. §1.2.3 TURBO PASCAL 程序的编写格式 在 TURBO PASCAL 中,声明外部子程序的格式如下: procedure prc(a, b : integer; var c : real);external; function func(a, b : integer) : real; external; 即在通常的 TURBO PASCAL 过程或函数的声明后加上 external 关键字.在声明了 外部过程或函数的主程序或程序单元中,要用编译指令{$L},把汇编好的目标模 块加载进来. 在 TURBO PASCAL 程序中使用外部汇编过程或函数时, 方法和一般的 TURBO PASCAL 过程和函数没有两样. §1.2.4 主程序中使用外部汇编子程序的典型例子分析 在 TURBO PASCAL 主程序中直接使用外部汇编子程序时,一般采用近调用方式, 所以汇编子程序返回指令为 RET,在特别指明采用远调用方式时,要用 RETF 返 回指令. 1.无参数传递的过程 program prog1; {$L prog1.obj}

procedure DisplayOk;external; begin DisplayOk; end. Title PROG1 LOCALS @@ DOSSEG .MODEL TPASCAL .CODE ASSUME CS:@CODE OkMsg db 'OK !',0dh,0ah,'$' ; Procedure DisplayOk PUBLIC DisplayOk DisplayOk: push ds ;保存数据段 push cs ;代码段入栈 pop ds ;弹出数据段 mov ah,09 ;显示字符串 mov dx,offset OkMsg ;字符串地址 int 21h ;DOS 功能调用 pop ds ;恢复数据段 ret ;近返回 end ;汇编子模块结束 2.传递字符型值参的过程 program prog2; {$L prog2.obj} procedure DisplayInt(ch : char);external; begin DisplayInt('a'); end. Title PROG2 LOCALS @@ DOSSEG .MODEL TPASCAL .CODE ASSUME CS:@CODE ; Procedure DisplayInt PUBLIC DisplayInt DisplayInt: push bp mov bp,sp

mov mov int pop ret end

ah,02 ;显示字符 dl,[bp+4] ;从栈中取参数 21h ;DOS 功能调用 bp 2 ;形式参数在栈中占 2 字节

3.传递字符型值参和整型变参的过程 program prog3; {$L prog3.obj} procedure ProcArg(ch : char;var i : integer);external; var i : integer; begin ProcArg('d',i); writeln(i); end. Title PROG3 LOCALS @@ DOSSEG .MODEL TPASCAL .CODE ASSUME CS:@CODE ; Procedure ProcArg PUBLIC ProcArg ProcArg: push bp mov bp,sp xor ax,ax mov al,byte ptr [bp+8] ;取字符参数 les si,[bp+4] ;取整数变量的地址 mov es:[si],al pop bp ret 6 ;形式参数在栈中占 6 字节 end 4.传递字符值参返回整型的函数 program prog4; {$L prog4.obj} function func(ch : char) : integer; external; begin writeln(func('a')); end.

Title PROG4 LOCALS @@ DOSSEG .MODEL TPASCAL .CODE ASSUME CS:@CODE ; Procedure func PUBLIC func func: push bp mov bp,sp xor ax,ax mov al,byte ptr [bp+4] ;取字符参数值 pop bp ret 2 ;形式参数在栈中占 2 字节 end ;子程序返回值在寄存器 AX 中 5.传递字符串型值参和返回字符串型的函数 program prog5; {$L prog5.obj} function func(s : string) : string; external; begin writeln( func('abcd') ); end. Title PROG5 LOCALS @@ DOSSEG .MODEL TPASCAL .CODE ASSUME CS:@CODE ; Procedure func PUBLIC func func: push bp mov bp,sp push ds xor ch,ch lds si,[bp+4] ;取字符串 S 的地址 les di,[bp+8] ;取返回值地址 mov cl,[si] inc cl cld @@1:

lodsb stosb loop @@1 pop ds pop bp ret 4 ;字符串 S 的地址在栈中占 4 字节 end 6.传递长整型值参和返回长整型的函数 program prog6; {$L prog6.obj} function func(li : LongInt) : Longint; external; var i : longint; begin i := func(111111110); writeln(i); end. Title PROG6 LOCALS @@ DOSSEG .MODEL TPASCAL .CODE ASSUME CS:@CODE ; Procedure func PUBLIC func func: push bp mov bp,sp mov ax,[bp+4] ;取长整型数高位 mov dx,[bp+6] ;取长整型数低位 les si,[bp+8] ;取函数返回地址 mov es:[si],dx mov es:[si+2],ax pop bp ret 4 ;长整型数 LI 在栈中占 4 字节 end 7.传递实型数值参和返回实型数的函数 program prog7; {$L prog7.obj} function func(r : real) : real; external; var r : real;

begin r := func(11111.1110); writeln(r); end. Title PROG7 LOCALS @@ DOSSEG .MODEL TPASCAL .CODE ASSUME CS:@CODE ; Procedure func PUBLIC func func: push bp mov bp,sp mov ax,[bp+4] ;取实数 R 的值 mov bx,[bp+6] ; mov dx,[bp+8] ; les si,[bp+0ah] ;取函数的返回地址 mov es:[si],dx mov es:[si+2],bx mov es:[si+4],ax pop bp ret 6 ;实数 R 在栈中占 6 字节 end §1.2.5 单元中使用汇编模块的情况 在下面的演示单元 DEMOU 中声明了两个外部汇编函数,P1 是在单元接口部分定 义的,在汇编模块中采用远返回方式,P2 是在单元的解释部分声明的,在汇编 模块中采用近返回方式.在单元 DEMOU 的过程 P3 中又调用了函数 P1 和 P2,调 用 P2 采用近调用,没有问题;当调用 P1 时,因其是在接口部分定义的,必须采 用远调用方式,这可以用编译指令{$F}来完成,在调用 P1 之前,加上 {$F+}, 在调用之后,加上{$F-}即可. program prog8; uses demou; begin if p1(1) then Writeln('Far call complete !'); p3; end. unit demou; interface

function p1(a : integer) : boolean; procedure p3; implementation {$L demou.obj} function p1( a : integer) : boolean; external; function p2( a : char ) : boolean; external; procedure p3; begin if p2('a') then writeln('Near call complete !'); {$F+} ;打开远调用编译指令 if p1(1) then Writeln('Far call again !'); {$F-} ;关闭远调用编译指令 end; end. Title DEMOU LOCALS @@ DOSSEG .MODEL TPASCAL .CODE ASSUME CS:@CODE ; function p1 PUBLIC p1 p1: push bp mov bp,sp xor ax,ax cmp ax,[bp+4] jnz @@1 mov al,0 jmp @@2 @@1: mov al,1 @@2: pop bp retf 2 ;此函数在单元接口部分定义 ; function p2 PUBLIC p2 p2: push bp mov bp,sp mov ax,'a' cmp ax,[bp+4] jnz @@3 mov al,0 jmp @@4

@@3: mov al,1 @@4: pop bp ret 2 ;此函数在单元解释部分定义 end §1.2.6 小结 本节详细地介绍了 TURBO PASCAL 与汇编语言混合编程的技术,并给出了许多典 型实例,读者可以参照实例的格式进行混合语言编程,解决工作中的具体问题. 高级语言和汇编语言混合编程是一个比较复杂的事情, 只有在实践中不断细心体 会,积累经验,才能有所提高.在对混合语言编写的程序进行调试时,不妨多使 用 TURBO DEBUGGER,它可以帮助你发现许多不易发现的汇编语言的错误,有利 于加快程序的开发进程. §1.3 与 C 语言混合编程 一般来说,高级语言间的相互调用比较困难.对 TURBO 系列软件来说,BORLAND 公司提供了语言之间相互调用的接口机制,下面介绍 TURBO PASCAL 和 TURBO C/C++混合编程的方法步骤.TURBO PASCAL 的调用协议在上一节中已经叙述,这 里不再赘述. §1.3.1 TURBO C/C++程序的编写与编译 用 TURBO C 编写的供 TURBO PASCAL 调用的模块的一般形式如下: /* C moduler for Turbo PAscal */ 类型 1 far 函数名 1(形参表) /* 在单元接口部分定义 */ { ... } 类型 2 near 函数名 2(形参表) /* 在程序或单元实现部分定义 */ { ... } 其中,第一个函数的说明部分使用了调用模式说明 far,它是在 TURBO PASCAL 单元的接口部分定义的,需要使用远调用.第二个函数用了 near 调用模式,它 是在单元的实现部分或程序中定义的,采用近调用. 编写供 TURBO PASCAL 程序使用的 TURBO C 模块应当遵循如下的规则: (1)在 TURBO PASCAL 单元的实现部分或主程序中直接定义的 C 函数,调用类型应 当说明为 near;在 TURBO PASCAL 单元的接口部分定义的 C 函数,调用类型应当 说明为 far; (2)公用数据应当在 TURBO PASCAL 程序中定义,TURBO C 模块中定义的数据不能 被 TURBO PASCAL 程序引用; (3)由于没有正确的段名, TURBO C/C++的运行库例程不能在 TURBO C 模块中使用.

但是,当你拥有 TURBO C/C++运行库例程的源码时,可以在你的 C 模块中包含进 库例程的原型,重编译单个库例程模块,这样即可使用相关的库例程; 将编写好的 TURBO C/C++程序模块编译成目标文件,需要遵循如下的规则: (1)任何 C 模块均用小内存模式(SMALL)编译; (2)把 TURBO C/C++的代码生成编译开关设置为 PASCAL; (3)段名设置如下: CODE names 的 Segment name 设置为 CODE,Group name 和 Class name 设为空; DATA names 的 Segment name 设置为 CONST,Group name 和 Class name 设为空; BSS names 的 Segment name 设置为 DATA,Group name 和 Class name 设为空; 或者,用 TURBO PASCAL 系统盘上提供的 TURBO C/C++的配置文件 TURBOC.CFG 来 编译 C 模块的源程序.方法有二: (1)在包含 TURBOC.CFG 和 C 模块子目录下,执行: TCC C 模块名.C (2)执行: TC /CCTOPAS.TC C 模块名.C 把 C 模块编译为目标模块,即可在 TURBO PASCAL 程序中引用.其中 CTOPAS.TC 和 TURBOC.CFG 都可以在 TURBO PASCAL 或 TURBO C 的系统盘上找到. §1.3.2 TURBO PASCAL 程序的编写 TURBO PASCAL 程序与普通的 TURBO PASCAL 程序没有两样,只是把有关的 C 函数 定义为外部函数,并用编译开关{$L 文件名}将 C 模块的目标模块(.OBJ)连接到 PASCAL 程序中即可. §1.3.3 程序中使用 TURBO C 函数的实例 在 TURBO PASCAL 的主程序中使用 TURBO C 模块定义的函数, 则 C 模块中的函数 一般均定义为 near 调用类型.实例如下: PASCAL 主程序 CPASDEMO.PAS: program CPASDEMO; uses Crt; var Factor : Word; {$L CPASDEMO.OBJ} function Sqr(I : Integer) : Word; external; { Change the text color and return the square of I } function HiBits(W : Word) : Word; external; { Change the text color and return the high byte of W }

function Suc(B : Byte) : Byte; external; { Change the text color and return B + 1 } function Upr(C : Char) : Char; external; { Change the text color and return the upper case of C } function Prd(S : ShortInt) : ShortInt; external; { Change the text color and return S - 1 } function LoBits(L : LongInt) : LongInt; external; { Change the text color and return the low word of L } procedure StrUpr(var S : string); external; { Change the text color and return the upper case of S-Note that } {the Turbo C routine must skip the length byte of the string. } function BoolNot(B : Boolean) : Boolean; external; { Change the text color and return NOT B } function MultByFactor(W : Word) : Word; external; { Change the text color and return W * Factor - note } { Turbo C's access of Turbo Pascal's global variable. } procedure SetColor(NewColor : Byte); { A procedure that changes the current } begin { display color by changing the CRT } TextAttr := NewColor; { variable TextAttr } end; { SetColor } var S : string; begin Writeln(Sqr(10)); { Call each of the functions defined } Writeln(HiBits(30000)); { passing it the appropriate info. } Writeln(Suc(200)); Writeln(Upr('x')); Writeln(Prd(-100)); Writeln(LoBits(100000)); S := 'abcdefg'; StrUpr(S);

Writeln(S); Writeln(BoolNot(False)); Factor := 100; Writeln(MultbyFactor(10)); SetColor(LightGray); end. C 模块 CPASDEMO.C: typedef unsigned int word; typedef unsigned char byte; typedef unsigned long longword; extern void setcolor(byte newcolor); /* procedure defined in Turbo Pascal program */ extern word factor; /* variable declared in Turbo Pascal program */ word sqr(int i) { setcolor(1); return(i * i); } /* sqr */ word hibits(word w) { setcolor(2); return(w >> 8); } /* hibits */ byte suc(byte b) { setcolor(3); return(++b); } /* suc */ byte upr(byte c) { setcolor(4); return((c >= 'a') && (c <= 'z') ? c - 32 : c); } /* upr */ char prd(char s) { setcolor(5); return(--s); } /* prd */

long lobits(long l) { setcolor(6); return((longword)l & 65535); } /* lobits */ void strupr(char far *s) { int counter; for (counter = 1; counter <= s[0]; counter++) /* Note that the routine */ s[counter] = upr(s[counter]); /* skips Turbo Pascal's */ setcolor(7); /* length byte */ } /* strupr */ byte boolnot(byte b) { setcolor(8); return(b == 0 ? 1 : 0); } /* boolnot */ word multbyfactor(word w) { setcolor(9); /* note that this function accesses the Turbo Pascal */ return(w * factor); /* declared variable factor */ } /* multbyfactor */ §1.3.4 TURBO PASCAL 单元中使用 TURBO C 函数的实例 在 TURBO PASCAL 单元中使用 TURBO C 模块定义的函数,则 C 模块中的函数的调 用方式可以是 near 和 far 两种类型.下面给出一个简单的例子. PASCAL 主程序 CPDEMO.PAS: program CTOPASDEMO; uses CPUNIT; begin writeln(add2(3)); DisplaySub2(3); end. PASCAL 单元 CPUNIT.PAS: unit CPUNIT;

interface function add2(x : integer) : integer; procedure DisplaySub2(x: integer); implementation {$L CTOPAS.OBJ} function add2; external; function sub2(x : integer) : integer; external; procedure DisplaySub2; begin WriteLn(Sub2(x)); end; end. C 模块 CTOPAS.C: int far add2( int x) { return (x + 2); } int sub2(int x) { return(x - 2); } TURBO PASCAL 和 TURBO C 均是目前比较流行的编程语言,广大编程人员如果能 正确熟练地使用 TURBO PASCAL 和 TURBO C 进行混合编程,可以达到事半功倍的 效果,使软件的开发得以加速. §1.4 过程类型及其使用 TURBO PASCAL 允许使用过程类型, 把过程或函数当做能赋给变量或传给参数 对象.在过程类型说明中,定义过程或函数的参数和函数的返回类型. §1.4.1 过程类型的说明 过程类型的说明方法如下: TYPE Proc0 = Procedure; Proc1 = Procedure(x : integer); func0 = function : integer; 的

func1 = function(x : integer) : boolean; 过程类型说明中的参数名完全是装饰性的,并无实际意义. §1.4.2 过程类型常量 过程类型常量必须指定过程或函数的标识符, 而且过程或函数必须与常量的类型 赋值兼容.例如: Type ErrorProc = Procedure(ErrorCode : integer); Procedure DefaultError(ErrorCode : integer); far; begin Writeln('Error ',ErrorCode,'.'); end; const ErrorHandler : ErrorProc = DefaultError; §1.4.3 过程类型变量 过程类型说明之后,就可以用来定义变量,这种变量叫做过程变量.如: Var f01,f02 : func0; f11,f12 : func1; p1 : proc1; 象整型变量一样,过程变量能赋以过程值.过程值可以是另一过程变量,一可以 是过程或函数的标识符.比如有如下过程和函数说明: function f1 : integer;far; begin f1 : = 1; end; 则下面的语句是成立的: f01 := @f1; f02 := f01; 把函数或过程赋给过程变量,必须满足下列要求: . 函数或过程必须是 far 调用类型 . 不能是标准过程或函数 . 不能是嵌入式过程或函数 . 不能是 INLINE 过程或函数 . 不能是 INTERRUPT 过程 过程类型的使用不限于简单变量,象其它类型一样,过程类型变量可以作为结构 类型的分量.如: type GotoProc = procedure(x,y:integer); procList = array[1..10] of GotoProc; WindowPtr = ^WindowRec; WindowRec = Record

Next : WindowPtr; Header : string[31]; Top,Left,Bottom,Right : integer; SetCursor : GotoProc; end; var p : ProcList; W : WindowPtr; 这样说明之后,下面语句是合法的过程调用: p[3](1,1); W^.SetCursor(10,10); 过程值赋给过程变量,实际上是把过程的地址存放到变量中.过程变量很象指针 指针量,只是指针变量指向数据,过程变量指向过程或函数的入口.过程变量占 4 个字节,第一个字存放地址的偏移量,第二个字存放段地址. §1.4.4 表达式中的过程类型 通常,在语句或表达式中使用过程变量表示对变量中储存的过程或函数的调用, 但也有例外,当 TURBO PASCAL 在一个赋值语句的左边发现一个过程变量时,它 就认为右边一定是一个过程值.例如: type IntFunc = function : integer; var F : IntFunc; N : integer; function ReadInt : integer; far; var i : integer; begin readln(i); ReadInt := i; end; begin F := ReadInt; N := ReadInt; end. 主程序中的第一个语句将过程值 ReadInt 赋给过程变量 F,第二个语句调用 ReadInt,将返回值赋给 N. 对语句: if F = ReadInt then Writeln('Equal'); 的解释是, 调用 F 和 ReadInt, 然后比较它们的返回值, 如果相等, 则打印 Equal. 若要比较 F 和 ReadInt 的过程值,必须使用以下的结构: if @F = @ReadInt then Writeln('Equal'); 当给出过程变量或过程,函数标识符是时,地址操作符@可以阻止编译器调用该 过程,函数,同时将参量转换为一个指针,@F 将 F 转换为包含地址的无类型指

针,@ReadInt 返回 ReadInt 的地址. 当要取得过程变量的内存地址时,必须连用两个地址操作符(@@).如,@F 表示 将 F 转换为一个无类型指针变量,@@F 则表示返回变量 F 的物理地址. §1.4.5 过程类型参数 由于过程类型可在任何场合使用, 所以可把过程类型作为过程或函数的参数进行 传递,这样在某些场合可以大大简化程序的编制工作.下面两例说明了过程参数 的使用. 利用 Simpson 公式,求 S=∫攩 b 搅揽 a 攭 f(x)dx.采用逐步近似逼近方 法:S=h/3*(f 揽 0 攭+4*(f 揽 1 攭+f 揽 3 攭+...+f 揽 n-1 攭)+2*(f 揽 2 攭+f 揽 4 攭+...+f 揽 n-2 攭+f 揽 n 攭),其中 f 揽 i 攭=f(a+i*h),h=(b-a)/n. program Simpson_Method; type Func = function(x: real) : real; {$F+} function Simpson(f : Func; a,b : real; n : integer) : real; var h,s : real; i : integer; begin h := (b-a) / n; s := f(a) + f(b); for i := 1 to n-1 do if odd(i) then s := s + 4 * f(a + i*h) else s := s + 2 * f(a + i*h); Simpson := s * h / 3; end; function f1(x : real) : real; begin f1 := sqr(x) * sin(x); end; function f2(x : real) : real; begin f2 := 1 / (1 + x); end; begin Writeln('∫1,2(x^2*sin(x) = ',SimpSon(f1,1,2,10):10:2);

Writeln('∫1,10(1/(1+x)) = ',SimpSon(f1,1,10,20):10:2); end. 利用过程参数,设计一个通用的打印自然对数和常用对数表的程序. program PrintLogTable; type Func = function(x : integer) : real; {$F+} function log(x : integer) : real; begin log := ln(x) / ln(10); end; function lne(x : integer) : real; begin lne := ln(x); end; procedure PrintTable(i,w : integer; f : func); var m,n : integer; begin m := 0; n := 0; repeat inc(m); inc(n); write(m:3,f(m):7:4,' '); if n = w then begin writeln; n := 0; end; until m = i; writeln; end; begin PrintTable(1000,7,log); PrintTable(1000,7,lne); end. §1.5 中断例程的编写方法

TURBO PASCAL 的运行库和由编译程序产生的代码是可中断的.大多数运行库是 可重入的,这允许你用 TURBO PASCAL 编写中断例程. §1.5.1 编写中断例程 中断过程可用 INTERRUPT 指令声明.每个中断程序必须用下列过程头来说明: procedure IntHandle(Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP : WORD); INTERRUPT; BEGIN ... END; 所有寄存器都是作为参数传递的,所以在源代码中可以使用和修改,并且可以省 略部分和全部参数. 在入口时,中断过程自动保存所有的寄存器,并初始化 DS 寄存器;出口代码恢 复所有寄存器,并执行中断返回指令. §1.5.1.1 常驻型中断例程(ISR)的框架 Program 程序名; {$M 栈长度,0,0} uses dos; var ... procedure 中断过程名(Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP : WORD); INTRRUPT; begin ... end; begin SetIntVec(中断号,@中断过程名); keep(k); end. 其中编译指令$M 是 Keep 过程要求的,栈段的长度值为 1024~65520,最大和最 小堆长度经常都说明为 0. §1.5.1.2 暂驻型中断例程的框架 Program 程序名; uses dos; var p : pointer; ... procedure 中断过程名(Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP : WORD); INTRRUPT;

begin ... end; ... begin GetIntVec(中断号,p); SetIntVec(中断号,@中断过程名); ... SetIntVec(中断号,p); end. 在暂驻型程序中,在设置自己的中断向量之前,首先把系统原有的中断向量存放 到有关的变量中,等到程序运行结束时,要把程序中所有重新定义过的中断向量 恢复到系统原有的状态.否则程序返回操作系统后随时都会造成死机. §1.5.2 设置中断向量,调用中断功能,程序驻留内存 在使用自己编写的中断例程的程序中, 可用 SetIntVec 来设置自己的中断服务程 序: SetIntVec(IntNo,@IntHandle); IntNo 为中断号,@IntHandle 为中断服务过程的地址. 在程序中可以使用 Intr 过程来调用自己的中断过程: Intr(IntNo,Regs); 当用 TURBO PASCAL 编写的中断例程要驻留内存时,可以使用 Keep 过程来实现: Keep(ExitCode); §1.5.3 建立通讯数据区 由于中断例程的代码段地址可以从中断向量表中直接得到, 用这个地址加上适当 的地址偏移量,作为预定义内存数组 Mem/MemW 和 MemL 的下标,即可直接访问这 些特定的内存单元,因此可以建立 ISR 的通讯数据区. 例如,为了防止同一个 ISR 在内存中重复驻留,应当设置一个"已驻留"的标志. 如果在 ISR 最前面的过程的第一条语句中,把一条在驻留之前显示的屏幕提示, 赋给一个动态串.通常这条提示串在显示完成后,其内容已不再需要,但可以通 过引用内存数组元素 MEMW[中断入口段地址:1],对其中的内容加以比较,即可 知道 ISR 是否驻留,新的 ISR 是否可以驻留. §1.5.4 释放驻留内存 一个设计优秀的 ISR,在撤离时,它所占用的内存也应该被释放.在用 TURBO PASCAL 4.0 或更高版本编译的程序中,有一个预定变量 PrefixSeg,保存着该程 序的程序段前缀 PSP 的起始段地址.把这个段地址作为调用参数送入 ES 寄存器 之后,再调用 INT 21H 的 49H 号功能,即可把 ISR 自身占用的内存予以释放. 除了 ISR 自身占用的驻留内存空间外,DOS 还为它另外分配了一块内存空间,作 为它的环境块.其中存放着 DOS 环境参数的副本.虽然环境块不大,但它们同样 也驻留着. PSP 的地址偏移量为 2CH 的位置上已经存放着上述环境块的段地址. 在

将内存数组元素 MEMW[Prefixseg:$2C]的值送入 ES 寄存器,再一次调用 INT 21H 的 49H 号功能, 就能够把 ISR 的环境块也释放掉, 从而回收 ISR 的全部驻留空间. 值得注意的是:如果在回收到的内存空间的高端地址处,还有其它未释放的驻留 块,则已经回收的内存空间就会成为待用自由链中的"碎块".这些碎块将会影 响到以后的内存分配情况.在重新启动系统之前,其中的一部分有可能不能再次 进行分配.因此在使用过程中应当先引导那些不需要撤换的 ISR,需要反复撤换 的 ISR 则放在最后引导. 此外,应当牢记:释放 ISR 的驻留内存之前,一定要恢复原来的中断向量,否则 会造成系统混乱,导致死机或其它故障. 能够释放驻留内存,就可以在用户需要的时侯,任意调换不同的 ISR,以满足各 种不同的使用要求,并能随时保持较小的内存开销. §1.5.5 程序实例 本程序(INTR.PAS)演示常驻型中断例程(ISR)的编写,并演示了怎样建立通讯数 据区和程序的撤离方法,释放所有驻留内存. {$M $1000,0,0} program intrrupt_example; uses dos; const MyMark:string[8] = 'MyInt500'; var OldInt5,MyIntr5 : longint; Mark : string[10]; reg : registers; procedure First; begin Mark := 'MyInt500'; end; procedure MyInt5(flags,cs,ip,ax,bx,cx,dx,si,di,ds,es,bp:word); interrupt; begin inline($fa); myintr5 := meml[0:5*4]; memw[0:5*4] := $a; inline($fb); if ax and $ff00 = $fe00 then begin inline($fa);

meml[0:4*5] := oldint5; reg.es := prefixSeg; reg.ah := $49; msdos(reg); reg.es := memw[PrefixSeg:$2c]; reg.ah := $49; msdos(reg); inline($fb); end else begin writeln('Call Interrupt 5'); inline($fa); meml[0:5*4] := myintr5; inline($fb); end; end; procedure SetInt5; begin setIntvec($1b,saveint1b); inline($fa); OldInt5 := meml[0:4*5]; SetIntVec(5,@MyInt5); mem[memw[0:4*5+2]:$a] := $cf; inline($fb); end; function CheckInt5 : boolean; var i,j : word; int5 : pointer; begin getIntVec(5,int5); checkInt5 := true; j := ofs(mark); for i := 1 to 8 do begin if (chr(Mem[seg(int5^):i]) <> MyMark[i]) then CheckInt5 := false; end; end; procedure RemoveInt5; begin

if CheckInt5 then begin reg.ah := $fe; intr(5,reg); writeln('Intrrupt 5 has been removed for memory'); end else writeln('Not find external Intrrrupt 5 routine'); end; begin first; if paramcount = 0 then begin if CheckInt5 then begin writeln('Int 5 has kept'); halt(1); end else begin SetInt5; keep(0); end end else if (paramstr(1) = 'R') or (paramstr(1) = 'r') then RemoveInt5; end. §1.6 动态数组及其使用 §1.6.1 TURBO PASCAL 的内存分配 TURBO PASCAL 将计算机的可用内存划分为 4 个部分,如图 1-2 所示.代码段用 于存放编译后程序指令;数据段用于存放程序和单元的常量和全程变量;栈段用 于存放程序中过程和函数的局部变量;堆段用于存放程序的动态变量. 殌 ┌————┐← 最高可用内存 堆段 →│动态变量│↑向上分配 ├————┤← 栈段 →│局部变量│ ├————┤ 数据段→│数据段 │ ├————┤

代码段→│程序指令│ └————┘← 最低可用内存 殣 图 1-2 TURBO PASCAL 程序的内存分配图 堆空间是一个由用户控制的动态数据区域, 它是利用指针变量在程序运行时动态 分配的.也就是说,程序在编译时并不为指针变量在堆上分配空间,而在程序运 行时,执行有关的语句时,才为其在堆上分配空间.堆的空间虽然能在 0~640K 之间变动,但在其中建立的每个动态变量的体积均不能大于 65521 字节. §1.6.2 构造动态数组的方法 根据 TURBO PASCAL 对内存的管理方法,对总体积不大于 64K 的数组,可以直接 在堆空间进行分配,在程序运行中构造动态数组.利用指针在堆中建立动态数组 的步骤如下: 1.数组及其指针类型的说明 Type IntArrPtr = ^IntArray; IntArray = array[1..100] of integer; 2.指针变量的说明 Var IntArr : IntArrPtr; 3.申请内存 在使用动态数组之前,用 New 或 GetMem 过程在堆中分配内存空间,建立动态数 组及它们的指针值.如: GetMem(IntArr,Sizeof(IntArr^)); 4.引用 在程序中按一般的 TURBO PASCAL 动态变量引用规则使用动态数组.如: writeln(IntArr^[10]); 5.释放内存 动态数组使用完毕,立即用 Dispose 或 FreeMem 过程释放堆空间.如: FreeMem(IntArrPtr,Sizeof(IntArr^)); 下面的程序演示了上述方法,它在堆中建立了一个 10000 个元素的实型数组 A. program Dynamic_Array; type Arr1 = array[1..10000] of real; var A : ^arr1; i : integer; begin GetMem(A,sizeof(a^)); for i := 1 to 10000 do a^[i] := i; for i := 1 to 10000 do write(a^[i]:8:0); FreeMem(A,sizeof(a^)); end.

§1.6.3 构造大于 64K 的数组 将整个大数组当做若干个小于 64K 的同类型,较低维数组的组合,在堆中建立这 些子数组.然后,将这些子数组的指针以一个指针数组的形式组织起来.在形式 上,该指针数组的数组名就是要定义的大数组的数组名,通过此名来统一引用该 数组的各元素, 从而达到能按通常的编程习惯在表达式中通过下标直接引用大数 组的元素的目的. 下面的程序给出了如何具体应用上述方法的示例.它在堆中建立了一个 8x100 x100 的三维实数数组 A,约占用 480K 内存.该数组被看成由 8 个 100x100 的二 维子数组组成,因为每个子数组的体积为 60000 字节,故可用一动态数组表示. 指向这 8 个子数组的 8 个指针组成了指针数组 A. 这样可通过 A[i]^[j,k]引用上 述三维数组的元素,不需要作任何下标变换,可直接参加表达式的运算,与静态 数组的用法非常接近.从而保证了原来的程序设计风格,给程序的设计,编写, 阅读,调试和修改都带来了方便. program Huge_Array; const n = 100; m = 8; type Arr2 = array[1..n,1..n] of real; var A : array[1..m] of ^Arr2; i,j,k : integer; begin for i := 1 to m do GetMem(A[i],sizeof(a[i]^)); for i := 1 to m do for j := 1 to n do for k := 1 to n do a[i]^[j,k] := 100*i + 10*j + k; for i := 1 to m do begin for j := 1 to n do begin writeln('*****i=',i,',j=',j,'*****'); for k := 1 to n do write(a[i]^[j,k]:8:0); writeln; end; writeln; end; for i := m downto 1 do FreeMem(A[i],sizeof(a[i]^)); end. §1.6.4 动态可调数组的实现

当程序中要多次进行某种数组运算,如矩阵的转置,矩阵相乘或求解线性方程组 时,程序员总希望把某种数组运算编写成一个通用过程,让过程中的数组是可调 数组,即数组的维数和元素类型固定,每维的上下界可变. 动态可调数组的实现方法如下: 1.类型说明 按照所需的数组类型建立一个数组类型,为了达到数组规模可调,说明时不必给 定数组各维的界限.如: Type RealArray = array[1..1] of real; 2.变量说明 动态可调数组变量的说明和动态数组的说明一样,采用指针的形式.如: var ra : ^RealArray; 3.动态可调数组的建立 当需要使用数组时,首先计算所有数组元素所占用的空间 AraaySize 的值,然后 用 New 或 GetMem 分配 ArraySize 个字节的堆空间, FillChar 函数将此空间填 用 入 0,即完成数组的建立. 4.数组的引用 与动态数组的引用方法一样. 5.数组的撤消 为提高堆空间的利用率,数组用完后应及时将其撤消,其方法是利用 Dispose 或 FreeMem 函数释放分配给动态数组的堆空间. 下面的程序演示了上述方法,首先说明一个两维数组类型,数组的界限不定;然 后说明一个具有此数组类型的指针变量;在程序开始,要求用户输入这个两维数 组的各维的大小,接着计算数组的大小,申请堆空间,而后通过指针变量实用数 组,最后撤消数组. program Changable_Array; Type ArrayInt2 = array[1..1,1..1] of integer; var P : ^arrayInt2; ArraySize : word; I,j,n,m : integer; begin write('n = ');readln(n); write('m = ');readln(m); ArraySize := n * m * Sizeof(integer); GetMem(p,ArraySize); FillChar(p^,Arraysize,'0'); For i := 1 to n do for j := 1 to m do begin randomize; p^[i,j] := Random(j); write(i:3,' ',p^[i,j]:5); end;

FreeMem(p,ArraySize); end. §1.7 扩展内存(EMS)及其使用 为了突破 DOS 管理 640K 自由内存空间的限制, 1985 年 Lotus/Intel 和 Microsoft 公司联合开发了一种扩展内存规范,简称 LIM-EMS.它的主导思想是避开 CPU 模 式切换的难题,采用了当时硬件上已相当成熟的"存储体切换"技术:将扩展内 存卡插入机器的扩展槽,卡上的内存处于 CPU 的正常寻址空间之外;通过建立特 定的映射关系由硬件将其以页面为单位重定位到 CPU 寻址空间中的某处,DOS 程 序中可以自由地改变这种映射关系,从而对整个扩展内存进行存取.这种用存储 体开关技术扩展了的那部分内存空间称为扩展内存(Expanded Memory).对扩展 内存的管理由扩展内存管理程序 EMM(Expanded Memory Manager)提供,具体是 由 INT 67H 提供的. 3.2 支持最大为 8MB 的 EMS 内存, 4.0 则可支持 16MB. EMM EMM EMS 技术在基于 8088/80286 的机器上得到了广泛的使用,绝大多数优秀的商业 软件,如电子表格,CAD 等等,都支持 EMS 规范.在用 TURBO PASCAL 编写的应 用程序中如何使用 EMS 内存呢?可以设计一种通用的 TURBO PASCAL 使用 EMS 的 程序单元(见§2.6),应用程序使用此程序单元,即可使用扩展内存.下面阐述 在程序中如何使用 EMS. §1.7.1 扩展内存的工作原理 使用扩展内存需要一块内存扩展卡.该卡由扩展内存管理器(EMM)软件来存取. 计算机启动时,加载扩展内存管理软件,这需要在 CONFIG.SYS 文件中指定. 扩展内存管理软件把扩展内存卡上的存储器划分为 16K 的页.这样,2M 的扩展 内存相当于 128 个扩展内存页,但这些页不能同时使用.每次能够使用的扩展内 存页数由页框的大小决定. §1.7.2 扩展内存页框 PC 机中 8088/86 微处理器能寻址 1M 字节,通常称为常规内存.然而在启动计算 机时,DOS 保留了很多内存(384K)自用,只为用户留下 640K.扩展内存管理器在 DOS 保留的 384K 中划分出 64K 作为扩展内存页使用. 页框就好象是扩展内存的窗口.该窗口有 4 个 16K 的"窗口片",每个片对应一 页扩展内存.在使用一页扩展内存之前,先要把该页影射到或称移动到页框中. 例如,如果想在扩展内存的 0 页中存储一些信息,就要把 0 页影射到页框中,然 后把数据移动到页框中. 当程序要使用更多的扩展内存时,就需要将一些新的页移动到页框中.当将一页 影射到页框中时, 页框中原来的页就要被影射出去, 也就是要保存在扩展内存中. 当以后再把它移回页框时,其中的信息同移出去以前是一样的. §1.7.3 逻辑页和物理页 同扩展内存相关的两个经常出现的术语是物理页和逻辑页. 物理页是组成页框的 4 个页,其编号是 0 到 3.而逻辑页是影射到页框中的扩展内存页.页框中的页

是"物理"的,是因为可以直接往页框中存储数据,但不能直接向扩展内存的页 传递数据. §1.7.4 扩展内存句柄 在使用一个扩展内存页之前,要调用扩展内存管理器进行分配.可以申请最少 1 页,最多全部可用页.当扩展内存管理器满足申请分配页时,返回一个扩展内存 句柄,即一个与所分配的页相关联的整数. 每个句柄有自己的一套扩展内存页.例如,句柄可以分配 3 页,编号为 0 到 2. 同时,另一个句柄可能有 5 页,编号为 0 到 4. §1.7.5 扩展内存功能 使用扩展内存功能与使用 DOS 和 BIOS 中断服务一样.扩展内存管理软件在装入 内存时,占用中断 67H.所有扩展内存服务均通过该中断完成. 扩展内存管理器几经修改,其最新版本是 4.0 版,它提供了 30 个扩展内存服务, 其中只有 15 个在旧的版本中能工作.而在这 15 个中,又只有少数几个为大多数 扩展内存程序所必需.表 1-3 列出了最常用的扩展内存服务. 表 1-3.最常用的扩展内存服务 殔 ┌———┬——————┬———————————————————┐ │ 功能│描述 │ 说 明 │ ├———┼——————┼———————————————————┤ │ 40H │取 EMM 状态 │ 确定是否加载了扩展内存管理程序和 │ │ │ │ 正常工作.结果返回在 AH 寄存器中, │ │ │ │ 0 表示安装了 EMM 并且没有检测到硬件 │ │ │ │ 错误 │ │ 41H │取页框地址 │ 取页框段地址在 BX 中.如果 AH 不等于 │ │ │ │ 0,则 BX 中的值无效 │ │ 42H │取未分配页数│ 得到计算机中扩展内存总页数(在 DX │ │ │ │ 中)和程序可用页数(在 BX 中) │ │ 43H │分配内存 │ 通知 EMM 程序分配扩展内存,供用户程 │ │ │ │ 序使用.BX 中放需要的页数.EMM 中 │ │ │ │ 断返回时将句柄放在 DX 中. │ │ 44H │影射内存 │ 将一个扩展内存页(BX 指定)影射到一 │ │ │ │ 个页框中的页(由 AL 指定).EMM 页句 │ │ │ │ 柄由 DX 指定 │ │ 45H │释放内存 │ 释放一个 EMM 句柄(DX 指定)的所有页. │ │ │ │ 一旦释放,这些页就可以再分配给一 │ │ │ │ 个新的句柄. │ │ 46H │取 EMM 版本 │ 返回当前所用 EMM 版本.返回时,AL 中 │ │ │ │ 高 4 位是主版本号,低 4 位是版本的小 │ │ │ │ 数部分. │ └———┴——————┴———————————————————┘

殣 §1.7.6 判断扩展内存是否安装 扩展内存服务 40H,报告是否加载了扩展内存管理器以及硬件功能是否正常.用 户可能会用这一功能确定运行程序的计算机是否安装了扩展内存, 然而这是错误 的,因为只有在安装了扩展内存管理器后才能使用 40H 号服务.如果在没有扩展 内存的情况下使用 40H 号服务程序,计算机可能会死锁. 那么 40H 号服务是干什么的?而且怎样知道计算机中是否安装了扩展内存?第一 个问题的答案很简单,要时检测扩展内存是否在正常工作.40H 服务能提供这种 周期性的状态报告. DOS 的 35H 号功能用来取某一中断程序的入口地址,调用它可以确定是否安装了 扩展内存.该服务程序返回指定中断的中断服务程序的段地址(ES)和偏移地址 (BX).因为 EMM 使用中断 67H,因此,如果安装了 EMM 的话,这一 DOS 调用会返 回 EMM 的入口地址. 如果在系统启动时装入了扩展内存管理程序(EMM),在内存中的一个固定地址就 会存放一个字符串——"EMMXXXXX0".该地址为 EMM 所在段,偏移量为 0AH 处. DOS 服务 35H 返回 EMM 段地址在 ES 中,偏移量在 BX 中.看一下在内存 ES:000AH 处的内容, 就可以判断是否安装了 EMM. 如果 ES:000AH 处是字符串 "EMMXXXXX0" , 那么就安装了 EMM,否则就没有安装这一管理软件.检测 EMM 是否存在的程序段 如下: ; 检测 EMM 是否存在 ; mov ax,3567h int 21h mov di,10 push cs pop ds mov si,offset DevName mov cx,8 rep cmpsb ; DevName DB 'EMMXXXX0' ; §1.8 扩充内存(XMS)及其使用 EMS 技术在基于 8088/80286 的机器上得到了广泛的使用, 但是几乎没有一台 386 机上会装扩展内存卡,因为 386 芯片与 286 不同,它的内存管理功能更为强大, 模式切换也非常方便,它的页式管理功能可以很容易把内存映射到任何地址;另 外 386 数据总线为 32 位,32 位的内存卡的存取速度要比 16 位的 EMS 卡快,而 且价格便宜; 因此在 386 机上由软件利用扩充内存(XMS)仿真扩展内存(EMS)就十 分划得来. 扩充内存(Extended Memory)是指物理地址位于 1MB(100000H)以上的那部分内 存,它只适用于配备 80286 以上档次的 CPU 的机器上.如果应用程序使用扩充内

存,不仅运行速度快,而且效率高.通常,在 80386 和 80486 系统中,MS-DOS 还提供了 EMM386.EXE 程序,使扩充内存仿真扩展内存. 在 MS-DOS 4.0 及 WINDOWS 3.0 中,提供了一个名为 HIMEM.SYS 的扩充内存管理 程序 XMM(Extended Memory Manager),它是按照 Lotus/Intel/Microsoft/AST 的扩充内存管理规范 2.0 版本编制的, 使得应用程序对扩充内存的使用变得非常 方便.HIMEM.SYS 是一个设备驱动程序,可以在系统配置文件(CONFIG.SYS)中用 DEVICE 命令加以说明,在机器启动时便可装入. XMS 的使用通过 INT 2FH 的 43H 子功能提供管理.下面具体介绍 XMS 管理功能和 使用方法. §1.8.1 扩充内存管理规范(XMS)简介 扩充内存管理规范(XMS)是 Lotus/Intel/Microsoft/AST 公司的合作成果.它为 286/386 微机定义了一个软件接口,可以允许实模式程序以一种与硬件无关的方 式使用扩充内存.如果不同厂商开发的程序按照这种协议来申请扩充内存,那么 它们之间就能和平共处,不会发生冲突. XMS 规范定义了 3 种内存块的申请,修改和释放功能: . 上位内存块(UMB):地址位于 640K 和 1024K 之间 . 高内存区 (HMA):地址位于 1024K 和 1088K 之间 . 扩充内存块(EMB):地址位于 1088K 以上 这 3 部分的关系如图 1-3 所示. 殌 ————┬————————┐16MB/4GB ↑ │EMB(扩充内存块) │ 扩充内存├————————┤1MB+64KB ↓ │HMA(高内存区) │ ————┼————————┤1MB ↑ │ROM BIOS │ │ ├————————┤ │ │UMB(上位内存块) │ │ ├————————┤ │ │EMS 页框地址 │ ├————————┤ 传统内存│外设口地址 │ ├————————┤ │ │视频刷新缓冲区 │ │ ├————————┤640KB │ │ 常规内存 │ │ │----------------│ ↓ │ MS DOS 内核 │ ————┴————————┘0KB 殣 图 1-3 286/386/486 内存映象图

所以扩充内存是指 80X86 机器 1MB 寻址空间之外的内存.在扩充内存规范中,扩 充内存也指高内存区(HMA)和上位内存块(UMB). UMB 是指在 DOS 内存 640KB 和 1MB 之间的内存.在 DOS5.0 以前,程序员只有通 过 XMS 驱动程序才能使用这一区域,从 DOS 5.0 开始,可以通过 DOS 内存服务来 访问 UMB.实际上 DOS 内存服务例程代为访问了 XMS 驱动程序. HMA 的存在比较特殊.当 CPU 运行在实模式并且第 21 条地址线(A20)处于激活状 态时,CPU 就可以访问一块 65520B 的内存(64K 少 16B),这块内存就叫 HMA.HMA 的存在与 CPU 的寻址方式有关.CPU 根据段地址:偏移地址来寻址,首先将段地 址乘以 16,再加上偏移地址,形成物理地址.如果此值超过 20 位,则截去其高 位, 使物理地址在 000000H-0FFFFFH 之间. 如果 A20 线不激活, 地址 0FFFF:0010H 就是物理地址 000000H;若 A20 线激活,0FFFF:0010H 就是物理地址 010000:0000H,这样就有了额外的 65520B 的内存.也就是说地址 0FFFF:0010H-0FFFF:0FFFFH 通常映象到物理地址 000000H-00FFFFH, A20 激活 当 后,映象到的物理地址就为 010000H-010FFEFH. XMS 驱动程序提供了五组功能:驱动程序信息,HMA 管理,A20 地址线管理,扩 充内存管理和上位内存块管理. 另外的两个功能是检查 XMS 驱动程序是否存在和 XMS 驱动程序控制功能的地址.表 1-4 给出了 XMS 功能调用. §1.8.2 XMS 的使用 使用扩充内存,需要判定扩充内存是否可用.首先执行如下代码,判定 XMS 程序 是否存在. MOV AX,4300H INT 2FH CMP AL,80H JNE XMS_NOTPRESENT ; XMS IS PRESENT 如果存在, 再取 XMS 驱动程序控制功能的地址, 用如下的代码段即可完成此功能. ; XMS_CONTROL DD (?) ; MOV AX,4310H INT 2FH MOV WORD PTR [XMS_CONTROL],BX MOV WORD PTR [XMS_CONTROL],ES ; 之后,就可以用远调用的方式来使用 XMM 提供的功能了.如执行取 EMM 版本号功 能的程序如下: ; mov ah,0 call XMS_control ; 表 1-4.XMS 的功能调用 殔┌——————┬———┬——————————┐

│ 功 能 │功能号│ 描 述 │ ├——————┼———┼——————————┤ │驱动程序信息│ 0 │ 取 XMS 版本号 │ ├——————┼———┼——————————┤ │管理高内存区│ 1 │ 请求高内存区 HMA │ │ (HMA) │ 2 │ 释放高内存区 HMA │ ├——————┼———┼——————————┤ │ 操纵 │ 3 │ 全程启用 A20 │ │ │ 4 │ 全程停用 A20 │ │ A20 │ 5 │ 局部启用 A20 │ │ │ 6 │ 局部停用 A20 │ │ 地址线 │ 7 │ 查询 A20 的状态 │ ├——————┼———┼——————————┤ │ │ 8 │ 查询自由扩充内存 │ │ 管理 │ 9 │ 分配扩充内存块 │ │ │ AH │ 释放扩充内存块 │ │ │ BH │ 移动扩充内存块 │ │ 扩充内存块 │ CH │ 锁住扩充内存块 │ │ │ DH │ 解锁扩充内存块 │ │ │ EH │ 取 EMB 句柄信息 │ │ (EMBs) │ FH │ 重新分配扩充内存块 │ ├——————┼———┼——————————┤ │ 管理 │ 10H │ 请求上位内存块 UMB │ │ 上位内存块 │ 11H │ 释放上位内存块 UMB │ └——————┴———┴——————————┘ 殣 §1.8.3 扩充内存管理功能 1.取版本号 入口参数:AH=00H 出口参数:AX=二进制版本号;BX=内部 XMM 版本;DX=1,存在 HMA 2.请求高存区(HMA) 入口参数:AH=01H;DX=请求长度 出口参数:AX=1,HMA 分配成功;否则 BL 返回错误码,错误码见表 1-5 3.释放高存区(HMA) 入口参数:AH=02H 出口参数:AX=1,HMA 释放成功;否则 BL 返回错误码 4.全程打开 A20 入口参数:AH=03H 出口参数:AX=1,A20 已激活;否则 BL 返回错误码 5.全程关闭 A20 入口参数:AH=04H 出口参数:AX=1,A20 已关闭;否则 BL 返回错误码 6.局部打开 A20

入口参数:AH=05H 出口参数:AX=1,A20 已激活;否则 BL 返回错误码 7.局部关闭 A20 入口参数:AH=06H 出口参数:AX=1,A20 已关闭;否则 BL 返回错误码 8.查询 A20 状态 入口参数:AH=07H 出口参数:AX=1,A20 已激活 9.查询自由扩充内存大小 入口参数:AH=08H 出口参数:AX=最大扩充内存块的长度(KB),DX=自由扩充内存总数(KB),BL=错 误码 这里查到的扩充内存总数是系统中实际安装的扩充内存数减去 HMA 的内存数. 10.分配扩充内存块 入口参数:AH=09H 出口参数:AX=1,分配成功;DX=扩充内存块句柄;BL=错误码 扩充内存的管理是通过扩充内存控制块来实现,扩充内存控制块的数据结构如 下: DB 标志(01:自由块;02:分配块;04 空闲块) DW 内存块始址 (KB) DB 加锁标志(0 : 加锁;非 0 : 解锁) DW 内存块长度 (KB) 扩充内存控制块的地址称为扩充内存块句柄.从数据结构中可以看出,扩充内存 最基本的管理单位是 1KB,即最大可存取的物理地址为 128MB.扩充内存控制块 的数量即句柄的数量可在系统配置文件中说明,默认值为 21,最大值为 128,即 最多可使用的内存块是 128 个. 11.释放扩充内存块 入口参数:AH=0AH,DX=扩充内存块句柄 出口参数:AX=1,扩充内存块已释放;否则 BL=错误码 12.移动扩充内存块 入口参数:AH=0BH,DS:SI=参数表地址 出口参数:AX=1,移动成功;BL=错误码 本功能可以在常规内存和扩充内存之间双向传送数据.DS:SI 所指参数表的格 式: DD 传送长度(必须是偶数) DW 目标块句柄 DW 源块句柄 DD 目标块内偏移 DD 源块内偏移 其中当句柄为 0 时,则相应的偏移量以 SEGMENT:OFFSET 的形式表示,数据由 BL 返回错误码. 13.扩充内存块加锁 入口参数:AH=0CH;DX=句柄 出口参数:AX=1,加锁成功;DX:BX=32 位加锁的内存地址;否则 BL=错误码 14.扩充内存块开锁 入口参数:AH=0DH;DX=句柄 出口参数:AX=1,开锁成功;否则 BL=错误码 15.取扩充内存控制块句柄信息

入口参数:AH=0EH;DX=句柄 出口参数:AX=1,信息块已获得;BH=加锁信息;BL=自由句柄数;DX=内存块长 度(KB);否则 BL=错误码 16.重新分配扩充内存 入口参数:AH=0FH;DX=句柄;BX=新的长度(KB) 出口参数:AX=1,重分配成功;否则 BL=错误码 表 1-5.XMM 错误码一览表 殔┏━━━┯━━━━━━━━━━━━┳━━━┯━━━━━━━━━━━ ━━┓ ┃错误码│ 含义 ┃错误码│ 含义 ┃ ┠———┼————————————╂———┼———————————— —┨ ┃ 80H │ 功能未实现 ┃ 91H │ HMA 已使用 ┃ ┃ 81H │ 已安装虚拟盘 ┃ 92H │ 请求长度小于最小请求长度 ┃ ┃ 82H │ A20 地址线处理错 ┃ 93H │ HMA 未使用 ┃ ┃ 8EH │ 一般驱动程序错 ┃ A0H │ 无自由扩充内存 ┃ ┃ 90H │ 不存在 HMA ┃ A1H │ 无扩充内存句柄可用 ┃ ┃ A2H │ 扩充内存控制块句柄无效 ┃ A8H │ 传送时有无效的地址重叠 ┃ ┃ A3H │ 源句柄无效 ┃ A9H │ 奇偶校验错 ┃ ┃ A4H │ 源偏移量无效 ┃ AAH │ 内存块已解锁 ┃ ┃ A5H │ 目标句柄无效 ┃ ABH │ 内存块已加锁 ┃ ┃ A6H │ 目标偏移量无效 ┃ ACH │ 加锁内存块数已溢出 ┃ ┃ A7H │ 传递长度无效 ┃ ADH │ 不能加锁内存块 ┃ ┗━━━┷━━━━━━━━━━━━┻━━━┷━━━━━━━━━━━━ ━┛ 殣 §1.9 程序的标准数据作代码处理的方法 很多程序在开始运行时,要把一些标准的数据文件读到内存中.这些数据文件包 含一些不变的信息,如字库和特殊的表.TURBO PASCAL 使用程序 BINOBJ 能使用 户直接把这些数据放在程序中,避免等到程序运行时再读取. 用 BINOBJ 把数据装入程序需要 3 步: . 创建数据文件; . 用 BINOBJ 将数据文件转换为.OBJ 文件; . 在程序中将数据文件作为外部过程引用. 把数据文件作为外部过程看待,成为程序的一部分,才能在程序启动时就自动装 入内存.这样做有如下优点:首先,由于不需要打开和读取文件,加快了程序运 行速度;其次,如果程序作为商品出卖,则可以减少磁盘文件数目;其三,增加 了程序的保密性. §1.9.1 创建数据文件 在用 BINOBJ 之前,要有一个准备好的二进制数据文件.下面的过程产生一个含 有 1 到 100 的自然数及其自然对数的二进制数据文件. 该文件的结构由数组类型

LogArray 定义. Program MakeBinaryDataFile; Type LogArrayPtr = ^LogArray; LogArray = Array[1..100] of Record I : Integer; LnI : Real; End; Var I : Integer; LogA : LogArrayPtr; F : File of LogArray; begin GetMem(LogA,sizeof(LogA^)); for i := 1 to 100 do begin LogA^[i].I := I; LogA^[i].LnI := ln(i); end; Assign(f,'LogData.bin'); Rewrite(f); Write(f,Loga^); close(f); end. 该程序产生的 LOGDATA.BIN 二进制数据文件可作为 BINOBJ 的输入文件,因文件 具有 LogArray 类型,所以在程序中存取这些作为外部过程的数据时,必须采用 相同的数据类型. §1.9.2 转换数据文件 用 BINOBJ 把二进制数据文件转换为目标文件(.OBJ). 使用 BINOBJ 的一般格式是: BINOBJ <源文件名.BIN> <目标文件名[.OBJ]> <公用数据名> 源文件是二进制数据文件,BINOBJ 不能自动加.BIN,所以使用时要写上数据文 件的扩展名.目标文件是 BINOBJ 产生的输出文件,如果不指定该文件的扩展名, BINOBJ 会把标准的扩展名(.OBJ)加上.最后,公用数据名是在访问这些数据时 所用的过程名. 用 BINOBJ 把上述程序产生的数据文件 LOGDATA.BIN 生成目标文件. BINOBJ LOGDATA.BIN LOGDATA LOGDAT §1.9.3 访问外部过程 将产生的数据文件转换为目标文件后, 就可以连接到程序中. 实现的一般形式是: Procedure <公用数据名>; erternal; {$L 目标文件名.OBJ}

过程名和运行 BINOBJ 的公用数据名相同.{$L}编译指令使用的名字与 BINOBJ 使用的目标文件名相同.所以,要使用上面生成的数据文件,必须在程序中作如 下的声明: Procedure LogDat; external; {$L LOGDATA.OBJ} 在运行 TURBO PASCAL 编译程序时,LOGDATA.OBJ 被连接到程序中,相应的数据 放在 LogDat 指明的地址上. 访问存储在代码中的这些数据很简单,首先,声明一个与创建的数据文件相同数 据类型的指针变量;其次,将指针变量指向存储这些数据的代码. 下面举例说明如何访问存储在 LOGDATA.OBJ 中的数据: Program TestBin; Type LogArrayPtr = ^LogArray; LogArray = Array[1..100] of Record I : Integer; LnI : Real; End; Var I : Integer; LogA : LogArrayPtr; procedure LogDat; external; {$L LOGDATA.OBJ} begin LogA := @LogDat; for i := 1 to 100 do begin Write(LogA^[i].I); writeln(LogA^[i].LnI:10:4); end; end. LogA 是与所创建的数据文件有相同的数据类型的指针变量.在运行时,LogA 通 过这个语句指向所连接的数据: LogA := @LogDat; 这个语句取出 LogDat 的地址,赋给 LogA.这样就可以访问 LogA 中的所有数据, 就象在堆中动态分配来的一样. 注意,LogA 没有申请任何内存,因为是在代码段中.不要试图释放 LogA 或其它 任何指向代码段的指针. 虽然利用 BINOBJ 把数据存放在代码是个有效的方法,但是它也具有一些弊端. 假设数据存放在程序的代码段,如果数据文件很大,则代码会超过 64K 限制,且 数据在程序启动后永远保存在内存中,无法象在堆上申请空间一样释放.另外, 如果修改数据文件,就必须重新运行 BINOBJ,并编译程序.


相关文章:
turbopascal高级教程.pdf
PASCAL 7.0,它提供了更方便、更广泛的编程环境,如 ...第一章 TURBO PASCAL 高级编程技术 TURBO PASCAL 是...本章就使用 TURBO PASCAL 开发高级软件的实用技术...
Turbo Pascal 基本知识1_图文.ppt
Turbo Pascal 基本知识 $1.1Turbo Pascal简介 Pascal是面向过程的高级程序语言。...turbopascal高级教程 14页 免费 Turbo Pascal图形编程教... 12页 3下载券 ...
turbo pascal教程.doc
Turbo Pascal 程序是由程序首部、程序说明部分和程序...pascal 编程时常见错误: 编程时常见错误: 1 无效 ...Turbo Pascal7 35页 1下载券 turbopascal高级教程...
Turbo Pascal程序设计_图文.ppt
第一章 Pascal语言概述与预备知识 0:一个最经典的Turbo Pascal程序 1:关于...Pascal高级编程技术:第... 42页 免费 Turbo Pascal基本函数 10页 2下载券...
pascal基础(1)很好.pdf
十三章 动态数据类型 第十四课 文件 第一课 初识 Pascal 语言 一、Pascal 语言概述 二、Pascal 语言的特点 三、Pascal 语言程序的基本结构 四、Turbo Pascal ...
Delphi高级编程技术_图文.doc
高级编程技术 综合课程设计任务书 技术》 《Delphi 高级编程技术》综合课程设计任务书 1 一、 课程设计目的 1) 2) 3) 4) 要求学生熟练掌握 Delphi 高级编程...
Turbo Pascal7.0讲解.doc
参与竞赛活动的第一步必须掌握一门高级语言及其程 ...2.1 Turbo Pascal 简介 PASCAL 语言也是一种算法...(数据结构和算法知识+编程技术)+良好的心理素质(...
高级编程技术课程学习心得与体会.pdf
本学期看到任选课中有高级编程技术,于 是没有犹豫的选了这门课。 我觉得可能由于之前学习过 pascal 语言以及大一上学期浅显的 学习过一部分 c 语言编程,大二的...
C必看高级编程技术_图文.ppt
C必看高级编程技术_信息与通信_工程科技_专业资料。C必看高级编程技术 ? 屏幕...? 第二章 图形程序设计 Turbo C为用户提供了一个功能很强的画图软 件库,它...
Turbo Pascal基本函数.pdf
OVERLAY 实现高级覆盖管理 中直接 功 SYSTEM 单元...填充一个区域 Turbo Pascal 图形编程教程 Pascal 是...第一章 使用 Pascal 进行图形操作前的准备 在 ...
TURBO PASCAL教程.pdf
(一)保留字 保留字是由字母拼成的字,Turbo Pascal 语言预先规定了他们的意义,...pascal 编程时常见错误: 1 无效 DoS 功能号 2 文件末找到 3 路径未找到 4 ...
pascal的基础知识.doc
pascal的基础知识_IT/计算机_专业资料。pascal 编程 语言 电脑 信息技术 Pascal 基本教程 第一章 第一章 Pascal 语言概述与预备知识 1 关于 Turbo Pascal Pascal ...
pascal教程-自学完整版_图文.ppt
? ? 第一章 第二章 第三章 第四章 第五章 ...当P 顺序 选择 A 当型 第二讲程序语言 编程语言...Turbo Pascal 7 Free Pascal 1.0/2.0 CP Pascal ...
高级编程技术模拟题.doc
高级编程技术模拟题_IT/计算机_专业资料。高级编程技术模拟题 《高级编程技术》 题库 1. XML Web 服务基于(C)协议在应用程序之间传输 XML 消息 A. IP B. ...
高级编程技术_图文.ppt
高级编程技术_计算机软件及应用_IT/计算机_专业资料。高级编程技术 第2章 高级编程的硬件基础主要内容计算机硬件组成 80x86微机系统 Turbo C编译方式 BIOS和DOS调用 ...
高级编程技术实验报告1.doc
高级编程技术实验报告1_IT/计算机_专业资料。高级编程技术实验报告实验一 简单
windows高级编程技术习题1.doc
windows高级编程技术习题1_工学_高等教育_教育专区。windows高级编程技术 (1) 项目工作区一般在集成开发环境的左侧。它展示一个工程的几个方面,它们分别是 资源视图...
bc高级编程技术.pdf
高级编程技术介绍如何利用 TC 系统所提供的相关函数实现菜单设计、图形绘制、动画...TURBO PASCAL, TURBO PROLOG 图形子系统的基础,它是一种快速、紧凑型和独立于...
C语言高级编程技术.pdf
第14 章 14 C 语言高级编程技术 使用过 Windows 系统的用户都感受到了图形...简单的应用中可采用两种办法: 一是直接使用 Turbo C 提供的键盘操作函数 ...
小学PASCAL全教程.doc
PASCAL 全教程 Pascal 基本教程 第一章 第一章 Pascal 语言概述与预备知识 1 关于 Turbo Pascal Pascal 是一种计算机通用的高级程序设计语言。它由瑞士 Niklaus ...
更多相关标签: