宗德才, 王康康
(1.常熟理工學(xué)院,計算機科學(xué)與工程學(xué)院, 江蘇,常熟 215500;2.江蘇科技大學(xué),數(shù)理學(xué)院, 江蘇,鎮(zhèn)江 212003)
目前,國內(nèi)很多高校計算機組成原理實驗教學(xué)內(nèi)容主要是基于TEC-XP16 教學(xué)機而開展的。為了使學(xué)生更好地掌握計算機各個組成部件的工作原理,理解計算機軟件系統(tǒng)的層次結(jié)構(gòu),系統(tǒng)地建立計算機整機概念,迫切需要開發(fā)一些設(shè)計性實驗,如在TEC-XP16教學(xué)機中設(shè)計一些新指令、利用TEC-XP16教學(xué)機的匯編指令設(shè)計高級語言解釋程序等。文獻(xiàn)[1]研究了在TEC-XP16教學(xué)機組合邏輯控制器中擴展簡單指令的方法;文獻(xiàn)[2]提出了TEC-XP16教學(xué)機微程序控制器中一種8 bit無符號數(shù)乘除指令的設(shè)計方法;文獻(xiàn)[3] 提出了一種快速設(shè)計實現(xiàn) 32 bit 浮點除法指令的方法;文獻(xiàn)[4]利用TEC-XP16教學(xué)機的匯編指令實現(xiàn)了BASIC語言解釋程序,BASIC語言解釋執(zhí)行程序?qū)崿F(xiàn)的語句如表1所示。為TEC-XP16教學(xué)機設(shè)計高級語言解釋程序,對理解計算機軟件系統(tǒng)的層次結(jié)構(gòu)很有用,特別是機器語言、匯編語言和高級語言的功能和用法上的不同之處[4]。
表1 BASIC語言解釋程序?qū)崿F(xiàn)的語句
本文利用TEC-XP16教學(xué)機的匯編指令[5]設(shè)計實現(xiàn)一種C語言解釋程序。
C語言解釋程序主要負(fù)責(zé)處理與用戶的接口,即循環(huán)處理執(zhí)行即時命令和C語言語句。
即時命令包括new、run和system,如表2所示。即時命令直接輸入后按下回車鍵就直接開始運行。
表2 即時命令
為了與即時命令相區(qū)別,C程序語句前面加行號,如果只輸入一個行號后面沒有語句,則此行號無效,如果原來存在以此行號為標(biāo)識的語句,則刪除原語句。
輸入完一個C語言程序之后,通過run命令運行C語言程序。
在C語言解釋程序啟動后,首先會初始化2000H~2600H地址范圍內(nèi)的教學(xué)機內(nèi)存和stack_While等堆棧,然后等待用戶輸入。
當(dāng)用戶輸入一條C語言語句并按下回車鍵之后,首先將輸入的每一個字符的ASCII碼用16位二進(jìn)制形式存入教學(xué)機2675H地址開始的內(nèi)存緩沖區(qū),然后檢查2675H地址開始的內(nèi)存緩沖區(qū)。如果輸入的行號或數(shù)字在0~9之間,則用16位二進(jìn)制表示,并加上數(shù)字標(biāo)志1000H;如果輸入的行號或數(shù)字大于等于10,則用32位浮點數(shù)表示,并加上標(biāo)志8000H;如果是保留字,則加上保留字標(biāo)志2000H,如表3所示。將C語言語句的保留關(guān)鍵字[6]用一個4位十六進(jìn)制數(shù)表示,如表4所示,WHILE用2000 H表示,=用201FH表示,IF用200BH表示,最后將所有的空格刪除,并將當(dāng)前的C語句復(fù)制到上一條語句之后的內(nèi)存緩沖區(qū)。
表3 數(shù)據(jù)類型與標(biāo)志
表4 C語言保留關(guān)鍵字與對應(yīng)的十六進(jìn)制數(shù)
每一條C語言語句用16位語句長度、16位行號,C語言語句內(nèi)容的格式存儲,如圖1所示。
圖1 C語言語句的存儲格式
輸入的第一條C語句最終將復(fù)制到教學(xué)機2000H地址開始的內(nèi)存緩沖區(qū),其余的C語句按照行號順序依次復(fù)制到上一條語句之后的內(nèi)存緩沖區(qū)。
輸入完一個C語言程序之后,通過run命令即可運行這個程序。
1.2.1 初始化堆棧
檢查2000H地址開始的內(nèi)存緩沖區(qū)。
(1) 取出第一條C語言語句。
(2) 檢查語句長度是否為0,若為0則結(jié)束,不為0則轉(zhuǎn)(3)。
(3) 如果語句內(nèi)容部分的第一個存儲單元內(nèi)容是2030H(main保留字),則語句種類0004H壓入堆棧Stack_ifwhl,然后轉(zhuǎn)(4);
如果語句內(nèi)容部分是自定義函數(shù)定義,則語句種類0003H壓入堆棧Stack_ifwhl,然后轉(zhuǎn)(4);
如果語句內(nèi)容部分的第一個存儲單元內(nèi)容是if保留字、while保留字、else保留字,則分別將0000H、0001H和0002H壓入堆棧stack_ifwhl,并將其所在行號壓入堆棧stack_line,然后轉(zhuǎn)(4);
如果語句內(nèi)容部分的第一個存儲單元內(nèi)容是“}”,且堆棧stack_ifwhl棧頂是0003H, 則stack_ifwhl棧頂元素出棧,然后轉(zhuǎn)(4);
如果語句內(nèi)容部分的第一個存儲單元內(nèi)容是“}”,且堆棧stack_ifwhl棧頂是0004H,則取得“}”所在行號壓入堆棧stack_emain,stack_ifwhl棧頂元素出棧,然后轉(zhuǎn)(4);
如果語句內(nèi)容部分的第一個存儲單元內(nèi)容是“}”,且堆棧stack_ifwhl棧頂是0000H、0001H、0002H,則取得“}”所在行號分別壓入堆棧stack_eif、stack_eWhl、stack_eelse,并取得stack_line棧頂行號和“}”所在行號依次存入Rel_tbl,stack_line棧頂元素出棧,stack_ifwhl棧頂元素出棧,然后轉(zhuǎn)(4)。
(4) 取出下一條C語句,然后轉(zhuǎn)(2)。
程序運行時各個保留字與堆棧stack_ifwhl、stack_line等的關(guān)系如表5所示。Rel_tbl表用于存放while語句、if語句和else語句所在的行號與其對應(yīng)的“}”所在的行號之間的對應(yīng)關(guān)系。
表5 程序運行時保留字與堆棧的關(guān)系
1.2.2 運行C語言程序
從2000H地址開始運行代碼。
(1) 取出第一條C語言語句。
(2) 檢查語句長度是否為0,若為0則結(jié)束,不為0則轉(zhuǎn)(3)。
(3) 如果語句內(nèi)容部分的第一個存儲單元內(nèi)容是“{”,則直接轉(zhuǎn)(4);
如果語句內(nèi)容部分的第一個存儲單元內(nèi)容是“}” ,且“}”的行號在堆棧stack_emain中,則結(jié)束C程序的運行;
如果語句內(nèi)容部分的第一個存儲單元內(nèi)容是“}”,且“}”的行號在堆棧stack_eWhl中,則調(diào)用call_EWHILE子程序,調(diào)用返回后轉(zhuǎn)(4);
如果語句內(nèi)容部分的第一個存儲單元內(nèi)容是“}”,且“}”的行號在堆棧stack_eelse中,則直接轉(zhuǎn)(4);
如果語句內(nèi)容部分的第一個存儲單元內(nèi)容是“}”,且“}”的行號在堆棧stack_eif中,如果if后沒有else則轉(zhuǎn)(4),如果if后有else則找到else語句的結(jié)束“}”的行號,轉(zhuǎn)(4);
如果語句內(nèi)容部分是自定義函數(shù)調(diào)用,則調(diào)用call_Func子程序,調(diào)用返回后轉(zhuǎn)(4);
如果語句內(nèi)容部分的第一個存儲單元內(nèi)容是C語言保留關(guān)鍵字,則執(zhí)行過程調(diào)用,如r0中是2000h,則執(zhí)行call_WHILE子程序;如r0中是200Bh,則執(zhí)行call_if子程序;如r0中是200Dh,則執(zhí)行call_Return子程序,調(diào)用返回后轉(zhuǎn)(4)。
(4) 取出下一條C語句,然后轉(zhuǎn)(2)。
call_While子程序的執(zhí)行過程如下:
(1) 取得當(dāng)前while語句所在的行號送r7寄存器;
(2) 計算while語句的條件表達(dá)式是否成立,若條件成立,則從堆棧stack_While棧頂取出行號送r1寄存器如果r7寄存器的值等于r1寄存器的值,則r14寄存器指向下一個語句行的長度位置;如果r7寄存器的值不等于r1寄存器的值,則將r7寄存器的值壓入堆棧stack_While并且r14指向下一個語句行的長度位置;
(3) 如果while語句的條件表達(dá)式不成立,則堆棧stack_While棧頂元素出棧,在Rel_tbl表中查找與當(dāng)前while語句對應(yīng)的“}”語句所在的行號,r14指向找到的“}”語句的下一個語句行的長度位置。
call_EWHILE子程序的執(zhí)行過程如下:
從堆棧stack_While棧頂取出行號送r1寄存器,從2000H地址開始查找行號等于r1寄存器值的語句行,r14指向該語句行的語句長度位置,如果沒有找到則報錯。
call_If子程序的執(zhí)行過程如下:
(1) 取得當(dāng)前if語句所在的行號送r7寄存器;
(2) 計算if語句的條件表達(dá)式是否成立,如果條件成立,則r14指向下一個語句行的長度位置;
(3) 如果if語句的條件表達(dá)式不成立,則在Rel_tbl表中查找與當(dāng)前if語句對應(yīng)的“}”語句所在的行號,然后判斷“}”語句后面有沒有else,如有else則執(zhí)行else語句,若沒有else則r14指向找到的“}”語句的下一個語句行的語句長度位置。
call_Func傳值調(diào)用時形式參數(shù)與調(diào)用函數(shù)實參的關(guān)系:
(1) 將調(diào)用函數(shù)的實參值從右往左先后壓入堆棧stack_funcPr;
(2) 將被調(diào)用函數(shù)形式參數(shù)名與調(diào)用函數(shù)實參值所在的內(nèi)存地址對應(yīng)關(guān)系存放到Func_Ptbl表;
(3) 令SubFun_flag=1;
(4) call_Func函數(shù)執(zhí)行過程中,在Func_Ptbl表中查找與被調(diào)用函數(shù)中形參名對應(yīng)的調(diào)用函數(shù)實參值地址。
call_FUNC傳地址調(diào)用時形式參數(shù)與調(diào)用函數(shù)實參的關(guān)系:
(1) 將調(diào)用函數(shù)的實參地址從右往左先后壓入堆棧Stack_funcPr;
(2) 將被調(diào)用函數(shù)形式參數(shù)名與調(diào)用函數(shù)實參地址所在的內(nèi)存地址之間的對應(yīng)關(guān)系存放到Func_Ptbl表;
(3) 令SubFun_flag=1;
(4) 在call_func函數(shù)執(zhí)行過程中,在Func_Ptbl表中查找與被調(diào)用函數(shù)中形式參數(shù)名對應(yīng)的調(diào)用函數(shù)實參地址所在的內(nèi)存地址。
call_Return子程序執(zhí)行后,令SubFun_flag=0,返回到函數(shù)調(diào)用語句的下一條語句處。
首先,將保存在main.asm文件中的C語言解釋程序用教學(xué)機匯編程序teca.exe匯編成MAIN.COD二進(jìn)制文件。接著,啟動TEC-XP16教學(xué)機模擬軟件,然后啟動教學(xué)機監(jiān)控程序,如圖2所示。
圖2 啟動監(jiān)控程序成功
圖2中選擇“發(fā)送文件”按鈕,把C語言解釋程序調(diào)入模擬系統(tǒng),使系統(tǒng)進(jìn)入C語言程序的運行環(huán)境。
在監(jiān)控程序中輸入g0a30啟動C語言程序的運行環(huán)境,如圖3所示。
圖3 啟動C語言程序的運行環(huán)境
為了驗證C語言解釋程序的正確性,設(shè)計了while循環(huán)語句的嵌套、while-if語句的嵌套、傳值函數(shù)調(diào)用和傳地址函數(shù)調(diào)用4個C語言程序,如圖4~圖7所示,仿真結(jié)果表明所設(shè)計的C語言解釋程序是正確的。
圖4 while循環(huán)語句嵌套
圖5 while-if語句嵌套
利用TEC-XP16教學(xué)機的匯編指令設(shè)計C語言解釋程序可以作為計算機組成原理實驗內(nèi)容和課程設(shè)計內(nèi)容,對學(xué)生理解掌握教學(xué)機的匯編指令很有幫助。為TEC-XP16教學(xué)機設(shè)計C語言運行環(huán)境能夠使學(xué)生更加深刻地理解機器語言、匯編語言和高級語言之間的聯(lián)系與區(qū)別。
之后將進(jìn)一步完善C語言運行環(huán)境,使其能支持for語句、do-while語句、break語句、continue語句、switch語句等,并使其能夠保存輸入的C程序以及打開已有的C程序文件。
圖6 傳值函數(shù)調(diào)用
圖7 傳地址函數(shù)調(diào)用