继续分析原来的代码,现在已经进入一个比较重要的函数dbginit(),因此这个函数相当复杂的功能调用。

 

/*
     * Init PMON and debug
     */
cpuinfotab[0] = &DBGREG;
dbginit(NULL);

第一行代码cpuinfotab[0]保存DBG寄存器值,主要包括32个通用寄存器和CP0的32寄存器,以及两个乘法除法这寄存器。

 

现在就来仔细地查看dbginit(NULL)的实现,代码如下:

 

/*
 * PMON2000 entrypoint. Called after initial setup.
 */
void
dbginitchar *adr)
{
int memsize, freq;
char   fs[10], *fp;
char   *s;
 
/* splhigh();*/
 
memsize = memorysize;

上面保存低端256M内存大小。

 

__init(); /* Do all constructor initialisation */

上面调用初始化C++的构造函数初始化和全局的静态变量。

 

SBD_DISPLAY ("ENVI", CHKPNT_ENVI);
envinit

上面初始化环境变量。

 

#ifdefined(SMP)
    /* Turn on caches unless opted out */
    if (!getenv("nocache"))
       md_cacheon();
#endif

上面打开缓存。

 

 

SBD_DISPLAY ("SBDD", CHKPNT_SBDD);
tgt_devinit();

上面特定的主板初始化。

 

#ifdefINET
    SBD_DISPLAY ("NETI", CHKPNT_NETI);
    init_net (1);
#endif

上面选择网络初始化。

 

#ifNCMD_HIST
    SBD_DISPLAY ("HSTI", CHKPNT_HSTI);
    histinit ();
#endif

上面初始化是否缓存过去的命令。

 

 

#ifNMOD_SYMBOLS
    SBD_DISPLAY ("SYMI", CHKPNT_SYMI);
    syminit ();
#endif

上面选择是否设置符号表初始化。

 

 

#ifdefDEMO
    SBD_DISPLAY ("DEMO", CHKPNT_DEMO);
    demoinit ();
#endif

上面选择是否初始化演示功能。

 

 

SBD_DISPLAY ("SBDE", CHKPNT_SBDE);
initial_sr |= tgt_enable (tgt_getmachtype

上面保存状态寄存器。

 

#ifdefSR_FR
    Status = initial_sr & ~SR_FR; /* don't confuse naive clients */
#endif
/* Set up initial console terminal state */
ioctl(STDIN, TCGETA, &consterm);

上面初始化终端输出。

 

 

#ifdefHAVE_LOGO
    tgt_logo();
#else
printf ("/n * PMON2000 Professional *"); 
#endif
printf ("/nConfiguration [%s,%s", TARGETNAME,
BYTE_ORDER == BIG_ENDIAN ? "EB" : "EL");

上面显示LOGO和显示字节顺序。龙芯是小端格式。

#ifdefINET
    printf (",NET");
#endif
#ifNSD
    printf (",SCSI");
#endif
#ifNWD
    printf (",IDE");
#endif
printf ("]/nVersion: %s./n", vers);
printf ("Supported loaders [%s]/n", getExecString());
printf ("Supported filesystems [%s]/n", getFSString());
printf ("This software may be redistributed under the BSD copyright./n");

上面显示提示信息。

 

tgt_machprint();

上面显示厂家信息。

 

freq = tgt_pipefreq
sprintf(fs, "%d", freq);
fp = fs + strlen(fs) - 6;
fp[3] = '/0';
fp[2] = fp[1];
fp[1] = fp[0];
fp[0] = '.';
printf (" %s MHz", fs);

上面显示总线的频率。

 

 

freq = tgt_cpufreq
sprintf(fs, "%d", freq);
fp = fs + strlen(fs) - 6;
fp[3] = '/0';
fp[2] = fp[1];
fp[1] = fp[0];
fp[0] = '.';
printf (" / Bus @ %s MHz/n", fs);

上面显示CPU频率。

 

 

printf ("Memory size %3d MB (%3d MB Low memory, %3d MB High memory) ./n", (memsize+memorysize_high)>>20,
memsize>>20), (memorysize_high>>20));
 
tgt_memprint();

上面显示内存的大小。

 

 

#ifdefined(SMP)
    tgt_smpstartup();
#endif

上面选择多CPU的初始化。

 

 

printf ("/n");
 
md_clreg(NULL);
md_setpc(NULL, (int32_t) CLIENTPC);
md_setsp(NULL, tgt_clienttos

上面清空保存寄存器的变量,并保存新的PC值和堆栈值。

 

#ifdefAUTOLOAD
    s = getenv ("al");
    autoload (s);
#else
s = getenv ("autoboot");
autorun (s);
#endif

上面设置是否自动加载操作系统文件。

 

}

 

这个函数做了很多工作,下面要仔细地分析每个子函数实现的功能,才能理解后面所做的工作,否则也不知道后面会做些什么。又进入下一层函数分析它的实现。

 

首先来了解怎么样初始化C++的构函数和全局静态变量。仔细地看一下函数__init(),如下:

 

void
__init()
{
static int initialized
 
/*
     * Call global constructors.
     * Arrange to call global destructors at exit.
     */
if (!initialized) {
initialized
__ctors();
    }
}

在这个函数里,先判断是否已经初始化全局函数和构造函数,如果没有初始化,就设置为已经初始化,接着调用函数初始化全局__ctors()函数。也许你会问,这些全局函数从那里来的呢?如果你去看看GCC的连接说明文件,就会发现有这个段在那里,因此这些全局函数是由编译器生成的,并不是由用户定义。如果不调用这些全局初始化函数,很多全局变量是没有定义的值,本来你编程时初始化为100的值,可能只是0或者任意的数值。现在又立即去看函数__ctors()的实现,如下:

staticvoid
__ctors()
{
void (**p)(void) = __CTOR_LIST__
 
while (*p)
p++)();
}

 

由于GCC编译器会根据连接文件生成全局调用函数,下面就是连接文件相关内容,如下:

.ctors         :
 {
                __CTOR_LIST__ = .;
                LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)
               *(.ctors)
                LONG(0)
                __CTOR_END__ = .;
 }
 .dtors         :
 {
                __DTOR_LIST__ = .;
                LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)
               *(.dtors)
                LONG(0)
                __DTOR_END__ = .;
 }

编译器就会根据上面的连接脚本生成全局初始化函数和全局析构函数,并且每个函数入口是按照4字节的指针排列的。因此,在函数__ctors()里循环地调用所有函数运行一遍。后面析构函数相应也调用函数__dtors()来实现的。