第8章 51DOS应用程序设计

8.1 helloworld例子程序

在Keil开发环境下创建helloworld工程。

8.1.1 Keil开发环境设置

根据上一章中的内存分配方案设置Keil开发环境。“Memory Model”和“Code Rom Size”都选择Large模式。把“BL51 Locate”选项中的“Code Range”设置为0x8000-0xFFFF“Xdata Range”设置为0x0000-0x3FFF“C51”选项中的“Interrupt vectors at address”设置为0x8000。这样设置后,生成的代码起始地址是0x8000,中断入口也从0x8000开始。设置输出的程序是Hex格式。

一般地,我们都认为C程序是从main函数开始执行的,实际上,在main函数之前,Keil开发环境会自动添加一段代码,这段代码中将主要完成栈指针SP的设置和全局变量的初始化工作。不同程序的变量占用片内RAM的大小是不确定的,所以栈指针要根据程序实际情况设置。程序存储在程序存储空间,而全局变量存储在数据存储空间,那怎么实现main函数执行时,全局变量已经有值了呢?只能在main函数之前写段汇编代码实现全局变量的初始化。我们可以把系统里自带的STARTUP.A51(安装目录Keil\C51\LIB下)和INIT.A51(安装目录Keil\C51\LIB下)汇编文件添加到自己的项目中,通过修改这两个文件可以改变main函数执行之前要做的事情。

设计51DOS时,执行LCALL指令后,返回地址自动压入栈,SP栈指针则指向栈顶。如前面所说,main函数之前会重设SP指针的指向和初始化片内部分RAM,如果不保护返回地址,那么return指令就无法返回操作系统了。所以,我们先把返回地址存储到DPTR寄存器,重设SP指针后,再把DPTR内容压入栈,然后再继续后面的全局变量初始化和main函数,这样处理后,main函数最后的return指令就能返回操作系统了。对STARTUP.A51的修改见下面代码中的斜体部分,INIT.A51文件则不用修改。

IDATALEN        EQU     80H;片内RAM初始化为0的长度

XDATASTART      EQU     0

XDATALEN        EQU     0;外扩RAM初始化为0的长度,可以不初始化

PDATASTART      EQU     0H

PDATALEN        EQU     0H

IBPSTACK        EQU     0       

IBPSTACKTOP     EQU     0xFF +1;没选择这种方式

XBPSTACK        EQU     1       

XBPSTACKTOP     EQU     0x3FFF +1;选择的XDATA方式,所以要设置

PBPSTACK        EQU     0       

PBPSTACKTOP     EQU     0xFF +1;没选择这种方式

PPAGEENABLE     EQU     0       

PPAGE           EQU     0

PPAGE_SFR       DATA    0A0H

ACC     DATA    0E0H

B       DATA    0F0H

SP      DATA    81H

DPL     DATA    82H

DPH     DATA    83H

 

                NAME    ?C_STARTUP

?C_C51STARTUP   SEGMENT   CODE

?STACK          SEGMENT   IDATA

                RSEG    ?STACK

                DS      1

                EXTRN CODE (?C_START)

                PUBLIC  ?C_STARTUP

                CSEG    AT      0x8000;代码的起始地址

?C_STARTUP:     LJMP    STARTUP1;从这里开始执行

                RSEG    ?C_C51STARTUP

STARTUP1:

                POP DPH;先把返回地址保存到DPTR寄存器

POP DPL

IF IDATALEN <> 0;片内RAM清零

                MOV     R0,#IDATALEN - 1

                CLR     A

IDATALOOP:      MOV     @R0,A

                DJNZ    R0,IDATALOOP

ENDIF

IF XDATALEN <> 0;XDATA无需清零

                MOV     DPTR,#XDATASTART

                MOV     R7,#LOW (XDATALEN)

  IF (LOW (XDATALEN)) <> 0

                MOV     R6,#(HIGH (XDATALEN)) +1

  ELSE

                MOV     R6,#HIGH (XDATALEN)

  ENDIF

                CLR     A

XDATALOOP:      MOVX    @DPTR,A

                INC     DPTR

                DJNZ    R7,XDATALOOP

                DJNZ    R6,XDATALOOP

ENDIF

IF PPAGEENABLE <> 0;没用到

                MOV     PPAGE_SFR,#PPAGE

ENDIF

IF PDATALEN <> 0;没用到

                MOV     R0,#LOW (PDATASTART)

                MOV     R7,#LOW (PDATALEN)

                CLR     A

PDATALOOP:      MOVX    @R0,A

                INC     R0

                DJNZ    R7,PDATALOOP

ENDIF

IF IBPSTACK <> 0;没用到

EXTRN DATA (?C_IBP)

                MOV     ?C_IBP,#LOW IBPSTACKTOP

ENDIF

IF XBPSTACK <> 0;选择这种方式,C语言局部变量栈设置

EXTRN DATA (?C_XBP)

                MOV     ?C_XBP,#HIGH XBPSTACKTOP

                MOV     ?C_XBP+1,#LOW XBPSTACKTOP

ENDIF

IF PBPSTACK <> 0;没用到 

EXTRN DATA (?C_PBP)

                MOV     ?C_PBP,#LOW PBPSTACKTOP

ENDIF

;重设栈指针SP的指向,跟C程序中片内RAM分配有关

                MOV     SP,#?STACK-1

;设置完SP后,把返回地址从DPTR压入SP指向的栈

;这样,当main函数最后return时,会自动把这个返回

;地址写入PC寄存器,也就可以返回到操作系统了

PUSH DPL

                PUSH DPH

;跳转到INIT.A51中去初始化全局变量,然后转到main

LJMP    ?C_START

END

8.1.2  HelloWorld源代码

环境设置好了,下一步就是写自己的C程序,写C程序的方法和第5章所讲的内容是一致的,不用考虑操作系统。添加main.c文件, helloworld的源代码如下:

#include <reg51.h>

#include <stdio.h>      

// 向串口输出一个字符

void PutChar(char c)

{

SBUF = c;

while (TI == 0);

TI = 0;

}

void Init_USART()

{

SCON = 0x50;

TMOD = 0x20;

PCON = 0x00;

TH1 = TL1 = 0xFD;

TI = RI = 0;

TR1 = 1;  

}  

// 主程序

void main()

{   

  Init_USART();

  printf("hello world!\r\n");  

}

编译源代码,可以生成helloworld.hex程序。

8.1.3 SD卡镜像文件读写

前面设计的8051计算机和51DOS系统能执行SD上的程序,所以需要把helloworld.hex写入SD卡镜像文件。我们提供两个windows下的小工具,分别是hex2bin.exe和sd_img.exe。因为8051计算机只能执行二进制程序,hex2bin.exe工具可以把hex格式文件转换成二进制文件。sd_img.exe工具可以用来创建镜像文件、从镜像文件里读出文件、把文件写入镜像等功能,这个小工具也是使用上一章设计的文件系统读写镜像的,源代码文件sd_img.c在本章“PC上的工具”目录下,读者可自行阅读。

sd_img.exe用法如下:

Ø sd_img.exe –h,列出支持的功能;

Ø sd_img.exe img_file –w local_file,把本地的文件local_file写入镜像,如果没有镜像,则自动创建;

Ø sd_img.exe img_file –r sd_file,把SD卡镜像中的文件读出到本地,文件名不变;

Ø sd_img.exe img_file –d sd_file,把sd卡镜像中的文件删除;

Ø sd_img.exe img_file –l,列出sd卡镜像中的文件;

Ø sd_img.exe img_file –u,统计sd卡空间占用情况。

首先,把hex2bin.exe和sd_img.exe复制到windows命令行目录下。然后,把helloworld.hex也复制到windows命令行目录下,命令行输入“hex2bin.exe hellowolrd.hex”生成hellowolrd.bin二进制文件,再输入“sd_img.exe test.mmc –w hellowolrd.bin”,可以创建test.mmc镜像,并把helloworld.bin文件写入镜像。操作示例如图8-1所示。

 

8-1 hex2bin.exe和sd_img.exe工具使用示意图

最后,打开第6章提供的“8051计算机”项目,设置Proteus仿真中SD卡的镜像文件为test.mmc,执行仿真,输入helloworld.bin就能看到执行结果了,运行结果如图8-2所示。

 

8-2 hellowolrd执行结果

如果把“8051计算机”做成实物,可以使用linux下dd等工具把镜像文件按字节写入真实的SD卡,也可以从真实SD卡读出镜像。

简单梳理一下,我们设计的计算机系统分成3个部分,分别是8051计算机硬件、51DOS操作系统和应用程序,应用程序以文件的方式存储在SD卡(好比硬盘)里,通过51DOS提供的命令行界面执行应用程序。应用程序的开发与没有操作系统时一样,只是需要设置一下开发环境,生成的hex程序转化成二进制文件并写入SD卡,把SD卡插入8051计算机,通过命令行界面运行各种应用程序。

后面再写其它应用程序时,可以使用helloworld例子的环境设置,读者只需要修改自己的C代码就行了。

8.2 文件操作类程序

下面我们设计并实现一组文件操作类命令程序,有了这些命令,用户就可以使用命令创建、删除、复制、查看、列出文件,就像DOS或Linux命令一样。

上一章中我们实现一组文件系统的操作函数接口,并封装成了文件库FILE.LIB,读者只需要把file.h头文件加到自己C代码里,把FILE.LIB加到自己的项目里就可以使用文件操作函数了。

8.2.1 touch命令

“touch 文件名”用来创建一个空文件。文件操作函数里的fcreate()函数可以用来创建空文件,用户可以使用helloworld例子的环境和代码,加入FILE.LIB库和file.h头文件,创建空文件的代码如下:

void main()

{

  int ret;

  Init_USART();   

  //创建空文件,操作系统传过来的第一个参数存储在外扩RAM的0x7F80

  ret = fcreate(&XBYTE[0x7F80]);  

if(ret == -1)

    printf("create file %s error!\r\n", &XBYTE[0x7F80]);

  else printf("create file %s ok!\r\n", &XBYTE[0x7F80]);  

}

touch命令程序创建空文件效果如图8-3所示。

 

8-3 touch命令执行示例

8.2.2 ls命令

ls命令用来列出SD卡上的文件信息,显示文件名和文件大小。

void main()

{

  int i, ret;

  char name[32];

  Init_USART();  

  for(i=0; i<50; i++)

  {

    memset(name, 0, 32);

    //循环读出每一个位置上的文件信息,

    ret = read_fileinfo(i, name);

if(ret != -1)

  printf("%s  %d\r\n", name, ret);   

  }   

}

ls命令的执行结果如图8-4所示。

 

8-4 ls命令执行示例

8.2.3 df命令

df命令用来统计SD卡存储空间使用情况,注意区分文件大小和文件占用存储空间的大小。

void main()

{

  int i, ret;

  long fsize, usesize;

  char name[32];

 

  Init_USART();

  fsize = 0;

  usesize = 0;  

  for(i=0; i<50; i++)

  {    

    ret = read_fileinfo(i, name);

if(ret != -1)

{

  fsize = fsize + ret; //文件总大小

     //文件占用的存储空间大小是512的倍数

  usesize = usesize + (ret / 512 + 1) * 512;

}   

  }

  printf("1048576 bytes total, %ld bytes used\r\nfiles size: %ld bytes\r\n", usesize, fsize);

}

df命令的执行结果如图8-5所示。

 

8-5 df命令执行示例

8.2.4 rm命令

rm命令用来删除文件,使用fremove函数就能实现,代码如下:

void main()

{

  int ret;

 

  Init_USART();   

  //要删除的文件名存储在外扩RAM的0x7F80处,也就是第一个参数

  ret = fremove(&XBYTE[0x7F80]);

  if(ret == -1)

    printf("%s not found!\r\n", &XBYTE[0x7F80]);

  else printf("rm %s ok!\r\n", &XBYTE[0x7F80]);   

}

rm命令执行情况如图8-6所示,执行rm后删除了test.txt文件。

 

8-6 rm命令执行示例

8.2.5 cp命令

cp命令用来复制文件。如果源文件是空文件,则直接创建目标空文件;如果源文件是非空文件,则循环读取源文件内容并写入目标文件。代码如下:

void main()

{

  int filesize, i, ret;

  char buffer[512];

 

  Init_USART();   

  //操作系统传过来的第一个参数是源文件

  filesize = fsize(&XBYTE[0x7F80]);

  if(filesize == -1)//源文件不存在

    printf("can not find %s\r\n", &XBYTE[0x7F80]);

  else if(filesize == 0)//源文件是空文件,目标文件也创建为空文件

  {

   ret = fcreate(&XBYTE[0x7F90]);//第二个参数是目标文件名

if(ret == -1)

  printf("create %s failed\r\n", &XBYTE[0x7F90]);    

  }

  else//源文件是非空文件

  {

    i = 0;

 ret = fcreate(&XBYTE[0x7F90]);//先创建目标空文件

 if(ret == -1)

 {

   printf("create %s failed\r\n", &XBYTE[0x7F90]);

   return;

 }

while(1)

{ //读取源文件内容并写入目标文件

     ret = fread(&XBYTE[0x7F80], buffer, i*512, 512);          

  if(ret < 512)//已经读取完毕,完成最后内容写入

  {

    fwrite(&XBYTE[0x7F90], buffer, ret);

 printf("cp ok\r\n");

    break;

  }

  else fwrite(&XBYTE[0x7F90], buffer, 512);//读取512字节并写入

  printf(".");//文件比较大时,显示进度条

  i++;

}  

  }   

}

执行“cp ls list”命令,把ls程序复制一份为list程序,执行list,可以看到list文件和ls文件大小一样,并且list的功能也和ls一样,cp命令执行情况如图8-7所示。

 

8-7 cp命令执行示例

8.2.6 cat命令

    cat命令用来查看文本文件的内容,循环读取,每次读取512字节并显示,直到文件结束,代码如下:

void main()

{

  int filesize, i, ret;

  char buffer[513];

 

  Init_USART();   

//操作系统传过来的第一个参数是要显示的文件名

  filesize = fsize(&XBYTE[0x7F80]);  

if(filesize == -1)

    printf("can not find %s\r\n", &XBYTE[0x7F80]);

  else if(filesize == 0)//空文件,无内容

    printf("%s is an empty file\r\n", &XBYTE[0x7F80]);

  else

  {

    i = 0;

 while(1)

 {   

   memset(buffer, 0, 513);//数组最后一个元素存储字符串结尾0

      //每次读取512字节并显示,如果返回值小于512字节,则结束

      ret = fread(&XBYTE[0x7F80], buffer, i*512, 512);

   printf("%s", buffer);     

   if(ret < 512)//已经读取完毕

     break;

   i++;

 }  

  }   

}

    通过ls命令可以看到test.txt文件有68字节,使用“cat test.txt”可以看到文件内容是一段basic代码,执行示例如图8-8所示。

 

8-8 cat命令执行示例

8.3 行文本编辑器

前面的小命令基本可以完成SD卡上文件的管理,但怎样实现对文本文件内容进行编辑呢?比如插入文本、追加文本、删除文本等操作,我们设计一个行文本编辑程序,可以实现基于“行”的文本插入、删除等操作。

8.3.1 子命令设计

我们设计的文本编辑器实现的基本功能:插入文本、删除文本、追加文本、列出内容、保存到SD卡、统计行数、退出等功能。设计最大支持8192字节的文件编辑,一次插入和追加的文件内容不超过512字节,输入的一行文本不超过128字节。根据设计分配内存,分配一个8192字节的buffer数组作为文件内容缓存,分配一个512字节的temp_buffer数组作为临时缓存,再分配一个128字节的temp数组作为一行数据的缓存。

首先,把文件内容从SD卡读取到文件缓存buffer;然后,接收用户的输入命令;最后,解析命令,并根据命令完成相应的文本编辑功能,编辑完成后返回重新接收命令。

文本编辑器支持的子命令设计如下:

l l n1,n2  列出n1行到n2行之间的内容;

l d n1,n2  删除n1行到n2行之间的内容;

l a  追加内容到结尾,“回车”+“.”+“回车”完成追加并返回命令行;    

l i n  第n行之前插入内容,“回车”+“.”+“回车”完成插入并返回命令行;

l w  保存文件内容缓存至SD卡;

l n  统计文件行数;

l q  退出文件编辑程序,回到操作系统;

l h  显示支持的功能。

8.3.2 子命令实现方法分析

1. 插入文本

接收用户输入数据至temp数组,当遇到回车时,则认为完成一行的输入,把temp中的一行数据输入temp_buffer,再接收新行并输入temp_buffer,直到遇到插入结束标志(“回车”+“.”+“回车”),把temp_buffer缓存中数据插入到文件缓存buffer的指定位置,重新回到命令解析模式,继续处理其它子命令。

怎样把临时缓存temp_buffer中的内容插入到文件缓存buffer中呢?

假设要把临时缓存temp_buffer中的内容插入到第6行,temp_buffer中总共有128字节,可以先把第6行行首到文件结尾的内容整体往后移动128字节,然后把temp_buffer中的128字节内容插入到空余出来地方。

2. 追加文本

接收用户输入的每一行数据至临时缓存temp_buffer,直到遇到追加结束标志(“回车”+“.”+“回车”),把temp_buffer缓存中的数据复制到文件缓存buffer的结尾即可。

3. 列出文本

列出n1到n2行之间的内容,并且在行首显示行号。把文件缓存中的内容从头到尾循环一遍,只要当前行号位于n1和n2之间,就显示当前字符出来,当前行号仍然统计回车符。

4. 删除文本

假设用户要删除n1至n2行之间的内容,可以先找到n1行的行首作为删除的开始位置start,再找到n2行的行尾作为删除的结束位置end,只需要删除start至end之间的内容即可。

怎样删除文件缓存buffer中start和end之间的数据呢?

我们可以把end后面直到文件结尾的内容依次往start位置搬移,就是把end后面的内容整体往前平移,覆盖要删除的内容。

5. 保存文本

保存文本就是把文件缓存buffer中的数据写入SD卡文件。如果文件不存在,则先创建文件后写入;如果文件为空,则直接写入;如果文件存在且不为空,则先删除原文件,再创建文件,最后写入内容。文件的操作函数上一章实现好了。

6. 统计行数

计算换行符“\n”的个数。

7. 退出

接收到q子命令时,设置退出标志为1,主函数里检测标志,如果标志为1,则返回到操作系统。

8.3.3 源代码分析

#include <reg51.h>

#include <stdio.h>

#include <string.h>

#include <absacc.h>

#include "file.h"

 

char buffer[8192];//文件内容缓存,暂时支持最大8192字节的文件

char cmd_line[100];//子命令存储缓存

char temp_buffer[512];//临时缓存,存放每次插入或追加的内容

char quit;//退出标志

// 向串口输出一个字符

void PutChar(char c)

{

SBUF = c;

while (TI == 0);

TI = 0;

}

void Init_USART()

{

SCON = 0x50;

TMOD = 0x20;

PCON = 0x00;

TH1 = TL1 = 0xFD;

TI = RI = 0;

TR1 = 1;  

}

//保存编辑好的文件至SD卡

void save_file()

{  

  int file_len, ret;

  file_len = fsize(&XBYTE[0x7F80]);//文件名存在0x7F80处

  if(file_len == -1)//如果文件不存在,则创建新文件并写入

  {

    ret = fcreate(&XBYTE[0x7F80]);

   if(ret == -1)

{

  printf("save file error!\r\n");

  return;

}

fwrite(&XBYTE[0x7F80], buffer, strlen(buffer));

  }else if(file_len == 0) //如果文件大小是0,则直接写入

  {

    fwrite(&XBYTE[0x7F80], buffer, strlen(buffer));  

  }else if(file_len > 0) //如果是非空文件,先删除、再创建、再写入

  {

   fremove(&XBYTE[0x7F80]);

fcreate(&XBYTE[0x7F80]);  

fwrite(&XBYTE[0x7F80], buffer, strlen(buffer));  

  }   

}

//插入小工具

void insert(char *dest, char *src, int pos)

{

  int length, i, j;

 

  length = strlen(src);

  for(i = strlen(dest) - 1; i >= pos; i--)//整体往后平移

    dest[i + length] = dest[i];

  for(j = pos;j < pos + length; j++)//插入空出来的位置

    dest[j] = src[j - pos];

}

//列出n1行到n2行之间的内容

void list_file_contents()

{

  char string1[5];

  char string2[5];

  char *p = NULL;

  int n1, n2;

  int i;

  int line = 1;

  //子命令是“l n1,n2”,但sscanf只认得空格分割,把逗号替换成空格

  p = strchr(cmd_line, ',');

  *p = ' ';

  sscanf(cmd_line, "l %s %s", string1, string2);

  sscanf(string1, "%d", &n1);//取出n1值

  sscanf(string2, "%d", &n2);//取出n2值

  if(n1 == 1)

    printf("%d  ", line);

  //把文件缓存内容循环一遍,当前行号在n1、n2范围内,显示字符

  for(i=0; i<8192; i++)

  {

   if(buffer[i] == 0) //到文件结尾了,结束

{

   printf("\r\n");

   break;

}

if(line >= n1 && line <= n2)//当前行号位于n1和n2之间时,显示

      printf("%c", buffer[i]);    

    if(buffer[i] == '\n')//计算当前行号

    {   

      line++;

      if(line >= n1 && line <= n2)

        printf("%d  ", line);//显示行号

   if(line > n2)//行号大于n2,结束显示

     break;

    }

  }

}

//删除n1行到n2行之间的内容

void delete_file_contents()

{

  char *p = NULL;

  int n1, n2;

  int i;

  int line = 1;

  int start = 0;

  int end = 0;

  

  p = strchr(cmd_line, ',');

  *p = ' ';

  sscanf(cmd_line, "d %d %d", &n1, &n2);//取出n1、n2

  if(n1 == 1)

    start = 0;  

  for(i=0; i<8192; i++)

  {     

    if(buffer[i] == '\n')

    {   

      line++;      

      if(line == n1)

        start = i + 1;//找出n1行的行首作为删除的起始位置

      if(line == n2 + 1)//n2的行尾时,行号已经变成了n2+1

        end = i;//找出n2的行尾作为删除的终止位置

    }

  }

 for(i=0; i<8192 - end -1; i++)

 {

  if(buffer[start + i] == 0)//到了文件末尾

    break;

  //end后面内容整体往前平移,覆盖要删除的内容,包括最后一个0

  buffer[start + i] = buffer[end + 1 + i];

 }

}

//追加文本内容至文件结尾

void append_txt()

{   

  int i;

  int line_len;

  char temp[128];

  

  memset(temp_buffer, 0 ,512);

  while(1)

  {

    memset(temp, 0, 128);

    gets(temp, 128);//读取输入的一行数据,遇到回车往下执行

 printf("\r");  //gets只回显换行,不回显回车,\r\n回车换行

    for(i=0; i<512; i++)

    { //把接收到的一行数据添加到临时缓存的末尾

      if(temp_buffer[i] == 0)

      {

        strcpy(&temp_buffer[i], temp);//复制的内容不包含回车换行

        line_len = strlen(temp);

        //行尾统一以回车换行(\r\n)作为结束标志,自行添加

temp_buffer[line_len + i] = '\r';

        temp_buffer[line_len + i + 1] = '\n';

        break;

      }

    }    

    //回车换行+'.'+回车换行是追加结束标志,追加结束需要把临时缓存

    //内容添加到文件缓存末尾,然后回到命令界面

    if((temp_buffer[line_len + i - 1] == '.') &&  (temp_buffer[line_len + i -2] == '\n'))

    {//“.”和回车换行也输入了temp_buffer中,需要再去掉

       temp_buffer[line_len + i + 1] = 0;//去掉多余的“.”

       temp_buffer[line_len + i] = 0;//去掉多余的回车

       temp_buffer[line_len + i - 1] = 0;//去掉多余的换行

       for(i=0; i<8192; i++)

       {//把临时缓存的内容添加到文件缓存末尾

         if(buffer[i] == 0)

         {

           strcpy(&buffer[i], temp_buffer);           

           break;

         }

       }

       break;

    }

  }

}

//插入文本内容

void insert_txt()

{

  char temp[128];

  int i;

  int n;

  int num = 1;

  int line_len;

  

  memset(temp_buffer, 0 ,512);

  sscanf(cmd_line, "i %d", &n);

  while(1)

  {

    memset(temp, 0, 128);

    gets(temp, 128);//输入一行数据

 printf("\r");

    for(i=0; i<512; i++)

    {

      if(temp_buffer[i] == 0)

      {

        strcpy(&temp_buffer[i], temp);

        line_len = strlen(temp);

        temp_buffer[line_len + i] = '\r';

        temp_buffer[line_len + i + 1] = '\n'; //行尾以回车换行结束

        break;

      }

    }        

    //遇到插入结束标志

    if((temp_buffer[line_len + i - 1] == '.') &&  (temp_buffer[line_len + i -2] == '\n'))

    {  //清除点和回车换行

       temp_buffer[line_len + i + 1] = 0;

       temp_buffer[line_len + i] = 0;        

       temp_buffer[line_len + i - 1] = 0;

       for(i=0; i<8192; i++)//找出插入行的行首位置

       {

         if(buffer[i] == '\n')    

           num++;

         if(num == n)

           break;   

       }         

       //把临时缓存内容插入文件缓存,若插入第一行,实际就是插入

//开始位置,其它行,插入位置就是i+1

insert(buffer, temp_buffer, n == 1 ? 0 : i + 1);

       break;

    }

  }  

}

//统计行数

void count_lines()

{

 int i;

 int num = 0;

 

 for(i=0; i<8192; i++)  

   if(buffer[i] == '\n')//就是统计换行符个数

     num++;

 printf("%d\r\n", num + 1);    

}

//解析子命令

void parse_cmd_line()

{

  printf("->");

  gets(cmd_line, 100);

  printf("\r");  

  if(cmd_line[0] == 'h')//帮助子命令

  {

    printf("l n1,n2---List n1,n2 contents\r\n");

    printf("d n1,n2---Delete n1,n2 contents\r\n");

    printf("a---append txt to the last line, enter+'.'+ enter back to cmd_line state\r\n");

    printf("i n---insert txt to the  line n, enter + '.'+ enter back to cmd_line state\r\n");

    printf("w---write to file\r\n");

    printf("n---how many lines\r\n");

    printf("q---Quit\r\n");

    printf("h---Show this help\r\n");

  }else if(cmd_line[0] == 'l')//列出子命令

  {

    list_file_contents();

  }else if(cmd_line[0] == 'd')//删除子命令

  {

    delete_file_contents();

  }else if(cmd_line[0] == 'a')//追加子命令

  {

    append_txt();

  }else if(cmd_line[0] == 'i')//插入子命令

  {

    insert_txt();

  }else if(cmd_line[0] == 'n')//统计行数子命令

  {

    count_lines();

  }else if(cmd_line[0] == 'w')//保存子命令

  {

    save_file();

  }else if(cmd_line[0] == 'q')//返回操作系统子命令

  {

    quit = 1;

  }else printf("cmd_line error!\r\n");//命令输入有误

  memset(cmd_line, 0, 100);

}

// 主程序

void main()

{

  int ret;

 

  Init_USART();

  quit = 0;

  memset(buffer, 0, 8192);   

  ret = fsize(&XBYTE[0x7F80]);

  if(ret > 0)//只有文件存在且有内容才需要读取到文件缓存

  {

    ret = fread(&XBYTE[0x7F80], buffer, 0, ret);

    if(ret == -1)

    {

      printf("read %s error!\r\n", &XBYTE[0x7F80]);

  return;

    }

  }   

  while(1)

  {

    parse_cmd_line();//循环解析命令并处理命令

 if(quit == 1)

   return;  /返回到操作系统

  }    

}

8.3.4 文本编辑器使用演示

文本编辑程序的使用范例如图8-9所示。分别操作了打开test.txt文件,统计行数,列出1到7行内容,删除3到6行内容,再统计行数,再显示1到3行内容,在第2行处插入两行文本,统计行数,列出文件缓存内容,保存至SD卡文件,退回到操作系统

 

 

8-9 ed命令执行示例

到这里为止,我们把计算机硬件的组成、硬件的工作原理、简单磁盘操作系统工作原理和应用程序开发都展现给了大家。打个比方,计算机硬件是一切的物质基础,好比人的身体;操作系统向下管理硬件,向上提供服务,好比人的思想;应用程序实现各种各样的具体功能,好比人的具体工作。我们首先要有健康的身体,然后要有强大的思想(做人、做事、宽心的哲学),最后还要有具体的事业,计算机系统也是如此,三者相辅相成,互为促进,缺一不可。前面还说过“结构、指令、程序”的模型,“结构”与这里说的硬件一致,“程序”与这里说的应用程序一致,“指令”与这里说的操作系统一致,操作系统可以认为是计算机指令集的更高级别的抽象。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
计算机基础与程序设计试题与答案- 卷面总分:100分 答题时间:90分钟 试卷题量:50题 一、单选题(共51题,共100分) 1.光盘驱动器通过激光束来读取光盘上的数据时,光学头与光盘() A.直接接触 B.不直接接触 C.播放VCD时接触 D.有时接触有时不接触 正确答案:B 您的答案: 本题解析: 暂无解析 2.一个完整的计算机系统应该包括() A.主机和外设 B.主机和操作系统 C.硬件系统和系统软件 D.硬件系统和软件系统 正确答案:D 您的答案: 本题解析: 暂无解析 3.在计算机中,指令通常是由() A.操作码、操作数组成 B.源操作数、目标操作数组成 C.反码、补码组成 D.直接寻址、目的寻址组成 正确答案:A 您的答案: 本题解析: 暂无解析 4.下列设备中属于输出设备的是() A.扫描仪 B.键盘 C.鼠标 D.显示器 正确答案:D 您的答案: 本题解析: 暂无解析 5.如果文件存储的是数据在内存中存放的字节形式,则该文件是() A.ASCII文件 B.文本文件 C.二进制文件 D.设备文件 正确答案:C 您的答案: 本题解析: 暂无解析 6.整数-2在机器内存中的表示是() A.1000000000000010 B.1111111111111110 C.11111111111111101 D.1111111111111111 正确答案:B 您的答案: 本题解析: 暂无解析 7.下列软件中属于应用软件的是() A.Ms-Dos B.Windows2000 C.WPS2000字处理软件 D.C语言处理程序 正确答案:C 您的答案: 本题解析: 暂无解析 8.在计算机硬件设备中能用作输入设备是() A.显示器 B.键盘 C.绘图仪 D.打印机 正确答案:B 您的答案: 本题解析: 暂无解析 9.在Dos系统中可以用文件扩展名表示文件类型,其中批处理文件的扩展名是() A..OBJ B..C C..BAT D..BAK 正确答案:C 您的答案: 本题解析: 暂无解析 10.具有只读功能的内存储器是指() A.ROM B.RAM C.硬盘 D.CD-ROM 正确答案:A 您的答案: 本题解析: 暂无解析 11.以下属于人际传播的活动是 A.健康大课堂 B.电视台健康专题节目 C.广播电台健康频道 D.北京市疾控中心微博 E.报纸健康专版 正确答案:E 您的答案: 本题解析: 暂无解析 12.存储量1MB等于() A.23字节 B.210字节 C.220字节 D.230字节 正确答案:C 您的答案: 本题解析: 暂无解析 13.下面数据中最小数是() A.(0.1100)2 B.(0.64)8 C.(0.8125)10 D.(0.D)16 正确答案:A 您的答案: 本题解析: 暂无解析 14.CPU处理数据和指令的基本单位是字(Word),一个字的字长是() A.8bits B.16bits C.32bits D.与CPU数据总线有关的bit位数 正确答案:D 您的答案: 本题解析: 暂无解析 15.与十进制1100等值的十六进制数是() A.44A B.44C C.54A D.54C 正确答案:B 您的答案: 本题解析: 暂无解析 16.Unix是() A.单用户、单任务的操作系统 B.单用户、多任务的操作系统 C.多用户、单任务的操作系统 D.多用户、多任务的操作系统 正确答案:D 您的答案: 本题解析: 暂无解析 17.一个二进制数位也称为一个() A.word B.byte C.KB D.bit 正确答案:D 您的答案: 本题解析: 暂无解析 18.()是用助记符来表示指令的符号语言。 A.机器语言 B.汇编语言 C.高级语言 D.语言处理程序 正确答案:B 您的答案: 本题解析: 暂无解析 19.-123的原码是() A.11111011 B.10000101 C.01111011 D.0000101 正确答案:A 您的答案: 本题解析: 暂无解析 20.若当前路径为C盘TC子目录,仅将A盘根目录下的文件名第3个字符为P,扩展名为C的 所有文件复制到C盘TC子目录下,一定成功的命令是() A.COPYA:\*P*.C B.COPYA:\??P*.CC: C.COPYA:\??P*.C D.COPY??P*.CC:\TC 正确答案:C 您的答案: 本题解析: 暂无解析 21.按冯诺依曼的设计思想,计算机采用的数制是() A.二进制 B.八进制 C.十进制 D.十六进制 正确答案:A 您的答案: 本题解析: 暂无解析 22.世界上第一台通用电子数字计算机诞生于() A.美国 B.英国 C.德国 D.日本 正确答案:A 您的答案: 本题解析: 暂无解析 23.第一台电子计算机是1946年在美国研制的,该机的英文缩

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值