簡體   English   中英

匯編代碼如何在基本級別實際解釋?

[英]How does assembly code actually get interpreted at the basic level?

我真的從底層開始理解編程。 所以,我已經學會了一台小型64kb計算機的內部構造,因為我對從晶體管級別理解計算機非常感興趣。 我理解晶體管,多路復用器的創建,解碼器,ALU的創建等。

我得到了LC3,這是我學到的,像0001 011 011 100001等操作碼將意味着0001將被解碼為添加指令等。但是,我很困惑我們如何編寫匯編來導致這一點。 我理解匯編程序翻譯ADD R3,R1,R2等指令並將其轉換為機器代碼,但真正令我煩惱的是這些ASCII字符如何“解釋”到機器代碼中。

我知道在電子級別如何處理這樣的指令,比如JMP來改變程序計數器等,但是在最基本的層面上,匯編指令如何變成機器/二進制? 我不了解它從裝配到機器代碼的方式。

我在網上找不到多少,但我提出的一個理論是鍵入的鍵實際上只是發送一個實際上是二進制的電信號,但仍然沒有得到計算機體系結構如何將這個“ADD”變成0001,因為它需要完整地理解ADD,而不僅僅是每個字符的二進制。 那么,將組件轉換為二進制的過程是什么,然后可以控制邏輯門,解碼,簽名擴展等?

編輯:對於那些詢問我使用哪本書的人來說,它是計算系統簡介:從比特和門到C和超越第二版(Patt)它從構建邏輯門從P / N晶體管到組裝到C.我不推薦它更希望了解整個過程的人。

匯編程序是一個讀取文本和寫入二進制文件的軟件程序。 它在任何方面都不是“特殊的”。 它不會您鍵入或運行任何內容時運行。

CPU運行存儲在RAM或ROM芯片中的機器代碼。 匯編器只是生成二進制數據的便捷方式 ,然后您可以將其輸入EEPROM或閃存編程機器(例如)以制作帶有代碼的芯片。 或者如果在同一台計算機上運行,​​則組裝到RAM或文件中。

要引導新平台,通常在另一台計算機上為其編寫匯編程序,並使用它來生成包含新系統機器代碼的二進制文件(或ROM /閃存芯片)。

對於微控制器,這是正常的工作流程; 在桌面上開發,構建映像(組裝),將其閃存到嵌入式系統上,硬件連接到桌面,然后在微控制器上啟動它。 使用像LC-3這樣的“玩具”計算機,過程將是相同的。 您通常不會費心編寫可以在LC-3上運行的匯編程序。 雖然你當然可以; 64kB的RAM很簡單,我認為LC-3對於按位運算(與MARIE或其他一些過於簡化的教學架構不同)足夠強大,它不會花費大量的代碼來執行將操作數編碼為位的正常操作。

第一個要編寫的匯編程序必須用機器代碼編寫 ,可能是打卡,或者通過翻轉機器控制台上的開關來創建二進制代碼。 人類可以與之交互的一些硬件,並且僅使用硬件產生所需的數字邏輯0和1。 在第一個匯編程序存在之前編寫了很多軟件:非常早期的計算機是如此罕見,以至於你沒有將它們用於一次性文本處理任務; 你可以手工做到!

相關: 梅爾的故事是一個非常好的真實故事,關於一個學習匯編的人,與一位專家老手一起工作,他在20世紀60年代在鼓機記憶計算機上直接用機器碼編寫程序。 絕對值得一讀,也有一個有趣的道德難題。 無論如何,可能會給你一些關於沒有匯編程序的編程的想法。


相關: 如何編寫新的家庭計算機平台的第一個匯編程序? 在retrocomputing.SE有一些答案可以幫助你解決問題,特別是這些評論描述了在沒有匯編程序的情況下創建機器代碼的確切過程:

在早期,記憶二進制指令很有用,因為許多計算機允許您使用控制面板上的物理開關來改變RAM內容。 實際上,通過直接切換RAM值來進入引導加載程序是一些早期計算機上的標准引導程序。 - slebetman

@slebetman我記得在我之前的一個大型機操作員工作中這樣做。 我們使用手動輸入的引導加載程序從穿孔卡加載進一步的指令,穿孔卡包含一個引導程序,允許我們從鼓硬盤加載完整的操作系統。 好時光...... - Rob Moir(后來在同一個主題中)


有關晶體管物理和匯編語言之間CPU設計層的相關內容。

請首先參考上面的注釋,您將了解程序集源文件在運行時未轉換為二進制文件。 (匯編程序只是將STRING替換為一些特殊的字節序列!)稍后我將添加一些關於我們的PC如何執行本機字節代碼的解釋。

  1. 我們按下電源按鈕。 電容器相關電路對CPU的復位引腳進行放電/充電。

  2. CPU重置自己。 它將程序計數器分配給BIOS的啟動程序。

  3. BIOS執行

    BIOS做了一些必要的事情來操作我們的電腦。

  4. BIOS將引導加載程序加載到內存並調用它。

    BIOS從Boot記錄中讀取一些字節,並檢查其第512個字節是否為0x55 0xAA ,二進制為0x01010101 10101010b ,以檢查該扇區是否為引導扇區。 如果它是正確的,BIOS將內容加載到地址0xC200並跳轉到0xC200。

  5. 引導加載程序執行

    它初始化PIC,視頻卡等外圍設備。它設置A20門,告訴我們要使用超過1MB的內存。 此外,由於BIOS的大小限制,它幾乎加載了幾乎所有無法加載的內核模塊。 它還將CPU模式更改為32位或64位等。

  6. 操作系統啟動自身。

    它初始化IDT,GDT,定時器,數據結構以使其運行,並將文件系統加載/解析到內存。

  7. 現在你看到“歡迎”的消息。

  8. 現在,您創建一個名為test.asm的文件。

     C: XOR EAX, EAX NOP JMP C 

    你的test.asm看起來像二進制(hex)

      43 3a 0d 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 4d 4f 56 20 30 2c 20 52 41 58 0d 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 4e 4f 50 0d 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 4a 4d 50 20 43 
  9. 你用匯編程序組裝它。

    (我手動組裝了這個,所以不要相信我的字節碼...)

    匯編器輸出可以是:例如

     31 C0 90 EB FC 

    關鍵是你的源文件字節和組裝的二進制文件是完全不同的。 (匯編程序只是將STRING替換為一些特殊的字節序列!)

    10.以及CPU如何解釋字節:(例如精簡指令集計算機,32位......例如舊MIPS)

    簡而言之,ALU只是一個計算器,機器語言是告訴計算器操作員和操作數的數據。 CPU將通過參考PC寄存器獲得的指令字節分成比特並解釋它們。 指令的這些位的位0到5,0到6等告訴計算器要做什么(例如,添加ADD(例如001001))。 從第6位或第7位開始,它可用於指定此操作所需的操作數。 您可以指定寄存器ID,內存地址和常量。 舉一個簡單的例子,假設ADD的指令ID為01101,寄存器AX的id為00001,該CPU的指令采用以下32位結構:

      op rs rt rd shamt funct 0-5 6-10 11-15 16-20 21-25 26-31 Op is operator id, rs and operand 1,2 respectively , and rd destination of operation. Shamt and funct is used for special purpose . 

    組裝ADD AX AX AX的匯編指令時,匯編程序使用從此行獲取的信息(op = 011101,rs = 00001,rt = 00001,rd = 00001,shamt = 00000,funct = 0000000)

      01110100001000010000100000000000 (74 31 08 00) 

    可以創建。 十六進制編輯器將顯示74 31 08 00 ,但CPU讀取為011101 00001 00001 00001 00000 000000並選擇011101作為ALU的運算符,並選擇寄存器堆中寄存器00001 rs和rt作為操作數1和操作數2分別是ALU。 當ALU完成計算時,寄存器文件存儲rd值00001記錄該值。 下一個CPU將4添加到PC寄存器並重復該過程。

所以這是一個偽匯編代碼。 (僅僅為了理解目的,它根本不起作用!)(故意省略標簽,簡單的跳轉問題)

for(String line: filecontent)
{
    Assemble(line);
}
void Assemble(String line)
{
    String[] parsed=line.split by_comma_or_space();
    String operator=parsed[0] ;
    String operand1=parsed[1];
    String operand2=parsed[2];
    String operand3=parsed[3];
    unsigned int opcode=opcodemap.get(operator);
    unsigned int operand1id=getOperandId(operand1);
    unsigned int operand2id=getOperandId(operand2);
    unsigned int operand3id=getOperandId(operand3);
    unsigned int totalcode=opcode<<32;
    totalcode|=operand1<<26;
    totalcode|=operand2<<21;
    totalcode|=operand3<<16;
    WritetoFile(totalcode);
}

補充讀數

RISC / CISC

CPU可以按字節長度的指令進行分類。 CISC指令可以具有用於單個指令的可變長度的字節序列。 例如, RETC3NOP90CCINT 3 (每條指令1字節),但EB xx xx xx xxJMP xxxxxxxx (5字節),依此類推。 作為命名復雜 ,其內部結構復雜且難以實現。 它的好處是CISC CPU可以明智地使用內存並支持可在一個時鍾周期內執行的大量指令。

RISC(精簡指令集計算機)

與CISC不同,它具有固定的指令長度,例如32位計算機中的指令。 我上面解釋的是CISC。 可以通過位輕松解析CISC指令以指定運算符和操作數。 它的好處是實現比CISC簡單,即使我可以通過學習理解。 它的損失是可以支持的運營商數量。

更多有關CISC / RISC差異的材料

對不起,但我只知道大約32位計算機和64位裝配的新手。

我希望我的回答是有幫助的,盡管這些都來自於我在對整個方面感到好奇時的簡短理解,從硅到應用程序,就像你一樣。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM