当前版本将zengl嵌入式脚本语言移植到了zenglOX里,从而在zenglOX里可以编辑和执行一些简单的脚本了。

    页面导航:
项目下载地址:

    v2.3.0版本的项目地址:

    github.com地址:https://github.com/zenglong/zenglOX (只包含git提交上来的源代码)

    Dropbox地址:点此进入Dropbox网盘  该版本位于zenglOX_v2.3.0的文件夹中,文件夹里的zip压缩包为源代码,readme.txt为版本的简单说明。

    Google Drive地址:点此进入Google Drive云端硬盘 对应也是zenglOX_v2.3.0的文件夹。

    sourceforge地址:https://sourceforge.net/projects/zenglox/files  对应也是zenglOX_v2.3.0的文件夹。

    Dropbox与Google Drive如果正常途径访问不了,则需要使用代理访问。

readme说明文档(包含截图):

    以下是网盘中readme.txt文件的内容(在github的commit message里也可以看到同样的内容),该文档详细的说明了运行步骤,注意事项,以及zengl程式的使用方法(包含截图):

zenglOX v2.3.0 移植zengl嵌入式脚本语言

先按照v2.0.0版本的要求, 编译好bochs,让其支持e1000网卡,
将源代码解压后, 执行make和make iso命令, 分别得到initrd.img, zenglOX.bin及zenglOX.iso文件,
在root权限下运行startBochs来启动bochs虚拟机, 如果执行失败, 可能是bochsrc.txt配置不正确,
具体原因请参考v2.0.0的官方文章

默认情况下, 源代码根目录下的hd.img只有32.5KB大小(该文件可以随着使用而动态的增加体积),
也就是没有分区过的bochs磁盘镜像文件, 如果你在该版本不想重新分区格式化一遍的话,
可以直接将上一个v2.2.0版本中使用过的hd.img文件拷贝到解压后的源码根目录, 以
覆盖掉默认的hd.img文件。(当前版本并没有对zenglfs文件系统做任何调整, 因此
可以直接使用之前版本里的磁盘镜像文件)

此外,和上一个版本类似, zengl可执行程式放置在光盘镜像里, 需要通过isoget命令
加载到磁盘中。

假设你的分区位于0号ide磁盘设备的1号分区里, 则输入如下命令来分别挂载光盘和磁盘分区:


      图1

当挂载iso和hd成功后, 就可以使用isoget命令来获取更新的ee编辑器, 更新的libc.so库文件,
以及zengl可执行文件到磁盘中(zengl脚本语言是以静态库的形式直接捆绑在zengl可执行文件里的,
因为目前zenglOX对动态库支持的并不完整, zengl动态库里的函数地址在进行赋值时,
有些地方并没有被替换为真实的线性地址, 而且动态库也不方便调试, 因此就使用的静态库):


      图2

上面除了前面提到的ee, libc.so, 以及zengl外, 还有 21.zl ,
21_def.zl 以及 21_fun.zl 这三个脚本文件, 这三个是21点扑克命令行界面的zengl脚本
小游戏。是从zengl language项目的v1.0.0版本里的console_21_point目录中提取和
移植过来的(修改了文件名, 去掉了脚本里的中文字符等)。

执行了isoget -all命令后, 就可以测试zengl脚本了:


      图3

玩法请参考官方zengl编程语言栏目v1.0.0版本的相关文章。

读者也可以通过ee编辑器修改21.zl脚本, 比如将Android的初始资金由180亿调整为
200亿等, 读者还可以用ee编辑器自己写一个小脚本:


      图4

上面用ee打开一个新的test.zl脚本文件, 可以在编辑器中输入如下内容:


      图5

保存完test.zl脚本后, 就可以进行测试了:


      图6

还可以对test.zl脚本进行调试:


      图7

通过在最后加入一个 -d 参数, 就可以对脚本进行调试了, 上面先用s命令进行单步
执行, 当执行完第三行代码后, 也就是对hello变量赋值以后, 就可以用p命令显示
hello变量的值了, 最后用c命令继续执行。

有关zengl脚本调试的方法, 请参考zengl编程语言栏目的v1.4.0版本的相关文章。

上面zengl hd/test.zl命令的输出显示: 当前使用的zengl语言的版本号为1.5.0, 这个
版本主要就是增加了对zenglOX操作系统的支持。v1.5.0版本还没有在zengl编程语言
栏目中发布, 过段时间应该会发布。

ee编辑器的版本由v1.3.8更新到了v1.3.9 , 当前版本的ee编辑器主要过滤了
\r字符(因为\r字符会破坏文件内容的显示输出, 也会影响文件的保存)。

每执行一个zengl脚本后, 都会在hd根目录中覆盖生成一个zl.log的日志调试文件, 读者
可以用ee hd/zl.log命令来查看该日志文件的内容, 里面记录了脚本文件的语法树
结构, 生成的指令集等信息。

此外, 如果脚本中有语法错误的话, 显示的语法错误信息中, 只会显示
英文字符, 中文字符则不会被显示出来。

内核代码部分并没做什么大的调整。

最后, 由于zenglOX在读磁盘时使用的是PIO模式, 没有使用DMA模式(DMA模式虽然快,
但是驱动比较难写, 不同的IDE控制器芯片的初始化过程都不太一样, 总之, 作者目前
写不出来), PIO模式读写数据都是通过i/o端口来完成的, 当数据量大的时候, 例如
zengl可执行文件(包含了zengl语言的静态库在内)就有650多KB , 因此, zengl执行时
, 绝大部分时间都消耗在磁盘的读写操作上了, 在VirtualBox虚拟机下, 尤其是
在开启了硬件加速的情况下(默认都开启了), 会明显感觉到读取上的时间消耗。

VirtualBox硬件加速的开关位置位于: 虚拟机的设置界面的 系统 -->> 硬件加速 标签下, 虽然关闭硬件加速可以明显提高读写速度, 但是在
关闭硬件加速的情况下, PS2的第一个端口即键盘相连的端口容易出现reset failed的
情况, 而导致键盘无法使用(这个问题有时候会出现, 有时候不会出现)。因此,
是否需要关闭硬件加速需要读者自行测试来决定。(关闭硬件加速时, 需要全部关闭, 只关闭
一部分容易出现蓝屏, 在之前的zenglOX v1.4.0与v1.4.1的官方文章里还提到过, 关闭
硬件加速可以方便VirtualBox的调试, 所以硬件加速目前是个矛盾体, 关与不关都有各自
的利弊, 不过如果读者只是用VirtualBox简单测试zenglOX的话, 并且不需要VirtualBox
自带的调试功能的话, 作者建议还是保持默认的开启加速的好, 因为硬件加速情况下,
当前版本表现的比较稳定, 虽然磁盘读取速度稍微慢点)

在bochs与VMware下, 读取速度都还可以接受。

如果读者有好的DMA驱动的话, 可以email给作者。

时间: 2014年10月19日
作者: zenglong
官网: www.zengl.com

Makefile文件解析:

    源码根目录下的makefile文件,只新增了一个导出变量:

#makefile for zenglOX

GCC_PATH = /mnt/zenglOX/opt/cross/bin
AS = @$(GCC_PATH)/i586-elf-as
CC = @$(GCC_PATH)/i586-elf-gcc

export CROSS_AS = $(AS)
export CROSS_CC = $(CC)
export CROSS_LD = @$(GCC_PATH)/i586-elf-ld
export CROSS_AR = @$(GCC_PATH)/i586-elf-ar
export CROSS_AS_FLAGS = -gstabs

.................................................


    上面的导出变量CROSS_AR的值,用于指明 ar 工具的完整路径信息,在编译生成zengl语言的静态库文件时,就会用到该工具。

    在build_initrd_img目录下的makefile文件中,对ee编辑器及libc.so库文件的编译规则进行了调整,同时新增了zengl程式的编译规则:

#makefile for ram disk

..................................................
AS_TARGET = cpuid uname reboot shutdown
C_TARGET = shell ls ps cat ata mount unmount testoverflow fdisk format file vga lspci isoget
C_NET_TARGET = arp dhcp ipconf ping
C_EXTRA_TARGET = extra/output/ee extra/output/libc.so extra/output/zengl
C_DEP_LIB = ld.so libcommon.so
C_DEP_NET_LIB = libnetwork.so
..................................................

LIBC_SRCS = standard/libc.c standard/printf.c standard/vsnprintf.c standard/fcvt.c standard/modf.c

ZENGL_SRCS = extra/zengl/linux/zengl_main.c extra/zengl/linux/zengl_parser.c extra/zengl/linux/zengl_symbol.c extra/zengl/linux/zengl_locals.c extra/zengl/linux/zengl_assemble.c extra/zengl/linux/zengl_ld.c extra/zengl/linux/zenglrun_main.c extra/zengl/linux/zenglrun_func.c extra/zengl/linux/zenglApi.c extra/zengl/linux/zenglApi_BltModFuns.c extra/zengl/linux/zenglDebug.c

extra/output/ee: extra/ee.c standard/include/stdlib.h common.h syscall.h task.h fs.h kheap.h extra/output/libc.so
	@echo "building $@"
	@mkdir -p extra/output
	$(CROSS_CC) -Wl,-emain -Wl,-dynamic-linker,ld.so -o extra/output/ee extra/ee.c $(CROSS_CLINK_FLAGS) -I standard/include -I. -L. -lcommon -L extra/output -lc

extra/output/libc.so: $(LIBC_SRCS) standard/include/stdlib.h common.h syscall.h task.h fs.h kheap.h
	@echo "building $@"
	@mkdir -p extra/output
	$(CROSS_CC) -fPIC -Wl,-shared -Wl,-soname,hd/lib/libc.so -o extra/output/libc.so $(LIBC_SRCS) $(CROSS_CLINK_FLAGS) -I standard/include -I. -L. -lcommon

extra/output/zengl: extra/zengl/zenglOX/zengl.c $(ZENGL_SRCS) libcommon.so extra/output/libc.so
	@echo "building $@"
	@mkdir -p extra/output
	@(cd extra/zengl/zenglOX; make all)
	@cp -rvf extra/zengl/zenglOX/zengl extra/output/

..................................................

clean:
	$(RM) $(RMFLAGS) *.o
	$(RM) $(RMFLAGS) $(AS_TARGET) $(C_TARGET) $(C_NET_TARGET) $(C_DEP_LIB) $(C_DEP_NET_LIB)
	$(RM) $(RMFLAGS) extra/output
	$(RM) $(RMFLAGS) make_initrd
	$(RM) $(RMFLAGS) initrd.img
	@(cd extra/zengl/zenglOX; make clean)

all: initrd.img


    上面的makefile文件中,LIBC_SRCS变量对应的值为编译libc.so所需的C源文件,其中vsnprintf.c的文件里实现了vsnprintf的C库函数,另外两个fcvt.cmodf.c文件里则实现了fcvt之类的库函数,这些库函数可以将浮点数转换为字符串,并且是按照IEEE754标准里的浮点格式来进行转换的(读者可以在汇编语言栏目的"汇编数据处理 (三) 浮点数"的文章中查看到该浮点格式的详细说明)。这些库函数的源代码,有的来源自网络,有的则是从Minix3的源代码里移植过来的。

    上面的ZENGL_SRCS变量所对应的值,则定义了编译zengl语言的静态库时,所需要的源文件。可以看到,这些源文件都位于extra/zengl/linux目录中(此路径是相对于build_initrd_img目录的),也就是用的linux目录下的C源文件来进行编译的,当需要生成extra/output/zengl的可执行文件时,它会先通过@(cd extra/zengl/zenglOX; make all)命令进入zengl项目的zenglOX目录中,并通过make all命令来生成zengl可执行文件,接着再通过cp命令将可执行文件拷贝到extra/output目录中(ee编辑器,libc.so库文件最后也会统一放在该目录下,该目录中的文件最终会被拷贝到iso镜像的EXTRA目录中)。

    此外,在clean规则里,最后也会cd进入zenglOX目录,并通过make clean命令来完成zengl项目的清理工作。

    刚才提到了,编译zengl语言的静态库,以及生成zengl可执行文件时,会进入zengl项目的zenglOX目录中,在该目录下,有个makefile文件,该文件定义了静态库与可执行文件的编译生成规则(该makefile文件的完整相对路径为:build_initrd_img/extra/zengl/zenglOX/makefile),makefile文件的内容如下:

#makefile for zengl language

CC = $(CROSS_CC)
AR = $(CROSS_AR)

ARFLAGS = rc
CFLAGS = -g3 -ggdb -O0 -std=c99 -Wall -Wextra -Wno-switch -Wno-unused-variable -Wno-unused-parameter -Wno-uninitialized -Wno-sign-compare -Wno-return-type -Wno-missing-braces -Wno-pointer-sign -ffreestanding -nostdlib
CDEFINES = -D ZL_LANG_EN_WITH_CH -D ZL_EXP_OS_IN_ZENGLOX
STATIC_CFLAGS = $(CFLAGS) -fvisibility=hidden

SRCS = ../linux/zengl_main.c ../linux/zengl_parser.c ../linux/zengl_symbol.c ../linux/zengl_locals.c ../linux/zengl_assemble.c ../linux/zengl_ld.c ../linux/zenglrun_main.c ../linux/zenglrun_func.c ../linux/zenglApi.c ../linux/zenglApi_BltModFuns.c ../linux/zenglDebug.c
OBJS = zengl_main.o zengl_parser.o zengl_symbol.o zengl_locals.o zengl_assemble.o zengl_ld.o zenglrun_main.o zenglrun_func.o zenglApi.o zenglApi_BltModFuns.o zenglDebug.o

zengl: zengl.o libzengl.a
	@echo "building $@"
	$(CC) $(CFLAGS) -Wl,-emain -Wl,-dynamic-linker,ld.so -o zengl zengl.o libzengl.a -I ../../../standard/include -I ../../.. -L ../../../ -lcommon -L ../../output -lc

libzengl.a : $(OBJS) longjmp.o setjmp.o
	@echo "building $@"
	$(AR) $(ARFLAGS) libzengl.a $(OBJS) longjmp.o setjmp.o

$(OBJS): $(SRCS) ../linux/zengl_global.h ../linux/zengl_locals.h ../linux/zengl_exportfuns.h ../../../standard/include/stdlib.h ./setjmp.h
	@echo "building $@ ..."
	$(CC) $(CDEFINES) $(STATIC_CFLAGS)  -c $(SRCS) -I . -I ../../../standard/include -I ../../.. -I ../linux -L ../../../ -lcommon -L ../../output -lc

zengl.o: zengl.c ../linux/zengl_exportfuns.h
	@echo "building $@"
	$(CC) $(CFLAGS) -c zengl.c -I ../../../standard/include -I ../../.. -I ../linux 

%.o: %.s
	@echo "building $@"
	$(CROSS_AS) -o $@ $< $(CROSS_AS_FLAGS)

clean:
	rm -fv *.o
	rm -fv libzengl.a
	rm -fv zengl

all: zengl


    上面makefile文件里,最开始的CC变量定义了gcc交叉编译器的完整路径信息,AR变量则定义了ar工具(即静态库的打包工具)的完整路径信息,这两个变量的值$(CROSS_CC)$(CROSS_AR),都是从顶层根目录的makefile文件里通过export关键字导出来的。

    上面的CFLAGS变量里,定义了需要传递给gcc编译器的参数,其中-Wno-switch等以-Wno开头的参数,是用于消除gcc编译zengl时,可能会产生的警告信息的。例如:-Wno-switch参数用于消除当switch....case....结构中没有default关键字时所产生的警告信息,-Wno-unused-variable参数用于消除当函数里存在没被使用过的变量时所产生的警告信息,等等。

    上面的CDEFINES变量里,定义了需要传递给gcc编译器的预定义宏参数,这样,gcc编译zengl时,就会预定义 ZL_LANG_EN_WITH_CH 与 ZL_EXP_OS_IN_ZENGLOX 宏,有了ZL_EXP_OS_IN_ZENGLOX宏,就说明当前是为zenglOX操作系统来编译zengl的,源代码中就会针对该系统做些特殊处理。

    SRCS变量定义了编译静态库时,所需要的linux目录里的C源文件,OBJS变量则定义了打包静态库时所需要的目标文件,上面的$(OBJS)规则中,就定义了如何利用SRCS变量里的C源文件,来生成目标文件的编译规则,在该规则中,通过 -c 参数来告诉gcc,只生成.o结尾的中间目标文件,而不生成ELF格式的最终的可执行文件。

    当生成了OBJS变量对应的zengl_main.o之类的目标文件后,就可以通过ar工具来将这些目标文件打包成libzengl.a的静态库了。

    不过,这里需要注意的是:在生成 libzengl.a 时,还依赖了longjmp.o及setjmp.o两个目标文件,这两个目标文件对应的C源文件分别为longjmp.s与setjmp.s,这是两个汇编程式,都位于build_initrd_img/extra/zengl/zenglOX目录中,它们分别定义了longjmp和setjmp函数,这两个函数是用于长跳转的,它们本应属于C标准的库函数,但是,当作者将它们放置到libc.so中时,这两个函数无法正常使用,因此,只好将其独立出来,放置到zengl项目的zenglOX目录下,作为zengl程式的两个单独的模块。

    有了静态库后,只需将zengl.o的主入口模块与libzengl.a的静态库编译到一起,就可以生成zengl可执行文件了。这里,zengl.o主入口模块对应的C源文件为zengl.c文件,该文件的完整相对路径为build_initrd_img/extra/zengl/zenglOX/zengl.c 。

    在clean规则里,会通过rm -fv命令将所有生成的.o中间目标文件,libzengl.a静态库文件,以及zengl可执行文件都删除掉。

源码简析:

    和zengl语言静态库相关的linux源码部分,唯一改动的文件就是zengl_global.h头文件(完整相对路径为:build_initrd_img/extra/zengl/linux/zengl_global.h),改动的代码如下:

/*
 * global.h
 *
 *  Created on: 2012-1-22
 *      Author: zenglong
 */

..................................................

#ifdef ZL_EXP_OS_IN_ZENGLOX
	#include <stdlib.h>
	#include "setjmp.h"
	#include "zengl_locals.h"
	#include "zengl_exportfuns.h"
#else
	#include <stdio.h>
	#include <stdlib.h>
	#include <memory.h>
	#include <stdarg.h>
	#include <ctype.h>
	#include <string.h>
	#include <time.h>
	#include "setjmp.h"
	#include "zengl_locals.h"
	#include "zengl_exportfuns.h"
#endif

..................................................


    当预定义了ZL_EXP_OS_IN_ZENGLOX宏时,就说明当前是为zenglOX系统进行的编译,在zenglOX里,C标准库函数都包含在stdlib.h头文件里,因此就只需包含stdlib.h即可,其他的像stdio.h之类的头文件就都不需要包含了(目前也不存在像stdio.h之类的头文件)。setjmp.h位于zengl项目的zenglOX目录中,该头文件声明的longjmp与setjmp函数在zenglOX里不属于C库函数(尽管在linux或者别的系统里,它属于C标准库函数),原因在之前讲解makefile时解释过,因此,需要单独包含进来。

    在github上查看当前版本的源代码时,可能会看到一些乱码,例如:


图8

    这些乱码是因为zengl项目的windows目录内的源代码采用的是GBK的编码,而github网站上显示源码时,采用的是UTF8的编码,因此,中文字符就会是乱码,英文字符的话,GBK与UTF8编码都是一样的,不会出现乱码,linux目录下采用的是UTF8编码,因此,linux目录里的源码不会出现中文乱码。其实根本不需要关注windows里的代码,因为zenglOX是采用linux目录中的源码来进行编译的。

    前面讲解makefile时提到过,zengl可执行文件是由libzengl.a静态库与zengl.o主入口模块来编译生成的,zengl.o模块对应的源文件为zengl.c(该文件的完整相对路径为:build_initrd_img/extra/zengl/zenglOX/zengl.c),该文件里,main入口函数的代码如下:

#define ZL_EXP_OS_IN_ZENGLOX //在加载下面的zengl头文件之前,windows系统请定义ZL_EXP_OS_IN_WINDOWS,linux , mac系统请定义ZL_EXP_OS_IN_LINUX
#include "zengl_exportfuns.h"
#include <stdlib.h>

....................................................

/**
	用户程序执行入口。
*/
int main(VOID * task, int argc, char * argv[])
{
	ZL_EXP_INT builtinID;
	ZL_EXP_VOID * VM;

	if(argc < 2)
	{
		printf("usage: %s <script_filename> \n",argv[0]);
		exit(-1);
	}
	else if(argc == 2 && (strcmp(argv[1], "-v")==0))
	{
		printf("zengl lib version is v%d.%d.%d", 
				ZL_EXP_MAJOR_VERSION, ZL_EXP_MINOR_VERSION, ZL_EXP_REVISION);
		return 0;
	}

	debuglog = fopen("hd/zl.log", "w");
	VM = zenglApi_Open();
	zenglApi_SetFlags(VM,(ZENGL_EXPORT_VM_MAIN_ARG_FLAGS)(ZL_EXP_CP_AF_IN_DEBUG_MODE | ZL_EXP_CP_AF_OUTPUT_DEBUG_INFO));
	zenglApi_SetHandle(VM,ZL_EXP_VFLAG_HANDLE_COMPILE_INFO,main_userdef_info);
	zenglApi_SetHandle(VM,ZL_EXP_VFLAG_HANDLE_RUN_INFO,main_userdef_run_info);
	zenglApi_SetHandle(VM,ZL_EXP_VFLAG_HANDLE_RUN_PRINT,main_userdef_run_print);
	if((builtinID = zenglApi_SetModInitHandle(VM,"builtin",main_builtin_module_init)) == -1)
		main_exit(VM,"set use module failed:%s",zenglApi_GetErrorString(VM));

	if(argc >= 3 && strcmp(argv[2],"-d") == 0)
		zenglApi_DebugSetBreakHandle(VM,main_debug_break,main_debug_conditionError,ZL_EXP_TRUE,ZL_EXP_FALSE); //设置调试API

	if(zenglApi_Run(VM,argv[1]) == -1) //编译执行zengl脚本
		main_exit(VM,"compile and run <%s> failed:%s\n",argv[1],zenglApi_GetErrorString(VM));
	zenglApi_Close(VM);
	fclose(debuglog);
	
	return 0;
}


    可以看到,main入口函数里,其实就是先通过zenglApi_Open接口函数来创建一个VM虚拟机,再通过zenglApi_SetFlags接口设置一些选项(例如开启调试模式等),并通过zenglApi_SetHandlezenglApi_SetModInitHandle接口设置一些特殊的处理函数以及builtin模块的初始化函数等。

    接着就可以通过zenglApi_Run接口在VM虚拟机中执行argv[1]所对应的脚本文件,脚本执行完后,最后再通过zenglApi_Close接口来关闭虚拟机,并释放相关资源。

    从上面的代码里,可以看到,在zenglOX的命令行下,可以简单的通过zengl -v命令来查看当前zengl语言静态库的版本号信息:


图9

    此外,一开始会通过fopen函数来打开hd/zl.log文件,在zengl虚拟机执行脚本时,会将语法树等调试信息输出到该文件中,在zenglOX命令行下,可以通过ee hd/zl.log命令来查看该日志文件里的信息。

    ee编辑器的源代码也只做了很小的改动,在github上也可以看到代码中所做的调整,其改动的代码如下(ee编辑器的C源文件为ee.c,完整相对路径为:build_initrd_img/extra/ee.c):

#include <stdlib.h>

#define EE_MAJOR_VERSION 1
#define EE_MINOR_VERSION 3
#define EE_REVISION 9

...................................................

/* file operation ---*/
void file_read(void)
{
	...................................................

	/* read complete line */
	do {
		c = fgetc(fi);
		if(c == EOF) 
		{
			fclose(fi);
			fi = 0;   /* no more read */
			break;
		}
		if(c == 9) 
		{
			/* tab */
			if(flag[TAB] == 0) show_flag(TAB, 1);
			do (*ae++ = BLK);
			while( ((ae-col) % tabsize) != 0);
		}
		else if(c == '\r') // 自动跳过和剔除 \r 字符
			;
		else if(c == LF) 
		{
			*ae++ = EOL;
			col = ae;
			ytot++;
		}
		else 
			*ae++ = c;
	} while(ae < aa+AMAX-BMAX || c != LF);

	...................................................
}

...................................................


    首先是EE_REVISION修正版本号变为了9,可以在zenglOX命令行下,通过ee -v命令来查看到完整的版本号信息:


图10

    你可以通过版本号信息,来判断你的磁盘中的ee编辑器是否是最新的版本,如果不是最新的版本,则可以通过isoget -all命令来获取最新版本。

    此外,上面的file_read函数中,当fgetc函数获取到的文件字符为 '\r' 时,就直接跳过去,这样就可以将文件里的 \r 字符给过滤掉了,之所以要去掉 \r 字符,是因为该字符会破坏编辑器的内容显示,也会影响文件内容的保存操作。

    libc.so库函数里的代码很多来源自网络,例如libc.c中定义的atol库函数(libc.c文件的完整相对路径为:build_initrd_img/standard/libc.c):

// convert ASCII string to long
// see http://code.google.com/p/minilib-c/source/browse/trunk/stdlib/atol.c
long atol(const char *s)
{
        return strtol (s, NULL, 10);
}


    该函数用于将字符串转为long整数类型,函数的代码包括其调用的strtol函数都来自于google code上的minilib-c项目。

    以上介绍的zengl程式,ee编辑器,以及libc.so的源代码都可以通过gdb来进行调试,调试方法请参考上一篇v2.2.0版本的文章。

    内核代码部分并没做什么太大的调整,只是zlox_kernel.c即内核的主入口函数里,增加了一条finit的汇编指令来初始化FPU浮点单元:

....................................................

//zenglOX kernel main entry
ZLOX_SINT32 zlox_kernel_main(ZLOX_MULTIBOOT * mboot_ptr, ZLOX_UINT32 initial_stack)
{
	....................................................
	zlox_syscall_monitor_write("! I will execve a shell\n"
				"you can input some command: ls , ps , cat , uname , cpuid , shell ,"
				" reboot , shutdown , ata , mount , unmount , testoverflow , fdisk , "
				"format , file , vga , dhcp isoget...\n\n"
				" this version we ported zengl language O(^_^)O~~ \n\n");

	asm ("finit");

	zlox_syscall_execve("shell");
	....................................................
}


    因为,如果不使用finit指令来初始化的话,那么在bochs虚拟机下,当遇到FPU相关的浮点指令时,虽然它会去自动初始化FPU,但是最开始执行的那个浮点运算指令就会被抛弃掉,从而导致最开始的浮点指令无法得到正确执行(VirtualBox与VMware下则没有这个现象),因此,在内核一开始手动初始化一下FPU浮点单元,还是很有必要的。

    其他的没有讲解的代码,请读者通过gdb调试器来自行分析。

    最后,再介绍一个linux下分析源码的实用工具:我们可以在linux命令行下通过grep命令来搜索特定的关键字:


图11

    通过给grep传递-inIEr --color=ALWAYS参数,可以让其高亮显示关键字,后面的"fprintf"参数为要搜索的关键字,最后一个 ./ 参数表示在当前目录及子目录中进行搜索。

    OK,就到这里,休息,休息一下 o(∩_∩)o~~
 
上下篇

下一篇: zenglOX v2.4.0 DMA(Direct Memory Access)

上一篇: zenglOX v2.2.0 ee(easy editor)文本编辑器 C标准库函数 uheap(单独的用户堆空间) atapi驱动BUG zenglfs文件系统BUG 分页BUG 堆算法BUG等修复

相关文章

zenglOX v2.4.0 DMA(Direct Memory Access)

zenglOX v0.0.10 Keyboard(获取键盘的输入)

zenglOX v0.0.1 开发自己的操作系统

zenglOX v2.0.0 E1000系列网卡驱动, PCI驱动, PS/2控制器驱动, 以太网,ARP,IP,UDP,DHCP,ICMP协议, dhcp,arp,ipconf,ping,lspci命令行程式

zenglOX v0.0.6 使用Heap(堆)动态分配和释放内存

zenglOX v1.0.0 shell(命令行程式及各种小工具)