汇编代码如何在基本级别实际解释?

时间:2018-03-26 10:34:21

标签: assembly

我真的从底层开始理解编程。 所以,我已经学会了一台小型64kb计算机的内部构造,因为我对从晶体管级别理解计算机非常感兴趣。我理解晶体管,多路复用器的创建,解码器,ALU的创建等。

我得到了LC3,这是我学到的,像0001 011 011 100001等操作码将意味着0001将被解码为添加指令等。但是,我很困惑我们如何编写汇编到领导对此。我理解汇编程序翻译ADD R3,R1,R2等指令并将其转换为机器代码,但真正令我烦恼的是这些ASCII字符如何“解释”到机器代码中。

我在电子层面知道如何处理这样的指令,比如JMP更改程序计数器等,但是在最基本的层面上,汇编指令如何变成机器/二进制?我不了解它从装配到机器代码的方式。

我在网上找不到多少,但我提出的一个理论是键入的键实际上只是发送一个实际上是二进制的电信号,但仍然没有得到计算机体系结构如何将这个“ADD”变成0001因为它需要完整地理解ADD,而不仅仅是每个字符的二进制。那么,将组件转换为二进制的过程是什么,然后可以控制逻辑门,解码,签名扩展等?

编辑:对于那些询问我使用哪本书的人来说,它是计算系统简介:从比特和门到C和超越第2版(Patt) 它从构建逻辑门从P / N晶体管到组装到C.我不能为想要概述整个过程的人推荐它。

2 个答案:

答案 0 :(得分:3)

汇编程序是一个读取文本和写入二进制文件的软件程序。它不是"特别的"以任何方式。它不会以 的形式运行

CPU运行存储在RAM或ROM芯片中的机器代码。汇编程序只是生成二进制数据的便捷方法,然后您可以将其输入EEPROM或闪存编程机器(例如)以生成带有代码的芯片。或者如果在同一台计算机上运行,​​则组装到RAM或文件中。

要引导新平台,通常在另一台计算机上为其编写汇编程序,并使用它来生成包含新系统机器代码的二进制文件(或ROM /闪存芯片)。

对于微控制器,这是正常的工作流程;在桌面上开发,构建映像(组装),将其闪存到嵌入式系统上,硬件连接到桌面,然后在微控制器上启动它。用#34;玩具"像LC-3这样的电脑,过程也是一样的。您通常不会编写可以在LC-3 上运行的汇编程序。虽然你当然可以; 64kB的RAM很简单,我认为LC-3对于按位运算(与MARIE或其他一些过于简化的教学架构不同)足够强大,它不会采用荒谬的代码来执行像编码操作数这样的正常操作。位。

要编写的第一个汇编程序必须在机器代码 中编写,可能在打卡上,或者通过在机器的控制台上翻转开关来创建二进制文件码。人类可以与之交互的一些硬件,并且仅使用硬件产生所需的数字逻辑0和1。在第一个汇编程序存在之前编写了很多软件:非常早期的计算机非常罕见,以至于你没有将它们用于一次性文本处理任务;你可以手工做到!

相关:The Story of Mel是一个非常棒的真实故事,关于一个学习汇编编程的人,与一位专家老手一起工作,他在20世纪60年代在鼓机记忆计算机上直接用机器码编写程序。绝对值得一读,这也是一个有趣的道德难题。无论如何,如果没有汇编程序,可能会给你一些关于编程的想法。

相关:retrocomputing上的How was the first assembler for a new home computer platform written?。有一些答案可以帮助你解决问题,特别是这些评论描述了在没有汇编程序的情况下创建机器代码的确切过程:

  

在早期,记忆二进制指令很有用,因为许多计算机允许您使用控制面板上的物理开关来改变RAM内容。实际上,通过直接切换RAM值来进入引导加载程序是一些早期计算机上的标准引导程序。 - slebetman

  

@slebetman我记得在我之前的一个大型机操作员工作中这样做。我们使用手动输入的引导加载程序从穿孔卡加载进一步的指令,穿孔卡包含一个引导程序,允许我们从鼓硬盘加载完整的操作系统。好时光...... - Rob Moir(后来在同一个帖子中)

有关晶体管物理和汇编语言之间CPU设计层的相关内容。

答案 1 :(得分:0)

请首先参考上面的注释,您将了解汇编源文件在运行时未转换为二进制文件。 (汇编程序只是将STRING替换为某些特殊的字节序列!)< / strong>稍后我将添加一些关于我们的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)< / p>

      01110100001000010000100000000000 (74 31 08 00)
    

    可以创建。十六进制编辑器将显示74 31 08 00,但CPU将其读取为011101 00001 00001 00001 00000 000000并选择011101作为ALU的运算符,并选择寄存器00001的rs和rt分别作为ALU的操作数1和操作数2。当ALU完成计算时,寄存器文件存储rd值00001记录该值。下一个CPU将4添加到PC寄存器并重复该过程。

  10. 所以这是一个伪汇编代码。 (仅仅为了理解目的,它根本不起作用!)(故意省略标签,为简单起见跳转问题)

    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个字节),但{{1}对于EB xx xx xx xx(5个字节)等等。由于命名为 complex ,其内部结构是复杂,难以实现。它的好处是CISC CPU可以明智地使用内存并支持可在一个时钟周期内执行的大量指令。

    RISC(精简指令集计算机)

    与CISC不同,它具有固定的指令长度,如32位计算机中的指令32位。我上面解释的是CISC。可以通过位轻松解析CISC指令以指定运算符和操作数。它的好处是实现比CISC简单,即使我可以通过学习理解。它的损失是可以支持的运营商数量。

    CISC/RISC difference

    的更多资料

    很抱歉,但我只知道大约32位计算机和64位装配的新手。

    我希望我的回答很有帮助,尽管这些都来自于我在对整个角色感到好奇时所得到的简短理解,从硅到应用程序,就像你一样。