最近想看計算機方面的經典教材《計算機系統概論》, 但是它上面的實例全部是在 LC3 仿真器環境下編寫的。要學習需要首先安裝 LC3 simulator 。
安裝 lc3 仿真器的方法:
1. 到官網下載 lc3 模擬器和 lc3 編譯器(網址:http://highered.mheducation.com/sites/0072467509/student_view0/lc-3_simulator_lab_manual.html)。
下載解壓縮后,我將其放在 /home/david/Public/LC3_simulator目錄下。下載下來的都是源碼文件,我們需要做的就是手動編譯下。
2. 命令行切換到 /home/david/Public/LC3_simulator/lc3tools 目錄下, 根據README描述,安裝模擬器需要gcc、flex、wish這三個組件。其實Ubuntu
自帶了 gcc 和 wish, 只需要安裝 flex 即可。那么 sudo apt install flex 安裝即可。
3. 輸入 ./configure
4. 用 gedit 打開 Makefile 文件,找到一句“OS_SIM_LIBS = -lcurses”,把其中的“-lcurses”刪掉后保存。
5. 回到終端, 輸入 make 編譯成功。 終端中輸入 ./lc3sim 即可打開模擬器。
6. 模擬器安裝完成了,下面就是編譯器了。cd ../lcc-1.3 來到編譯器的目錄。同樣是閱讀README文件,輸入 ./configure 提示權限不足,用 sudo ./configure 還是不行,這時我們需要強制使用sh來運行這個腳本。完整的命令為:sudo sh ./configure。(出現上述問題的原因是那個configure腳本沒有加上可執行的權限,所以我們需要sudo sh來強制讓它執行。解決辦法為:先輸入chmod +x configure,然后就可以正常以./configure的方式來執行了。)輸入賬戶密碼后便可以了。接下來輸入“make”和“make install”來編譯源碼並安裝。
7. 安裝結束后,二進制可執行文件會存放在該目錄下/install/的文件夾中,其中的lcc便是編譯器的主程序。它同樣是一個命令行程序,不帶參數輸入./lcc便可以看到用法說明。
8. 試着把一個 C 程序翻譯成LC-3 匯編指令:
用 gedit 編寫 C 程序:
int main() { printf("Hello, world! \n"); return 0; }
保存為 test.c 文件。
在終端切換到 install目錄下,執行:./lcc test.c, 會在同層目錄下自動生成 a.asm 文件,打開之后發現文件居然這么長。。。
以下是自動編譯成的 LC-3 匯編指令:
.Orig x3000 INIT_CODE LEA R6, #-1 ADD R5, R6, #0 ADD R6, R6, R6 ADD R6, R6, R6 ADD R6, R6, R5 ADD R6, R6, #-1 ADD R5, R5, R5 ADD R5, R6, #0 LD R4, GLOBAL_DATA_POINTER LD R7, GLOBAL_MAIN_POINTER jsrr R7 HALT GLOBAL_DATA_POINTER .FILL GLOBAL_DATA_START GLOBAL_MAIN_POINTER .FILL main ;;;;;;;;;;;;;;;;;;;;;;;;;;;;main;;;;;;;;;;;;;;;;;;;;;;;;;;;; main ADD R6, R6, #-2 STR R7, R6, #0 ADD R6, R6, #-1 STR R5, R6, #0 ADD R5, R6, #-1 ADD R6, R6, #-1 ADD R7, R4, #3 ADD R6, R6, #-1 STR R7, R6, #0 ADD R0, R4, #1 LDR R0, R0, #0 jsrr R0 LDR R7, R6, #0 ADD R6, R6, #1 ADD R7, R4, #2 ldr R7, R7, #0 lc3_L1_/home/david/Desktop/test STR R7, R5, #3 ADD R6, R5, #1 LDR R5, R6, #0 ADD R6, R6, #1 LDR R7, R6, #0 ADD R6, R6, #1 RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; void printf(const char *format, ...) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PRINTF_PERCENT .FILL -37 PRINTF_C .FILL -99 PRINTF_D .FILL -100 PRINTF_S .FILL -115 PRINTF_B .FILL -98 PRINTF_O .FILL -111 PRINTF_X .FILL -120 PRINTF_ASCII .FILL 48 ;postive ascii value of '0' .FILL 49 .FILL 50 .FILL 51 .FILL 52 .FILL 53 .FILL 54 .FILL 55 .FILL 56 .FILL 57 .FILL 65 ;A .FILL 66 .FILL 67 .FILL 68 .FILL 69 .FILL 70 PRINTF_MINUS .FILL 45 PRINTF_BUF .BLKW 18 lc3_printf ADD R6, R6, #-2 STR R7, R6, #0 ;return address ADD R6, R6, #-1 STR R5, R6, #0 ADD R5, R6, #-1 ADD R6, R6, #-1 STR R4, R6, #0 ADD R5, R5, #4 ;cheating with the bp (no longer bp) LDR R4, R5, #0 ;got addr of format string PRINTF_LOOP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LDR R0, R4, #0 ADD R0, R0, #0 ;End of string? (0x0000) BRz PRINTF_DONE ADD R2, R0, #0 LD R1, PRINTF_PERCENT ADD R2, R2, R1 BRnp PRINTF_CHAR ADD R4, R4, #1 LDR R0, R4, #0 ;is it %c? ADD R2, R0, #0 LD R3, PRINTF_C ADD R2, R2, R3 BRnp PRINTF_CHECKSTR ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%c ADD R5, R5, #1 LDR R0, R5, #0 PRINTF_CHAR OUT ADD R4, R4, #1 BRnzp PRINTF_LOOP PRINTF_CHECKSTR ;is it %s? ADD R2, R0, #0 LD R7, PRINTF_S ADD R2, R2, R7 BRnp PRINTF_CHECKDEC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%s ADD R5, R5, #1 LDR R0, R5, #0 PUTS ADD R4, R4, #1 BRnzp PRINTF_LOOP PRINTF_CHECKDEC ;is it %d? ADD R2, R0, #0 LD R7, PRINTF_D ADD R2, R2, R7 ;BRnp PRINTF_ERROR BRnp PRINTF_CHECKHEX AND R2, R2, #0 ADD R2, R2, #-10 ;going to divide by 10 by using sub loop BRnzp PRINTF_NUM PRINTF_CHECKHEX ADD R2, R0, #0 LD R7, PRINTF_X ADD R2, R2, R7 BRnp PRINTF_CHECKOCT AND R2, R2, #0 ADD R2, R2, #-16 ;going to divide by 10 by using sub loop BRnzp PRINTF_NUM PRINTF_CHECKOCT ADD R2, R0, #0 LD R7, PRINTF_O ADD R2, R2, R7 BRnp PRINTF_CHECKBIN AND R2, R2, #0 ADD R2, R2, #-8 ;going to divide by 10 by using sub loop BRnzp PRINTF_NUM PRINTF_CHECKBIN ADD R2, R0, #0 LD R7, PRINTF_B ADD R2, R2, R7 BRnp PRINTF_ERROR AND R2, R2, #0 ADD R2, R2, #-2 ;going to divide by 10 by using sub loop ;BRnzp PRINTF_NUM ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%d PRINTF_NUM LEA R7, PRINTF_BUF ADD R7, R7, #15 ADD R7, R7, #1 ;AND R2, R2, #0 ;ADD R2, R2, #-10 ;going to divide by 10 by using sub loop ADD R5, R5, #1 ;acquire the binary number LDR R0, R5, #0 ADD R0, R0, #0 BRzp PRINTF_DECPOS NOT R0, R0 ;make num positive for sub loop ADD R0, R0, #1 PRINTF_DECPOS AND R3, R3, #0 ADD R3, R3, #-1 PRINTF_DIVLOOP ADD R3, R3, #1 ;num/10 ADD R0, R0, R2 ;R0 = num % 10 - 10 BRzp PRINTF_DIVLOOP ADD R3, R3, #0 BRz PRINTF_LASTDIGIT ;LD R1, PRINTF_ASCII ;ADD R1, R1, R0 ;NOT R2, R2 ;ADD R1, R1, R2 ;ADD R1, R1, #1 ;NOT R2, R2 ;;;;;ADD R1, R1, #10 ;STR R1, R7, #0 ;ADD R7, R7, #-1 ;stored ascii value of one digit LEA R1, PRINTF_ASCII ADD R1, R1, R0 NOT R2, R2 ADD R1, R1, R2 ADD R1, R1, #1 NOT R2, R2 LDR R1, R1, #0 STR R1, R7, #0 ADD R7, R7, #-1 ;stored ascii value of one digit ADD R0, R3, #0 ;num/10 BRnzp PRINTF_DECPOS PRINTF_LASTDIGIT ;LD R1, PRINTF_ASCII ;ADD R1, R1, R0 ;ADD R1, R1, #10 ;STR R1, R7, #0 LEA R1, PRINTF_ASCII ADD R1, R1, R0 NOT R2, R2 ADD R1, R1, R2 ADD R1, R1, #1 NOT R2, R2 LDR R1, R1, #0 STR R1, R7, #0 ;stored ascii value of highest order digit LDR R0, R5, #0 ADD R0, R0, #0 BRzp PRINTF_DECSTRING LD R0, PRINTF_MINUS ;num was negative ADD R7, R7, #-1 STR R0, R7, #0 ;stored ascii value negative sign PRINTF_DECSTRING ;print the calculated string ADD R0, R7, #0 PUTS ADD R4, R4, #1 BRnzp PRINTF_LOOP PRINTF_ERROR PRINTF_DONE LDR R4, R6, #0 ;restore R4 ADD R6, R6, #1 LDR R5, R6, #0 ;restore bp ADD R6, R6, #1 LDR R7, R6, #0 ;restore ret addr ADD R6, R6, #1 RET GLOBAL_DATA_START L1_/home/david/Desktop/test .FILL lc3_L1_/home/david/Desktop/test printf .FILL lc3_printf L3_/home/david/Desktop/test .FILL #0 L2_/home/david/Desktop/test .STRINGZ "Hello, world! \n" .END