首页
社区
课程
招聘
[原创]浅谈TC、BC和汇编混合编程连接技术及参数传递方法
发表于: 2006-7-7 13:14 20356

[原创]浅谈TC、BC和汇编混合编程连接技术及参数传递方法

2006-7-7 13:14
20356
浅谈TC、BC和汇编混合编程连接技术及参数传递方法

作者:CCDebuger

摘要:本文主要讨论如何应用混合编程技术,让汇编语言与高级语言相结合,以简化编程工作。

关键词:Turbo C++;Borland C++;Turbo Assembler;混合编程

一、引言

在平常编写程序时,我们一般都希望选择C、Pascal、Basic等这样的高级语言来编写,这样的话程序开发周期比较短,省时省力。但在很多时候为了提高程序的执行效率或其它原因,我们却不得不用汇编程序来编程。如果只用汇编语言编程的话是比较繁琐的,对于一个比较大的程序来说编写起来将非常耗时,开发周期比较长。为了既能缩短程序开发周期,又能保证程序的执行效率,我们通常会考虑用高级语言编写主程序,而把程序中与执行效率关系密切的关键部分用汇编语言来编写。这里就涉及到了混合编程的问题。本文选择Turbo C++和Borland C++这种以C语言为基础的程序开发工具与Turbo Assembler这样的汇编开发工具为例,来讨论一下高级语言与汇编语言混合编程的相关问题。

TC与汇编混合编程

        将C语言和汇编语言混合使用的传统方式是,先完全用C语言或汇编语言写出单独的模块,编译C语言模块并汇编汇编语言模块,然后将分别编译过的模块连接到一起。按这种方式Turbo C++模块和Turbo Assembler模块可以很方便地进行连接。
        用户可以采用以下命令,使C源文件与汇编源文件混合产生可执行文件:
tcc filenam1 filenam2.asm
该命令指示Turbo C++首先将FILENAM1.C编译成FILENAM1.OBJ,再激活Turbo Assembler将FILENAM2.ASM汇编成FILENAM2.OBJ,最后激活TLINK连接FILENAME1.OBJ与FILENAM2.OBJ并产生FILENAM1.EXE。
        这种分别编译的方法对于可确定大小的汇编代码程序极为有用,因为它可以让用户使用Turbo Assembler的全部功能,在纯汇编语言环境中进行汇编语言程序设计,不需要asm关键字、额外的编译时间以及嵌入式汇编中与C相关的辅助开销。
        Turbo C++与Turbo Assembler的接口中,有两个重要的问题值得注意。首先,C代码与汇编码的各个不同部分必须恰当地连接在一起。其次,汇编码必须能恰当地处理C风格的函数调用,包括访问传递过来的参数、返回值以及遵从C函数所要求的寄存器保存规则。为了将Turbo C++与Turbo Assembler模块连接在一起,必须完成以下工作:
(1) Turbo C++与Turbo Assembler模块必须以Turbo C++可以接受的方式共享适当的函数和变量名。
(2) Turbo Assembler模块必须使用与Turbo C++兼容的段命名方案。
必须用TLINK将这些模块连接成可执行程序。此时,我们关心的是如何创建一种模式,以便可以按此模式编写与C兼容的汇编语言函数。为此,我们需要来了解一下以下内容:

1、内存模式和段

        对于一个给定的可以由C调用的汇编语言函数,该函数必须采用与C语言一致的内存模式(tiny、small、compact、medium、large或huge),同时必须使用与C兼容的代码段。同样,为了让C代码能够访问在汇编语言模块中定义的数据,或让汇编代码能够访问C模块中定义的数据,汇编语言代码务必遵从C语言的数据段命名约定。
        通常,汇编语言模块由三个基本段(代码段、初始化数据段和非初始化数据段)组成。每组信息被安排在各自的段中,段名取决与C程序所使用的内存模式。Turbo assembler(TASM)提供了三种简化的段伪指令(.CODE,.DATA,.DATA?)来定义这些段。其中,.CODE段标志着程序代码段的开始,告知Turbo Assembler用户指令究竟放在哪个代码段中。.DATA标志数据段的开始,用户应将内存变量放在数据段中,此段中包含的是已初始化的数据。.DATA? 的使用与.DATA一样,但.DATA? 数据段中包含的是未初始化的数据。这些简化的段伪指令产生的段与Turbo C++兼容。
        伪指令.MODEL告诉Turbo Assembler,用简化的段伪指令创建的段与选定的内存模式兼容,并控制用PROC伪指令创建的过程的隐含类型。由.MODEL伪指令定义的内存模式与具有同样命名的Turbo C++模式是兼容的。若C程序选择了小内存模式,下面列出了采用简化的段伪指令组织汇编模块的一种方式:
.MODEL 	SMALL
.CODE
…代码段…
.DATA
…初始化数据段…
.DATA?
…非初始化数据段…

2、汇编语言与Turbo C++的交互性

        此处着重讨论参数传递、返回值及寄存器使用约定。
        假设我们编写一个求最小数的函数min,其C语言的函数原型为:
        extern “C” int min(int V1,int V2);
此函数返回两个参数中的最小值。在汇编语言中其整体格式如下:
	PUBLIC C min
	min		PROC	C	NEAR
			…
	min		ENDP

这里假定min为near函数,若为far函数,可将上面格式中的near换成far。

(1)传递参数

首先考虑使用哪一种参数传递方法。除非有适当的理由,否则应该使用C参数传
        递方法而不用Pascal参数传递方法。对于16位的程序,当min被调用时,栈的内容如下:
SP+4:                V2
SP+2:                V1
SP:                        返回地址
        如想不退栈就取得参数,则需保存基指针(BP),将栈顶指针(SP)送BP,然后用BP作为下标直接到栈中取得参数值。此时需注意:若把BP压入栈内,则参数的偏移量将加2,因为现在栈内又增加了两个字节。
       
(2)返回值

        正如C函数一样,可由C调用的汇编语言函数也可以返回值。函数值的返回形式在通常情况下,16位值返回到AX中;32位值返回到DX:AX中,其中DX存放高16位值,而AX存放低16位值;浮点值返回到8087/80287栈顶(TOS)寄存器,即ST(0)中;如果使用8087/80287仿真器,则返回值存放在仿真器的TOS寄存器中。结构的返回要稍复杂一些。1个字节长的结构返回到AL中;2个字节长的结构返回到AX中;4个字节长的结构返回到DX:AX中;3字节的结构或大于5字节的结构则保存在静态数据区中,然后返回指向此静态数据的指针(小数据模式置于AX中,大数据模式只有DX:AX中)。调用子程序时必须把返回值拷贝到这个指针所指的单元中。
                例如,min函数返回的时16位数值,所以其返回值在AX中。下面是min的汇编代码:
PUBLIC	C min
min	PROC	C NEAR
	
	PUSH BP		;保存BP
	MOV BP,SP	;将SP拷贝到BP中
	MOV AX,[BP+4]	;将V1送到AX中
	CMP AX,[BP+6]	;与V2比较
	
	JLE EXIT	;如果V1>V2
	MOV AX,[BP+6]	;将V2送AX中
EXIT:	POP BP		;恢复BP
	RET		;返回C

min 	ENDP

如果min是far函数,则主要在栈的入口处发生了变化,现在栈内的情况是:
                SP+6:        V2
                SP+4:        V1
                SP+2:        返回段地址
                SP:                返回偏移量
                这就意味着栈的偏移量已增加了两个字节,因为两个额外字节(返回段地址)被压进栈内。在min函数的far版本中,V1将在[BP+6]中,V2将在[BP+8]中。这是因为返回段地址被压入栈内,V1、V2的偏移量增加了两个字节。
如果使用Pascal参数传递顺序,则栈内情况变化如下(假设min是near函数):
                SP+4:        V1
                SP+2:        V2
                SP:                返回地址
此时,标识符min必须遵从Pascal类型的约定:使用大写字母和不加前缀下划线的标识符。
除了V1和V2交换了位置外,此方法还需要min在返回时用ret指令清栈。在本例中,必须弹出V1和V2的四个字节(返回地址由ret只能自动弹出)。
下面是采用Pascal参数传递方法的min汇编代码:
	PUBLIC	PASCAL min
min	PROC	PASCAL NEAR
	
	PUSH BP		;保存BP
	MOV BP,SP	;将SP拷贝到BP中
	MOV AX,[BP+6]	;将V1送到AX中
	CMP AX,[BP+4]	;与V2比较
	
	JLE EXIT	;如果V1>V2
	MOV AX,[BP+6]	;将V2送AX中
EXIT:	POP BP		;恢复BP
	RET	4	;清栈并返回

min 	ENDP


(3)寄存器的使用约定

                在min函数中,使用了多个寄存器。现在讨论在Turbo C++程序中将使用哪些寄存器,以及使用中的一些约定。
                在所有寄存器中,BP应首先引起注意,因为一旦进入汇编程序,BP的内容在子程序入口处被压入栈内,并且栈指针(SP)的当前值必须放在BP中,在子程序出口处BP被弹出,恢复原来的值。
                其他需注意的是SI和DI寄存器,这两个寄存器被Turbo C++用作寄存器变量。如果在一个汇编语言过程中使用它们,则应在进入时保存它们(可能在栈中),在退出时恢复它们。
               
3、Turbo C++中调用汇编模块的方法

        一般情况下,Turbo C++希望所有的外部标号均以下划线“_”开头。如果汇编模块中定义的函数及变量准备供Turbo C++调用,应以下划线开头。若不想使用下划线的话,可在PUBLIC后加C标明调用的语言类型。如若有函数XXX,要定义为供Turbo C++调用的函数,可这样写为PUBLIC C XXX 而不需写为通常的PUBLIC _XXX。汇编程序对字母的大小写并不敏感,所以不区别对待大写字母和小写字母,而皆以大写字母对待。因为C语言区别对待大小写字母,所以在编写准备与C模块相连接的汇编模块时,应该注意符号名的大小写,以便保持一致。而且,要通知汇编程序对大小写区别对待,至少对于C模块和汇编模块所共享的那些符号而言应该如此。汇编程序的命令行可选项 /ml 和 /mx 可以做到这一点。汇编程序的命令行可选项 /ml 使得汇编只对公共标识符和外部标识符等按大小写区别对待。按本机配置编写一个Turbo C++和汇编混合编程的程序的步骤如下:
       
(1) 因为在本机上Turbo C++ 3.0(以下简称TC)和Turbo Assembler 5.0(以下简称TASM)都安装在E盘,其可执行文件分别位于E:\TC\BIN及E:\TASM\BIN目录下。于是编辑环境变量中的系统变量内的path值,添加以下内容:E:\TC\BIN; E:\TASM\BIN 。这样做的目的是为了可以在TC的集成开发环境中直接调用TASM来编译汇编文件,而不需转到TASM目录在命令行下编译汇编文件。

(2) 启动TC集成开发环境新建一个工程,命名为ASMTEST.PRJ。添加ASMTEST.CPP及ADD.ASM两个文件,源码分别如下(已在TC下调试通过):
//	程序名:ASMTEST.CPP
#include "stdio.h"
extern "C"			//声明在外部模块中定义的函数
{
int addnum( int , int );
}

main()
{
int x,y;		//定义变量
printf("Please input x,y,the space key to compart:=>");
scanf("%d %d",&x,&y);
printf("x+y=%d\n",addnum(x,y));

return (0);
}

;程序名:ADD.ASM
	DOSSEG
	.MODEL small						;声明内存模式
	
	;.DATA								;数据段未定义,此处为空
	
	.CODE								;代码段
	PUBLIC C addnum						;定义函数
addnum	PROC C NEAR x:WORD,y:WORD		;定义变量
	
	mov ax,x
	mov bx,y
	add ax,bx
	ret

addnum ENDP
	END

以上的ASMTEST.CPP程序调用ADD.ASM中的addnum函数,来实现两个数相加。完成代码输入后,编译后即可运行。另一种在汇编程序中定义以下划线“_”开头的函数的方法可参考附件中TOTAL工程内的文件。

二、 Borland C++与汇编的混合编程
        在本机上Borland C++(简称为BC)版本为5.02,安装在F:\BC5目录下,可执行文件位于F:\BC5\BIN目录。在此讨论在BC开发的Windows 程序中调用汇编程序。
        BC调用汇编模块与TC调用汇编模块的方法基本相同,只有几个需注意的地方:
        因为和 16 位 Windows 下的把代码分成 DATA,CODE 等段的内存模式不同,WIN32 只有一种内存模式,即 FLAT 模式,意思是"平坦"的内存模式,再没有 64K 的段大小限制,所有的 WIN32 的应用程序运行在一个连续、平坦、巨大的 4GB 的空间中。这同时也意味着您无须和段寄存器打交道,您可以用任意的段寄存器寻址任意的地址空间,这对于程序员来说是非常方便的。在Win32下编程,有许多重要的规则需要遵守。有一条很重要的是:Windows 在内部频繁使用 ESI,EDI,EBP,EBX 寄存器,而且并不去检测这些寄存器的值是否被更改,这样当您要使用这些寄存器时必须先保存它们的值,待用完后再恢复它们。
        在WIN32指定内存模式参数调用规则时可这样定义:
        .MODEL FLAT,STDCALL
        此处的STDCALL 告诉编译器参数的传递约定。前面我们已经谈到了调用约定的问题,这里我们再来仔细讨论一下:在Win16下有两种约定:C 和 PASCAL。C 约定规定参数传递顺序是从右到左,即最右边的参数最先压栈,由调用者恢复堆栈指针。PASCAL约定和C约定正好相反,它规定参数是从左向右传递,由被调用者恢复堆栈。Win16采用了PASCAL约定, 因为PASCAL约定产生的代码量要小。STDCALL是C约定和PASCAL约定的混合体,它规定参数的传递是从右到左,恢复堆栈的工作交由被调用者。
        在BC所建立的工程中的汇编模块,调用C模块中的外部变量时可不在变量前加下划线“_”。由此,可建立一个调用汇编模块的简单示例程序如下(已在BC下调试通过):
在BC中新建一个AsmInc工程,此处使用SDK方法,即不使用OWL,只使用WINAPI。设置为静态连接,添加以下几个文件:
//程序名:AsmInc.cpp
//////////////////////////////////////////////////////////////////////////////////////////////////
//预处理

#include "windows.h"
#include "stdio.h"
#include "string.h"

/////////////////////////////////////////////////////////////////////////////////////////////////
//全局变量

extern "C"
{
	void Gen( void );            //定义外部模块
	char username[255] = "";		//定义用户名和密码
	char serial[255] = "";
}
HINSTANCE hInst;	//应用程序进程句柄,一般程序中经常用到此变量,故使用全局变量
HWND hwnd;		//主窗口句柄,一般程序中经常用到此变量,故使用全局变量

/////////////////////////////////////////////////////////////////////////////////////////////////
//函数声明

LRESULT CALLBACK KeyGen(HWND, UINT, WPARAM, LPARAM);

///////////////////////////////////////////////////////////////////////////////////////////////// 
//主函数

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	DialogBox(hInst,MAKEINTRESOURCE( 1 ),NULL,(DLGPROC) KeyGen);
	return (FALSE);
}


///////////////////////////////////////////////////////////////////////////////////////////////// 
//对话框主函数

LRESULT CALLBACK KeyGen(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	char *sn;						//用于临时存放生成的密码
	
	switch(message)
	{
	case WM_COMMAND:
		switch(LOWORD (wParam))
		{
		case WM_INITDIALOG:
			LoadIcon( hInst , MAKEINTRESOURCE(1));	//设置程序图标
			break;
			
		case 105:						//处理输入的用户名
			GetDlgItemText( hDlg, 103, username, 255);
			Gen();                                           //调用外部模块
			sn = serial;
			SetDlgItemText( hDlg , 104 , sn);			//把计算后的密码输出
			break;
			
		case 106:
			{
				MessageBox( NULL, TEXT("********混合编程示例********\n\n*******By cao_cong********\n******2005年2月5日*****"),
					TEXT("关于"),MB_OK);
				return 0;
			}
			break;
			
		case 107:
			EndDialog ( hDlg , 107);
			break;
		}
		
		default : return (FALSE);
	}
	return (TRUE);
}

;程序名:Gen.asm
.386
.MODEL  FLAT STDCALL     ;定义使用的内存模式及参数调用方式

PUBLIC  Gen              ;声明函数

.DATA
EXTERN	username         ;声明在其他模块中定义的变量
EXTERN	serial

.CODE
Gen	PROC C           ;主函数
	
	PUSH EBX         ;保存EBX
	XOR EAX,EAX
	XOR EBX,EBX
	XOR ECX,ECX
@LOOP:	MOV EBX,[username+ECX]   ;获得用户输入的姓名
	MOV serial+ECX,EBX       ;把姓名送到密码中
	INC ECX
	CMP BYTE PTR ES:[username+ECX],0   ;检测是否已送完
	JNZ @LOOP
	
	POP EBX           ;恢复EBX
	ret

Gen endp
END

//资源文件:AsmInc.rc(摘录)
#define IDI_ICON	1
#define IDD_KEYGEN	1
#define IDC_STATICTEXT1	101
#define IDC_STATICTEXT2	102
#define IDC_NAME	103
#define IDC_SN	104
#define IDEXIT	107
#define IDABOUT	106
#define IDGEN	105

IDD_KEYGEN DIALOG 0, 0, 208, 85
STYLE DS_MODALFRAME | DS_3DLOOK | DS_CENTER | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "混合编程示例"
FONT 9, "宋体"
LANGUAGE LANG_CHINESE , SUBLANG_CHINESE_SIMPLIFIED
{
 CONTROL "姓名:", IDC_STATICTEXT1, "static", SS_CENTER | WS_CHILD | WS_VISIBLE, 8, 16, 36, 12
 CONTROL "密码:", IDC_STATICTEXT2, "static", SS_CENTER | WS_CHILD | WS_VISIBLE, 8, 40, 36, 12
 CONTROL "", IDC_NAME, "edit", ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 44, 12, 156, 14
 CONTROL "", IDC_SN, "edit", ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 44, 36, 156, 17
 CONTROL "生成密码", IDGEN, "button", BS_PUSHBUTTON | BS_CENTER | BS_FLAT | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 24, 60, 44, 17
 CONTROL "关    于", IDABOUT, "button", BS_PUSHBUTTON | BS_CENTER | BS_FLAT | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 90, 60, 44, 17
 CONTROL "退出", IDEXIT, "button", BS_PUSHBUTTON | BS_CENTER | BS_FLAT | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 156, 60, 44, 17
}

        以上程序功能主要是把你输入的姓名在密码框中输出,主要用于说明调用汇编模块的方法。

参考文献:
1、林亨利、陈维兴、成渝 编译:面向对象的程序设计系统 TURBO C++应用教程(中),清华大学出版社,1992
2、杨季文 等编著:80X86 汇编语言程序设计教程,清华大学出版社,1998
3、求实 编著:最新 Borland C++ 实用教程 1:Borland C++ 入门,科学出版社,1994
4、王松 张良治 编:Borland C++ 实用技术入门精解(3.1~4.0),电子科技大学出版社,1995

【版权声明】 本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢!

附件是示例源码:

[课程]Android-CTF解题方法汇总!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (23)
雪    币: 2506
活跃值: (1000)
能力值: (RANK:990 )
在线值:
发帖
回帖
粉丝
2
这是一篇以前写的文章,可能还有点参考价值,就发上来了。本来还打算再添加一下VC和汇编的混合编程的,想想还是算了。因为文章是以前写的,我发布的时候只是删了一些无关的部分,文章的具体内容基本上我都没看,如果有什么错误,还望大家指正。
2006-7-7 13:19
0
雪    币: 2384
活跃值: (766)
能力值: (RANK:410 )
在线值:
发帖
回帖
粉丝
3
CC兄写的文章很规范。
2006-7-7 13:25
0
雪    币: 44229
活跃值: (19960)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
4
最初由 CCDebuger 发布
这是一篇以前写的文章


期待CC更多的以前和以后文章。;)
2006-7-7 13:26
0
雪    币: 2506
活跃值: (1000)
能力值: (RANK:990 )
在线值:
发帖
回帖
粉丝
5
呵呵,你们两个手够快啊
我很少写文章的,这篇文章也是四处瞎翻才找到的
2006-7-7 13:29
0
雪    币: 370
活跃值: (15)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
6
CCDebuger可以出书了
2006-7-7 13:33
0
雪    币: 603
活跃值: (617)
能力值: ( LV12,RANK:660 )
在线值:
发帖
回帖
粉丝
7
全才,全才啊
2006-7-7 14:03
0
雪    币: 603
活跃值: (617)
能力值: ( LV12,RANK:660 )
在线值:
发帖
回帖
粉丝
8
记得我刚上大学的时候,在火车上遇见一个穿军装的GG, 语重心长地对我说, 其实写程序很简单, ASM也很简单... 不会就是你吧?
2006-7-7 14:37
0
雪    币: 2506
活跃值: (1000)
能力值: (RANK:990 )
在线值:
发帖
回帖
粉丝
9
保密守则告诉我们,不该说的不要说
2006-7-7 15:16
0
雪    币: 450
活跃值: (552)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
10
签保密协议先
2006-7-7 15:40
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
11
瞻仰CC
2006-7-7 21:04
0
雪    币: 2506
活跃值: (1000)
能力值: (RANK:990 )
在线值:
发帖
回帖
粉丝
12
最初由 北极星2003 发布
瞻仰CC

2006-7-7 21:19
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
终于可以看懂一部分了
2006-7-7 21:29
0
雪    币: 93944
活跃值: (200219)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
14
学习
2006-7-7 21:32
0
雪    币: 159
活跃值: (339)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
15
最初由 北极星2003 发布
瞻仰CC

这个词和缅怀是一个意思吧
2006-7-8 19:01
0
雪    币: 603
活跃值: (617)
能力值: ( LV12,RANK:660 )
在线值:
发帖
回帖
粉丝
16
楼上的好狠~
2006-7-8 22:21
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
17
最初由 Lenus 发布
这个词和缅怀是一个意思吧


想象力真丰富啊
2006-7-8 23:29
0
雪    币: 2506
活跃值: (1000)
能力值: (RANK:990 )
在线值:
发帖
回帖
粉丝
18
最初由 Lenus 发布
这个词和缅怀是一个意思吧

把小马拉出去打PP
2006-7-8 23:50
0
雪    币: 2559
活跃值: (176)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
19
汇编有这么学习的
2006-7-10 18:15
0
雪    币: 239
活跃值: (473)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
20
可否说一下用C++ builder建立SDK程序?
怎么编译的? 写个编译的批处理吧,呵呵
2006-7-13 09:52
0
雪    币: 207
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
请问delphi 和 vc 编写的模块怎么组合在一起, 如何进行混编?
2006-7-13 20:24
0
雪    币: 239
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
谢谢

最好能写点 汇编 调用 TC 的例子。
2006-7-22 10:19
0
雪    币: 750
活跃值: (228)
能力值: ( LV9,RANK:780 )
在线值:
发帖
回帖
粉丝
23
虽然现在还用不着,不过帮顶了
2006-7-23 16:51
0
雪    币: 200
活跃值: (78)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
最初由 北极星2003 发布
瞻仰CC


2006-7-24 09:27
0
游客
登录 | 注册 方可回帖
返回
//