ikkhksvu 发表于 2024-10-3 08:34:10

【译】超硬核|在自制的 CPU 上运行 Rust


    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">看到一篇有趣且硬核的<span style="color: black;">文案</span>,边翻译边学习。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">作者自制了一个 CPU ,<span style="color: black;">而后</span>用 Rust 实现了软件部分,<span style="color: black;">包含</span><span style="color: black;">有些</span>简单的程序:绘图器、BASIC/Scheme 语言解释器、Web 服务器、终端模拟器和MIDI 音乐播放器等。本文将<span style="color: black;">触及</span>许多主题内容,喝一杯,慢慢看。</p><span style="color: black;">“</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">原文:https://zdimension.fr/crabs-all-the-way-down/</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">尽管自90年代<span style="color: black;">败兴</span>,<span style="color: black;">各样</span>各样的 CPU 架构数量<span style="color: black;">已然</span>逐步减少,但<span style="color: black;">此刻</span>仍然有许多<span style="color: black;">区别</span>的、不兼容的CPU架构在<span style="color: black;">运用</span>。大<span style="color: black;">都数</span>计算机<span style="color: black;">运用</span>x86_64,几乎所有的移动设备和<span style="color: black;">近期</span>的 Mac 都<span style="color: black;">运用</span>某种基于ARM64的ISA(指令集架构)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">不外</span>在特定<span style="color: black;">行业</span>,还有更奇特的:大<span style="color: black;">都数</span>路由器仍然<span style="color: black;">运用</span>MIPS(历史<span style="color: black;">原由</span>),一部分<span style="color: black;">研发</span>者<span style="color: black;">运用</span>RISC-V,PS3 <span style="color: black;">运用</span>PowerPC,20年前的<span style="color: black;">有些</span>服务器<span style="color: black;">运用</span>Itanium,当然,IBM 仍然在<span style="color: black;">营销</span><span style="color: black;">她们</span>基于S/390的大型机(<span style="color: black;">此刻</span>改名为z/Architecture)。嵌入式世界的<span style="color: black;">制品</span>就<span style="color: black;">更加多</span>了。AVR(用于Arduino)、SuperH(土星、Dreamcast、卡西欧9860计算器),以及可敬的8051,一个1980年的英特尔芯片,<span style="color: black;">迄今</span>仍在生产、<span style="color: black;">营销</span>,<span style="color: black;">乃至</span>被第三方扩展。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">所有这些架构在其定义特征上都有所<span style="color: black;">区别</span>,<span style="color: black;">重点</span>的区别是:</p>字的<span style="color: black;">体积</span>(word size)。8、16、31、32、64位,有时<span style="color: black;">更加多</span>。设计风格(design style)。RISC(指令少,操作简单),CISC(指令多,执行<span style="color: black;">繁杂</span>的操作,VLIW(指令长,<span style="color: black;">同期</span>并行做<span style="color: black;">非常多</span>事情)。存储架构(memory architecture)。哈佛(Harvard,独立的代码存储器和数据存储器),冯-诺伊曼(von Neumann,共享)。许可成本。RISC-V是开放的,可<span style="color: black;">以避免</span>费<span style="color: black;">运用</span>,而X86和ARM等则需要许可费。特性集(features set):有些特性在特定架构平台有特定的支持。<span style="color: black;">例如</span>,浮点数(x87)、加密(AES-NI)、支持本地高级字节码执行(Jazelle、AVR32B)、矢量计算(SSE、AVX、AltiVec)。<span style="color: black;">“</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">这还不算 DSP 架构,轻描淡写地说,它是 ISA 版本的“阴阳魔界(Twilight Zone)”(支持<span style="color: black;">各样</span><span style="color: black;">奥妙</span>的算术运算,特殊的数据<span style="color: black;">体积</span>等)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">译注:&nbsp;Twilight,本意是晨光或暮光,特指黎明或<span style="color: black;">傍晚</span>时分的太阳尚<span style="color: black;">位置于</span>地平线以下,微弱的阳光已<span style="color: black;">起始</span>照耀大地,使世界陷入一种为朦胧的状态。<span style="color: black;">傍晚</span>时分<span style="color: black;">一般</span>被视为通往幻想世界的入口。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">非常多</span>人构建了<span style="color: black;">她们</span>自制的CPU,要么在<span style="color: black;">实质</span>的面包板上,要么在软件中,用于模拟器或电路合成 。<span style="color: black;">她们</span>都是非常有趣的项目,即使对初学者<span style="color: black;">来讲</span><span style="color: black;">亦</span>是如此,<span style="color: black;">由于</span>它确实有助于<span style="color: black;">把握</span>代码<span style="color: black;">怎样</span>转化为电信号,为<span style="color: black;">咱们</span><span style="color: black;">运用</span>的<span style="color: black;">每一个</span>设备<span style="color: black;">供给</span>动力,以及<span style="color: black;">怎样</span>真正在简单操作的<span style="color: black;">基本</span>上实现<span style="color: black;">繁杂</span>的语言功能。</p>
    <h2 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">制作一个 CPU</span></h2>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">有些场景<span style="color: black;">促进</span>我在数字电路模拟器中设计一个简单的 类 ARM(ARM-ish)的 CPU。我最初<span style="color: black;">运用</span>的是logisim-evolution (后来我<span style="color: black;">作为</span>其<span style="color: black;">研发</span>团队的成员),<span style="color: black;">近期</span><span style="color: black;">因为</span>性能<span style="color: black;">原由</span>,我将电路迁移到了Digital(Logisim<span style="color: black;">没法</span>在超过50或60Hz的频率下模拟我的电路,而 Digital 则达到了20kHz)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">选取</span> ARM 是<span style="color: black;">由于</span>它支持ARM Thumb指令集的一个子集,它本身<span style="color: black;">便是</span>ARM CPU所支持的多个指令集之一。它<span style="color: black;">运用</span>32位字,但指令的宽度为16位。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">但又类 ARM (-ish)是<span style="color: black;">由于</span>,它只支持其中的一个子集(很大,但远不完整),并且在某些方面被故意限制。不支持<span style="color: black;">哪些</span>奇怪的指令,如PUSH / POP / LDM / STM系列(RISC ARM ISA中的一个巨大的来自CISC的污点),并且被汇编器实现为手动 load/store。<span style="color: black;">同期</span><span style="color: black;">亦</span>不支持中断(Interrupt)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">【此段重复,可忽略】从本质上讲,我设计的<span style="color: black;">不仅</span>是一个CPU,而是一个<span style="color: black;">能够</span><span style="color: black;">叫作</span>为计算机的东西;它有一个ROM,一个RAM,以及<span style="color: black;">做为</span> "前面板 "的<span style="color: black;">各样</span>设备。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">从本质上讲,我设计的<span style="color: black;">不仅</span>是一个 CPU,而是一个<span style="color: black;">能够</span><span style="color: black;">叫作</span>为计算机的东西:它有一个ROM,一个RAM,以及<span style="color: black;">做为</span> "前面板(front panel)"的<span style="color: black;">各样</span>设备。</p>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;">设备</h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">一台真正有用的计算机,不仅需要一个 CPU 和一个内存芯片。它还会有外围设备和其他设备:键盘、屏幕、磁盘驱动器、扬声器、网卡等,几乎所有你能(或<span style="color: black;">不可</span>)想象的东西都<span style="color: black;">已然</span>被做<span style="color: black;">成为了</span>计算机设备。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">归根结底,你<span style="color: black;">独一</span>需要的是能够从设备中传输数据。有两种相反的方式来做到这一点:要么设备是<span style="color: black;">尤其</span>的,要么就不是。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">基本上,<span style="color: black;">有些</span>架构(X86,说的<span style="color: black;">便是</span>你)除了内存之外,还有一个特殊的、独立的<span style="color: black;">位置</span>空间用于I/O,有其特殊的、<span style="color: black;">区别</span>的指令:在8086上,你会用MOV来读写主内存,用IN/OUT来读写设备。<span style="color: black;">有些</span>设备(最重要的设备:PS/2<span style="color: black;">掌控</span>器、软盘、串口......)有一个固定的端口号,其他设备在<span style="color: black;">起步</span>时由BIOS分配一个端口号。在过去,<span style="color: black;">一般</span>的做法是<span style="color: black;">需求</span>设置环境变量或编写配置文件来<span style="color: black;">通告</span>软件<span style="color: black;">那些</span>设备被<span style="color: black;">插进</span>(例如著名的BLASTER配置行)。这被<span style="color: black;">叫作</span>为PMIO(端口映射的输入/输出)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">另一个<span style="color: black;">选取</span>,<span style="color: black;">亦</span>是几乎所有其他人<span style="color: black;">运用</span>的<span style="color: black;">选取</span>,<span style="color: black;">包含</span>现代X86计算机,是<span style="color: black;">持有</span>一个统一的虚拟(virtual)<span style="color: black;">位置</span>空间。</p><span style="color: black;">“</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">我在<span style="color: black;">这儿</span><span style="color: black;">运用</span>虚拟这个词是为了将这个统一的<span style="color: black;">位置</span>空间与真正的<span style="color: black;">理学</span>内存<span style="color: black;">位置</span>空间区<span style="color: black;">掰开</span>来(<span style="color: black;">理学</span>内存<span style="color: black;">位置</span>空间本身<span style="color: black;">仅在</span><span style="color: black;">拥有</span>单一内存单元的<span style="color: black;">设备</span>上有真正的<span style="color: black;">道理</span>)。还有一个叫做虚拟内存(virtual memory)的概念,它指的是一个完全不<span style="color: black;">关联</span>的东西(虽然在方式上很<span style="color: black;">类似</span>):<span style="color: black;">经过</span><span style="color: black;">运用</span>交换(swap)等策略,为程序<span style="color: black;">供给</span>一个比计算机RAM更大的<span style="color: black;">位置</span>空间,<span style="color: black;">准许</span>将RAM页面移动到磁盘存储中,以释放工作内存的空间。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">想象一下,IP<span style="color: black;">位置</span>应该是映射<span style="color: black;">全部</span>互联网的,但在现实中,一个<span style="color: black;">位置</span>不<span style="color: black;">必定</span>要精确地映射到某个<span style="color: black;">地区</span>的一台<span style="color: black;">设备</span>。例如,127.0.0.1(IPv6中的::1)是本地回环<span style="color: black;">位置</span>,映射到你正在<span style="color: black;">运用</span>的<span style="color: black;">设备</span>。这不需要<span style="color: black;">经过</span>网络通信的软件<span style="color: black;">晓得</span>,<span style="color: black;">由于</span>映射<span style="color: black;">是由于</span>操作系统的网络栈完成的。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这儿</span><span style="color: black;">亦</span>是<span style="color: black;">同样</span>:(虚拟)<span style="color: black;">位置</span>空间的区域被映射到<span style="color: black;">理学</span>组件上。为了给你一个现实世界的例子,下图是NES 的<span style="color: black;">位置</span>空间。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/hicrFibaKFMd3nc7ZzdTH8pkftcn1ibDzhX6Hkmf9EzVHgEicib4PicfmiaYpveKplLJs4W56hwfT9GYXo9Kne5U4uqicw/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>img<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">从0到800(十六进制)的<span style="color: black;">位置</span>被映射到WRAM(工作RAM),从2000到2008映射到PPU(图形卡)的<span style="color: black;">掌控</span>寄存器,从4000到4018映射到APU(声卡)。这被<span style="color: black;">叫作</span>为MMIO(内存映射的输入/输出)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">对我而言,这种<span style="color: black;">办法</span>的最大优点是它的简单性。从CPU的<span style="color: black;">方向</span><span style="color: black;">来讲</span>:它只是内存<span style="color: black;">罢了</span>!我认为这是最重要的。取设备区域的<span style="color: black;">位置</span>、读、写,真的很简单。它<span style="color: black;">亦</span>使软件层面<span style="color: black;">更易</span>:你不必写内联汇编来调用特殊指令,只要你能从指针上读写,就<span style="color: black;">能够</span>了。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">除此之外,内存映射还<span style="color: black;">能够</span>用来<span style="color: black;">供给</span>对<span style="color: black;">区别</span>内存芯片(如ROM和RAM)的<span style="color: black;">拜访</span>。下面是我的电路图示意。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/hicrFibaKFMd3nc7ZzdTH8pkftcn1ibDzhX9hWVFg4cESYeHfkCDTZopS9SACEUkMAeRaxRHzzcXHeHXibEF0fUEyQ/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>circuit<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">重视</span>组件和映射器之间的边缘的箭头;它们<span style="color: black;">暗示</span>组件是只读、读/写或只写的。</p>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;">CPU</h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">与真正的 CPU 相比,<span style="color: black;">咱们</span>要做的这个 CPU 非常简陋。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">它和拇指<span style="color: black;">同样</span>大,有 16 个 32 位的寄存器,编号为r0到r15。最后三个有专用别名:r13是sp(Stack Pointer,栈指针),r14是lr(Link register,链接寄存器),r15是pc(Program Counter,程序计数器)。</p>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;">栈指针(Stack Pointer)</h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">内存是很难的,暂且不表,后面会讲。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">16个是<span style="color: black;">非常多</span>的。<span style="color: black;">因此</span>在现实中,它们被分为低(r0-r7)和高(r8-r15)寄存器。高位寄存器只能用特定的指令进行操作,<span style="color: black;">因此</span>低位寄存器是平常要用到的。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">指令被分为几类,每一类都<span style="color: black;">包括</span>有一个相同头部(common header)的指令。我不在<span style="color: black;">这儿</span>一一列举,但最常用的是ALU(算术和<span style="color: black;">规律</span>)运算、load/store 指令(相<span style="color: black;">针对</span>pc、sp或<span style="color: black;">通常</span>寄存器)、栈操作指令和分支指令(有<span style="color: black;">要求</span>和无<span style="color: black;">要求</span>)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">指令组由独立的子电路处理,它们都写入共享总线中。</p>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;">总线(Bus)</h3><span style="color: black;">“</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">Bus 是一个令人惊讶的多义词。它在<span style="color: black;">非常多</span><span style="color: black;">行业</span>都有定义,<span style="color: black;">乃至</span>在电子和硬件<span style="color: black;">行业</span>,它<span style="color: black;">亦</span>被用来做<span style="color: black;">各样</span>事情。所有这些定义之间的<span style="color: black;">一起</span><span style="color: black;">原因</span>是 "连接其他事物的东西"。与前几部分<span style="color: black;">同样</span>,无论<span style="color: black;">怎样</span>我都会简化解释,<span style="color: black;">由于</span>电子学是一个庞大的<span style="color: black;">科研</span><span style="color: black;">行业</span>。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">在电路设计术语中,总线是一组连接在<span style="color: black;">一块</span>的导线。<span style="color: black;">详细</span><span style="color: black;">来讲</span>,在这种<span style="color: black;">状况</span>下,它是一组电线,在某一瞬间<span style="color: black;">仅有</span>一根电线发出信号。在电气<span style="color: black;">行业</span>,这是<span style="color: black;">经过</span><span style="color: black;">运用</span><span style="color: black;">所说</span>的三态<span style="color: black;">规律</span>来实现的:一个信号要么是0,要么是1,要么是Z(发音为 "高阻抗")。Z是 “较弱”的信号,即,<span style="color: black;">倘若</span>你将Z信号与0或1信号相连,将输出后者。这<span style="color: black;">特别有</span>用:你<span style="color: black;">能够</span>有<span style="color: black;">非常多</span>独立的元件,<span style="color: black;">每一个</span>都有一个 "enable"输入,<span style="color: black;">仅有</span>在它们被使能时才输出信号,否则就输出Z。它是一个简单的<span style="color: black;">规律</span>门,<span style="color: black;">倘若</span>被启用,则输出其输入信号而不改变,否则输出Z。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">而后</span>,所有这些组件都<span style="color: black;">能够</span>插在<span style="color: black;">一块</span>,你<span style="color: black;">能够</span>使能其中一个,并<span style="color: black;">容易</span><span style="color: black;">得到</span>其输出。</p>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;">组件实例:带寄存器偏移的 load/store</h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">这是一个处理形如 {direction}R{sign}{mode} {destination}, [{base}, {offset} 指令的组件,其中:</p>{direction}:不是 LD(load),<span style="color: black;">便是</span> ST(store){sign}:要么不做(不扩展),要么<span style="color: black;">便是</span> S(符号扩展值,以填充32位){mode}:要么是 nothing(全字,32位)和 H(半字,16位),要么是 B(字节,8位){destination}:<span style="color: black;">目的</span>寄存器,要读出/写入{base}, {offset}:内存中的<span style="color: black;">位置</span>(将是两者的值之和)<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">例如</span>,ldrh r1, 大致相当于C代码中的r1 = *(short*)(r2 + r3)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">这组指令<span style="color: black;">能够</span>按如下表所示编码:</p>15141312111098765432100101opcodeRoRbRd<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">opcode 是一个 3位的值,用于<span style="color: black;">同期</span>对{direction}, {sign} 和 {mode}进行编码。</p><span style="color: black;">“</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">{direction} 有两个可能的值(load, store)。{sign} 有两个(raw, sign-extended),{mode}有三个 (word, halfword, byte)。这就<span style="color: black;">寓意</span>着有 2⋅2⋅3=12 种可能多组合,起码超过了opcode 的 2^3=8 种可能值,<span style="color: black;">因此</span>有些组合是不可能的。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">这是<span style="color: black;">由于</span><span style="color: black;">仅有</span>对不完整值(halfword, byte)的 load操作<span style="color: black;">能够</span>进行符号扩展,<span style="color: black;">因此</span>无效的组合(strsh, strsb, strs, ldrs)<span style="color: black;">无</span>得到编码。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这儿</span>是电路图:</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/hicrFibaKFMd3nc7ZzdTH8pkftcn1ibDzhXZg2O4gYvhQMqBZ322ibu1MBTosCXUt7uR4Y4YPfoCD9BCKuXXmQ2icLw/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>circuit2<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这儿</span><span style="color: black;">运用</span>到<span style="color: black;">有些</span><span style="color: black;">区别</span>的<span style="color: black;">规律</span>单元:</p>小三角形是隧道(Tunnel):命名的导线<span style="color: black;">能够</span>在电路中的任何<span style="color: black;">地区</span>接入。大梯形是复用器(Multiplexers):它们输出第n个输入,其中n<span style="color: black;">亦</span>是一个输入。三条线的三角形是缓冲器(Buffers):<span style="color: black;">倘若</span>边线是高电平,它们就输出其输入,否则就输出Z(高阻抗)。黄色方框将一个3位的低位寄存器号码转换成4位的寄存器号码(在其前面加一个0)。旁边有整数范围的大矩形是分割器(splitters):它们将一个多比特的值分割成多个较小的值,以<span style="color: black;">拜访</span>单个比特或比特范围。<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">从上到下依次为:</p>三个寄存器(Rd, Rb, Ro)在各自的位置(0-2, 3-5, 6-8)从指令中被读取,并被送到相应的全局通道(RW, RA, RB)。opcode被解码,以<span style="color: black;">检测</span>它是一个store(000,001,010)还是一个load(剩余值)。opcode被解码以找到模式的值。0<span style="color: black;">表率</span>字,1<span style="color: black;">表率</span>半字,2<span style="color: black;">表率</span>字节。opcode再次被解码以找到符号的值(仅对操作码011和111来说是真的,<span style="color: black;">因此</span><span style="color: black;">咱们</span><span style="color: black;">能够</span><span style="color: black;">检测</span>最后两个比特是高位)。<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">Instr0708隧道是这个组件的激活引脚;<span style="color: black;">倘若</span>当前指令属于这个指令组,它<span style="color: black;">便是</span>高电平。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">几乎所有的其他元件都和这个元件<span style="color: black;">同样</span>,当你把它们都插在<span style="color: black;">一块</span>时,你就得到了一个<span style="color: black;">能够</span>执行指令的电路。</p>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;">内存很难</h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">操作数据并将其存储在某处以便你以后<span style="color: black;">能够</span>取回,这个看似简单的问题实际上......并不简单。让你的CPU<span style="color: black;">拜访</span>一个大的线性内存单元阵列是<span style="color: black;">不足</span>的,你必须决定你要用它来做什么。<span style="color: black;">瞧瞧</span>这个Python程序。</p>print(<span style="color: black;">"Hello,&nbsp;World!"</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">)</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">字符串应该存放在哪里?它<span style="color: black;">必定</span>是在某个<span style="color: black;">地区</span>。那print呢?它不是一条指令,它只是一个全局变量,恰好被设置为一个内置函数或<span style="color: black;">办法</span>类型的对象,你<span style="color: black;">能够</span>用()操作符调用。它<span style="color: black;">亦</span>必须被储存在某个<span style="color: black;">地区</span>。记住,你<span style="color: black;">日前</span>真正<span style="color: black;">持有</span>的东西是一个大的数字数组。除此之外,你真正<span style="color: black;">持有</span>的<span style="color: black;">独一</span>一组操作是{"按<span style="color: black;">位置</span>加载(load)数值","按<span style="color: black;">位置</span>存储(store)数值"}。不是吗?</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">CPU的语言是汇编指令。这些指令有一个固定的、定义好的编码,在ARM Thumb指令集上,它们总是(<span style="color: black;">亦</span><span style="color: black;">便是</span>几乎总是)有相同的<span style="color: black;">体积</span>:16位。忽略指令头(告诉你这是哪条指令),这将占用几个比特,<span style="color: black;">咱们</span><span style="color: black;">火速</span>就会<span style="color: black;">发掘</span>,<span style="color: black;">倘若</span><span style="color: black;">咱们</span>把<span style="color: black;">位置</span><span style="color: black;">做为</span>立即值(指令中为常量值),<span style="color: black;">咱们</span><span style="color: black;">不可</span>寻址到超过216字节的内存。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">因此呢</span>:寻址模式和内存对齐。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">倘若</span><span style="color: black;">咱们</span>看一下平常的程序,<span style="color: black;">能够</span>观察到内存有两个<span style="color: black;">重点</span>的<span style="color: black;">运用</span><span style="color: black;">状况</span>:存储局部变量(函数中的变量,或参数),和存储全局变量(全局配置,将在程序之间共享的内存)。</p>用例分配<span style="color: black;">体积</span>最大生命周期分配时间释放时间本地(Local)<span style="color: black;">一般</span>为小分配在当前函数调用内当进入当前函数时当离开当前函数时全局(Global)任意静态生命周期任意时刻任意时刻<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">有一个<span style="color: black;">显著</span>的区别:一方面,"本地内存",用于小的、确定的分配,而 "全局内存",用于任何事情,在任何时间,很少有限制。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">这<span style="color: black;">怎样</span>映射到<span style="color: black;">咱们</span>的 "大块内存单元"?<span style="color: black;">咱们</span>将从 "全局 "内存<span style="color: black;">起始</span>。<span style="color: black;">咱们</span>对它的<span style="color: black;">运用</span>方式一无所知,<span style="color: black;">因此</span><span style="color: black;">咱们</span><span style="color: black;">不可</span>做太多的假设。你<span style="color: black;">能够</span>在任何时候<span style="color: black;">需求</span>任何数量的字节,并在任何时候把它还给操作系统,<span style="color: black;">况且</span>你<span style="color: black;">一般</span><span style="color: black;">期盼</span> "还给 "的空间<span style="color: black;">能够</span>被后续分配<span style="color: black;">运用</span>。这很难。现实世界中的对应物是一大堆东西,躺在地上,等着某个程序把它捡起来,<span style="color: black;">运用</span>它,<span style="color: black;">或</span>把它扔进垃圾桶。<span style="color: black;">因为</span>这个<span style="color: black;">原由</span>,它被<span style="color: black;">叫作</span>为 “堆(heap)”。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">接下来,<span style="color: black;">咱们</span><span style="color: black;">能够</span>看到 "本地内存 "以一种特定的方式演变:当<span style="color: black;">咱们</span>进入一个函数时,它就会增长,当<span style="color: black;">咱们</span>退出时,它就会缩小,<span style="color: black;">况且</span>函数调用遵循类似栈(stack)的模式(当你进入一个函数时,你<span style="color: black;">能够</span>做任何你想做的事,但你<span style="color: black;">最后</span>总是在某个点退出它)。事实上,它确实是一个栈(在算法数据结构的<span style="color: black;">道理</span>上),它有两个操作:push (增长)和pop(缩小)。这个 "本地内存 "被<span style="color: black;">叫作</span>为栈。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">因为</span>它是以这种方式增长和收缩的,<span style="color: black;">因此</span><span style="color: black;">咱们</span><span style="color: black;">并不</span>需要对分配的内存块做任何登记,<span style="color: black;">例如</span>它们在哪里,用什么策略来<span style="color: black;">选取</span>分配新块的位置,等等。<span style="color: black;">咱们</span><span style="color: black;">独一</span>需要的数据是 “深度”(即<span style="color: black;">咱们</span>在该堆栈中的深度,<span style="color: black;">或</span>换句话说,栈的长度)。<span style="color: black;">一般</span>的做法是,<span style="color: black;">咱们</span>将内存中的某个<span style="color: black;">地区</span>设置为栈的起点,并在某个<span style="color: black;">地区</span>(例如,在一个寄存器中)<span style="color: black;">保存</span>一个全局变量,该变量<span style="color: black;">包括</span>栈最顶层的项(topmost item)在内存中的位置:栈指针(在ARM上为sp,或其全名为r13)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">还有一点我<span style="color: black;">无</span>说明:栈的增长方向。有些架构使它向上生长(push等于<span style="color: black;">增多</span>栈指针,pop等于减少),但大<span style="color: black;">都数</span>架构则相反,使它向下生长。向下增长<span style="color: black;">寓意</span>着你<span style="color: black;">能够</span>很容易地使堆(heap)从<span style="color: black;">位置</span>0<span style="color: black;">起始</span>,并使栈(stack)从任何最大的<span style="color: black;">位置</span><span style="color: black;">起始</span>,<span style="color: black;">况且</span>你<span style="color: black;">能够</span>保证它们不会<span style="color: black;">出现</span>碰撞,直到堆向上增长得太多或堆向下增长得太多。</p><span style="color: black;">“</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">在内存术语中,向上<span style="color: black;">寓意</span>着<span style="color: black;">增多</span>,向下<span style="color: black;">寓意</span>着减少。"栈向下增长 "<span style="color: black;">寓意</span>着栈的增长会减少栈指针,反之<span style="color: black;">也</span>然。网上的许多图都是以<span style="color: black;">位置</span>0为顶点<span style="color: black;">来讲</span>明内存,暗示向下<span style="color: black;">寓意</span>着<span style="color: black;">增多</span>,但这是一种误导。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">此刻</span><span style="color: black;">咱们</span><span style="color: black;">晓得</span>了内存的工作原理,<span style="color: black;">那样</span><span style="color: black;">咱们</span>该<span style="color: black;">怎样</span><span style="color: black;">拜访</span>它呢?<span style="color: black;">咱们</span>已经看到,<span style="color: black;">因为</span>指令太小,<span style="color: black;">咱们</span><span style="color: black;">没法</span><span style="color: black;">所有</span>寻址,<span style="color: black;">那样</span><span style="color: black;">咱们</span><span style="color: black;">怎样</span>应对这种<span style="color: black;">状况</span>呢?</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">答案是<span style="color: black;">运用</span><span style="color: black;">区别</span>的寻址模式。例如,<span style="color: black;">倘若</span>你想<span style="color: black;">拜访</span>栈上的内存,你<span style="color: black;">一般</span>会<span style="color: black;">拜访</span>栈顶部的东西(例如你的局部变量),<span style="color: black;">因此</span>你不必给出完整的内存<span style="color: black;">位置</span>(大),而只需要给出相<span style="color: black;">针对</span>栈指针的数据距离(小)。这<span style="color: black;">便是</span>sp-relative寻址模式,看起来像ldr r1, 。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">另外</span>,<span style="color: black;">咱们</span><span style="color: black;">能够</span>假设你大多会存储4字节或更大的东西,<span style="color: black;">因此</span><span style="color: black;">咱们</span>会说栈是字对齐(word-aligned)的:所有东西都会被移动,以便<span style="color: black;">位置</span>是4的倍数。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">有时,你想存储只对单一功能有用的数据。例如, switch / match指令<span style="color: black;">一般</span><span style="color: black;">运用</span>跳(jump)表来实现:在程序中存储一个偏移量列表,<span style="color: black;">而后</span>加载正确的偏移量并<span style="color: black;">转</span>过去。<span style="color: black;">因为</span>这将被存储在函数本身的代码中,<span style="color: black;">因此呢</span>相<span style="color: black;">针对</span>代码中的当前位置进行内存操作就变得<span style="color: black;">特别有</span>用,这<span style="color: black;">便是</span><span style="color: black;">怎样</span>得到pc-relative寻址的:ldr r2, 。与sp<span style="color: black;">同样</span>,内存是字对齐的,<span style="color: black;">因此</span>偏移量必须是4的倍数。</p>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;">函数调用</h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">在汇编中,调用函数的最简单<span style="color: black;">办法</span>是<span style="color: black;">经过</span><span style="color: black;">运用</span>jump。你把一个标签放在某个<span style="color: black;">地区</span>,<span style="color: black;">而后</span>跳到那里。但有一个问题:你怎么回去呢?一个函数<span style="color: black;">能够</span>从多个<span style="color: black;">地区</span>被调用,<span style="color: black;">因此</span>你需要能够 “记住 ”这个函数是从哪里被调用的,<span style="color: black;">况且</span>你需要能够跳到一个<span style="color: black;">位置</span>,而不是跳到一个已知的标签。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">简单的<span style="color: black;">办法</span>,就像之前的栈指针<span style="color: black;">同样</span>,是<span style="color: black;">运用</span>一个全局变量(即寄存器)来存储调用者的<span style="color: black;">位置</span>,并有一个特殊的<span style="color: black;">转</span>指令,将寄存器设置到当前位置(链接),<span style="color: black;">这般</span><span style="color: black;">咱们</span>就<span style="color: black;">能够</span>在以后回到它(分支)。在ARM上,这<span style="color: black;">便是</span>bl(branch-link)系列指令,该寄存器被<span style="color: black;">叫作</span>为链接寄存器(缩写为lr,昵<span style="color: black;">叫作</span>为r14)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">然则</span>还有一个问题:它对嵌套调用不起<span style="color: black;">功效</span>! <span style="color: black;">倘若</span>你从另一个被调用的函数里面调用一个函数,链接寄存器的值会被覆盖。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">这<span style="color: black;">并不</span>是一个新问题:当你调用一个函数时,其他的寄存器<span style="color: black;">亦</span>会被覆盖,你<span style="color: black;">不可</span>指望程序员去阅读<span style="color: black;">她们</span>所调用的<span style="color: black;">每一个</span>函数的代码,<span style="color: black;">瞧瞧</span><span style="color: black;">那些</span>寄存器是安全的,<span style="color: black;">那些</span>不是。<span style="color: black;">这儿</span><span style="color: black;">触及</span>到调用惯例:在<span style="color: black;">每一个</span>架构(X86、ARM......)上都有一套规则(ABI),告诉你一切是<span style="color: black;">怎样</span>工作的,一个函数被<span style="color: black;">准许</span>做什么,<span style="color: black;">尤其</span>是<span style="color: black;">那些</span>寄存器应该被被调用者<span style="color: black;">保存</span>下来。一个<span style="color: black;">保存</span>的寄存器不是只读的:被调用者<span style="color: black;">能够</span>对它做任何事情,只要当<span style="color: black;">掌控</span>权被交还给调用者时,旧的值就回来了。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">处理</span>这个问题的<span style="color: black;">办法</span>是<span style="color: black;">经过</span>寄存器<span style="color: black;">保留</span>。当进入一个函数时,在栈中为局部变量分配空间,但<span style="color: black;">亦</span>为必须<span style="color: black;">保存</span>的寄存器分配空间,当退出时,原始值从栈中放回到寄存器中。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">在ARM上的这些寄存器中,链接寄存器<span style="color: black;">亦</span>被<span style="color: black;">保留</span>。ARM特殊寄存器<span style="color: black;">能够</span><span style="color: black;">做为</span>通用寄存器<span style="color: black;">运用</span>的一个很酷的方面是,你不必<span style="color: black;">运用</span>分支指令来<span style="color: black;">转</span>到某个<span style="color: black;">地区</span>:你<span style="color: black;">能够</span>直接写到PC中去。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">ARM汇编中的函数的<span style="color: black;">一般</span>模式是<span style="color: black;">这般</span>的:</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">my_function:</p>&nbsp;push&nbsp;{r4,&nbsp;r5,&nbsp;lr}&nbsp;;&nbsp;save&nbsp;r4,&nbsp;r5&nbsp;<span style="color: black;">and</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">&nbsp;lr</p>movs&nbsp;r4,<span style="color: black;">#123&nbsp;;&nbsp;do&nbsp;stuff</span>&nbsp;movs&nbsp;r5,&nbsp;<span style="color: black;">#42</span>&nbsp;pop&nbsp;{r4,&nbsp;r5,&nbsp;pc}&nbsp;;&nbsp;restore&nbsp;the&nbsp;values&nbsp;to&nbsp;r4,&nbsp;r5&nbsp;<span style="color: black;">and</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">&nbsp;*pc*!</p>
    <h2 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">设备</span></h2>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">要使一个电路看起来像一台计算机,需要的东西并不多。<span style="color: black;">针对</span>初学者<span style="color: black;">来讲</span>,你可能想从以下方面入手。</p>一个键盘(读取原始字符输入)。终端<span style="color: black;">表示</span>器(<span style="color: black;">表示</span>字符,如终端模拟器)。一个视频<span style="color: black;">表示</span>器(<span style="color: black;">表示</span>原始像素数据)。一个随机数<span style="color: black;">出现</span>器。一个十进制的7段<span style="color: black;">表示</span>器。一个网卡(<span style="color: black;">能够</span><span style="color: black;">经过</span>TCP接收和传输数据)。所有这些都被CPU和在其上运行的程序视为内存中的<span style="color: black;">位置</span>。例如,向<span style="color: black;">位置</span>0xFFFFFF00写一个字节将在终端<span style="color: black;">表示</span>器上<span style="color: black;">表示</span>一个字符。从<span style="color: black;">位置</span>0xFFFFFF18中读取一个字节,就<span style="color: black;">能够</span><span style="color: black;">晓得</span>键盘缓冲区<span style="color: black;">是不是</span>为空。<h2 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">运行代码</span></h2>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">在这个东西上运行代码的最简单<span style="color: black;">办法</span>是简单地编写<span style="color: black;">设备</span>代码并将其加载到ROM中。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这儿</span>有一个简单的程序。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">movs r0, #255 ; r0 = 255 (0x000000FF)</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">mvns r0, r0 ; r0 = ~r0 (0xFFFFFF00, address of terminal)</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">movs r1, #65 ; r1 = 65 (ASCII code of A)</p>str r1, ; *r0 = r1<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">它将被汇编为 20ff 43c0 2141 6001(8 字节),当被加载运行时,它会在 4 个周期后输出 A:</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/hicrFibaKFMd3nc7ZzdTH8pkftcn1ibDzhXXWlhCznu3SyVY0FbQH3BaezQqug5zLGb52eEsNA9O1y3TTMxzZutLA/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">当然,用汇编编写程序并不完全实用。<span style="color: black;">咱们</span>在很久以前就为此发明了宏汇编程序和高级(与汇编相比)编程语言,<span style="color: black;">因此</span>在<span style="color: black;">这儿</span>就<span style="color: black;">这般</span>做吧。我最初用的是C语言,但<span style="color: black;">火速</span>就换<span style="color: black;">成为了</span>Rust,<span style="color: black;">由于</span>它的易用性和强大的宏支持(<span style="color: black;">针对</span>像<span style="color: black;">这般</span>的受限环境<span style="color: black;">特别有</span>用)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">Rust(技术上<span style="color: black;">来讲</span>,参考编译器rustc)<span style="color: black;">运用</span>LLVM<span style="color: black;">做为</span>编译的后端,<span style="color: black;">因此</span>任何LLVM支持的<span style="color: black;">目的</span>,Rust都在<span style="color: black;">必定</span>程度上支持。在<span style="color: black;">这儿</span>,我<span style="color: black;">运用</span>内置<span style="color: black;">目的</span>thumbv6m-none-eabi(ARM v6-M Thumb,<span style="color: black;">无</span>供应商或操作系统,嵌入式ABI),但有一个很大的限制:我的CPU不是一个完整的ARM CPU。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">因为</span>不是所有的指令都被支持(有些指令<span style="color: black;">是由于</span>我自制的汇编器模拟的),我<span style="color: black;">不可</span>只是<span style="color: black;">创立</span>ARM二进制文件并加载它们。我需要<span style="color: black;">运用</span>我自己的汇编器,<span style="color: black;">因此</span>我直接调用编译器,告诉它发出原始汇编代码,<span style="color: black;">而后</span>将其发送到我的汇编器,最后生成可加载的二进制文件。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">另外</span>,<span style="color: black;">因为</span>我运行的代码<span style="color: black;">无</span>操作系统,<span style="color: black;">无</span>任何<span style="color: black;">外边</span>代码,<span style="color: black;">因此</span>我<span style="color: black;">不可</span><span style="color: black;">运用</span>Rust的标准库。这是一个完全支持的用例(<span style="color: black;">叫作</span>为no_std),并不<span style="color: black;">寓意</span>着我完全<span style="color: black;">不可</span><span style="color: black;">运用</span>任何东西:它只是<span style="color: black;">寓意</span>着我不<span style="color: black;">运用</span>std crate(<span style="color: black;">一般</span>的标准库),而是<span style="color: black;">运用</span> core crate,它只<span style="color: black;">包括</span>最基本的必需品,<span style="color: black;">尤其</span>是不依赖于运行在下面的操作系统。然而,核心库不<span style="color: black;">包含</span>任何依赖堆分配的东西(如String或Vec),这些都是在alloc库中找到的,<span style="color: black;">因为</span>与我的构建系统<span style="color: black;">相关</span>的<span style="color: black;">有些</span><span style="color: black;">繁杂</span><span style="color: black;">原由</span>,我<span style="color: black;">亦</span>不<span style="color: black;">运用</span>这个核心库。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">基本上,我写了我自己的标准库。我<span style="color: black;">此刻</span><span style="color: black;">能够</span>写<span style="color: black;">有些</span>程序,<span style="color: black;">例如</span>:</p><span style="color: black;"><span style="color: black;">fn</span>&nbsp;<span style="color: black;">main</span></span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">()&nbsp;{</p>&nbsp;<span style="color: black;">println!</span>(<span style="color: black;">"Hello,&nbsp;world!"</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">);</p>&nbsp;screen::circle(<span style="color: black;">10</span>,&nbsp;<span style="color: black;">10</span>,&nbsp;<span style="color: black;">20</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">,&nbsp;ColorSimple::Red);</p>&nbsp;<span style="color: black;">let</span>&nbsp;x&nbsp;=&nbsp;fp32::from(<span style="color: black;">0.75</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">);</p>&nbsp;<span style="color: black;">let</span>&nbsp;<span style="color: black;">mut</span>&nbsp;video&nbsp;=&nbsp;screen::tty::blank().offset(<span style="color: black;">50</span>,&nbsp;<span style="color: black;">50</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">);</p>&nbsp;<span style="color: black;">println!</span>(<span style="color: black;">"sin("</span>,&nbsp;Blue.fg(),&nbsp;x,&nbsp;Black.fg(),&nbsp;<span style="color: black;">")&nbsp;=&nbsp;"</span>,&nbsp;Green.fg(),&nbsp;x.sin(),&nbsp;=&gt;&nbsp;&amp;<span style="color: black;">mut</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">&nbsp;video);</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">}</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">输出:</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
    <h2 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">陷阱</span></h2>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">运用</span>rustc的原始汇编输出<span style="color: black;">寓意</span>着我<span style="color: black;">不可</span>依靠我正在构建的 crate 以外的其他 crate 的代码(这需要<span style="color: black;">运用</span>链接器,我在<span style="color: black;">这儿</span><span style="color: black;">无</span><span style="color: black;">运用</span>)。我<span style="color: black;">乃至</span><span style="color: black;">不可</span><span style="color: black;">运用</span>编译器的内置函数:像memcpy或memclr<span style="color: black;">这般</span>的函数经常被用来执行块拷贝,但它们并不存在于生成的汇编中,<span style="color: black;">因此</span>我不得不自己实现它们(我从Redox<span style="color: black;">这儿</span>借用了<span style="color: black;">有些</span>代码)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">另一个问题是,<span style="color: black;">因为</span>我正在模拟<span style="color: black;">有些</span>指令(<span style="color: black;">经过</span>将它们翻译成其他支持的指令序列),分支偏移量可能比编译器预期的要大。问题是:Thumb上的<span style="color: black;">要求</span>性分支需要一个8位有符号的立即值,<span style="color: black;">因此</span><span style="color: black;">倘若</span>你试图跳到超过128条指令的前面或后面,你就<span style="color: black;">不可</span>对该指令进行编码。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">在实践中,这<span style="color: black;">寓意</span>着我经常要从函数中提取代码块以使其更小,<span style="color: black;">况且</span><span style="color: black;">全部</span>代码库都<span style="color: black;">运用</span>了#,以迫使编译器将这些代码块放在单独的函数中。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">实现一个可用的标准库并不是最简单的任务。<span style="color: black;">保证</span><span style="color: black;">全部</span>库符合人机工效学原理,<span style="color: black;">运用</span>起来很顺手则更难。我不得不<span style="color: black;">运用</span>许多不稳定的(nightly-only)功能,如GATs、<span style="color: black;">相关</span>类型默认值和特化等等。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">选取</span> Rust 完成整个项目的舒适度,让我有兴趣在<span style="color: black;">将来</span>的底层/嵌入式<span style="color: black;">研发</span>项目中<span style="color: black;">选取</span><span style="color: black;">运用</span> Rust。<span style="color: black;">倘若</span>用C语言来做这个项目的百分之一,难度会大得多,<span style="color: black;">况且</span>代码的可读性<span style="color: black;">亦</span>不会像这个项目<span style="color: black;">同样</span>。</p>
    <h2 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">成品展示</span></h2>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;">绘图器(Plotter)</h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">展示视频:https://zdimension.fr/content/media/2022/08/javaw_8FU7Np02mK.mp4</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">这个绘图器<span style="color: black;">运用</span>fixed-point(16.16)数字库,三角函数是用泰勒级数实现的。</p>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;">BASIC 解释器</h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">这是一个简单的BASIC解释器REPL,类似于80年代的家用电脑(如C64)上的东西。你<span style="color: black;">能够</span>逐行输入程序,<span style="color: black;">表示</span>它们,并运行它们。支持的指令有PRINT、INPUT、CLS、GOTO和LET。提示支持LIST、RUN、LOAD、ASM和ASMRUN。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>basic<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">程序<span style="color: black;">亦</span><span style="color: black;">能够</span><span style="color: black;">经过</span>网络加载(类似于C64上的磁带加载),用一个程序,如:</p>cat&nbsp;$<span style="color: black;">1</span>&nbsp;&lt;(echo)&nbsp;|&nbsp;<span style="color: black;">#&nbsp;read&nbsp;file,&nbsp;add&nbsp;newline</span>dos2unix&nbsp;|&nbsp;&nbsp;&nbsp;<span style="color: black;">#&nbsp;convert&nbsp;<span style="color: black;">line</span>ends&nbsp;to&nbsp;Unix&nbsp;(LF)</span>nc&nbsp;-N&nbsp;::<span style="color: black;">1</span>&nbsp;<span style="color: black;">4567</span>&nbsp;&nbsp;<span style="color: black;">#&nbsp;send&nbsp;to&nbsp;port</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">在<span style="color: black;">这儿</span>,LOAD<span style="color: black;">起始</span>监听,收到的行会以#为前缀<span style="color: black;">表示</span>,并像用户输入的那样进行阅读。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>basic2<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">程序<span style="color: black;">亦</span><span style="color: black;">能够</span>被编译成 Thumb 汇编以<span style="color: black;">得到</span>更高的性能。<span style="color: black;">关联</span>视频:https://zdimension.fr/content/media/2022/08/javaw_ABdTnpRbRl.mp4</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">这部分工作机制:</p><span style="color: black;">每一个</span>BASIC指令和表达式都被转换(编译)为一连串的汇编指令,例如CLS只是将12(\f)存入终端输出的<span style="color: black;">位置</span>,而表达式只是设置了一个小的栈机,对公式进行计算并将结果存入r1,LET指令计算其表达式并将r1存入变量的存储单元中。一旦程序被编译,它就像一个函数<span style="color: black;">同样</span>被执行,像<span style="color: black;">这般</span>。<span style="color: black;">let</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">&nbsp;ptr&nbsp;=&nbsp;instructions.as_ptr();</p><span style="color: black;">let</span>&nbsp;as_fn:&nbsp;<span style="color: black;">extern</span>&nbsp;<span style="color: black;">"C"</span>&nbsp;<span style="color: black;"><span style="color: black;">fn</span></span>()&nbsp;-&gt;&nbsp;()&nbsp;=&nbsp;<span style="color: black;">unsafe</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">&nbsp;{&nbsp;core::mem::transmute(ptr)&nbsp;};</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">as_fn();</p>bx lr指令被附加在指令列表的最后,<span style="color: black;">因此</span>当程序结束时,它将<span style="color: black;">掌控</span>权交还给解释器。<h3 style="color: black; text-align: left; margin-bottom: 10px;">Web 服务器</h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"><span style="color: black;">web</span></p>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;">Scheme 语言 REPL</h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;">终端模拟器</h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">这是一个简单的终端模拟器,支持ANSI(VT)转义代码的一个子集(足以<span style="color: black;">表示</span><span style="color: black;">美丽</span>的颜色和移动光标)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">它<span style="color: black;">运用</span>从<span style="color: black;">这儿</span>借来的5x7字体,ANSI解码<span style="color: black;">规律</span>是手工编写的(外面有<span style="color: black;">有些</span>工具箱<span style="color: black;">便是</span><span style="color: black;">这般</span>做的,但它们支持所有的ANSI代码,而我只需要这个程序的一个非常小的子集)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">像<span style="color: black;">这般</span>的一个脚本<span style="color: black;">能够</span>用来<span style="color: black;">起步</span>一个shell,并把它<span style="color: black;">传送</span>到电路中。</p>exec&nbsp;<span style="color: black;">3</span>&lt;&gt;/dev/tcp/<span style="color: black;">127.0</span>.<span style="color: black;">0.1</span>/<span style="color: black;">4567</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">cd&nbsp;~</p>unbuffer&nbsp;-p&nbsp;sh&nbsp;-c&nbsp;<span style="color: black;">stty</span>&nbsp;echo&nbsp;-onlcr&nbsp;cols&nbsp;<span style="color: black;">80</span>&nbsp;rows&nbsp;<span style="color: black;">30</span>erase&nbsp;^H;sh&nbsp;&lt;&amp;<span style="color: black;">3</span>&nbsp;<span style="color: black;">1</span>&gt;&amp;<span style="color: black;">3</span>&nbsp;<span style="color: black;">2</span>&gt;&amp;<span style="color: black;">3</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>shell<h3 style="color: black; text-align: left; margin-bottom: 10px;">MIDI 音乐播放器</h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">Digital<span style="color: black;">供给</span>了一个MIDI输出组件,它支持按下或释放某个乐器的键,<span style="color: black;">因此</span>我写了一个简单的程序,用midly来解析<span style="color: black;">经过</span>网络发送的MIDI文件,<span style="color: black;">而后</span>解码有用的信息来播放歌曲。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">关联</span>视频:https://zdimension.fr/content/media/2022/08/javaw_vJd5RnPa6r-1.mp4</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
    <h2 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">小结</span></h2>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">总而言之,这<span style="color: black;">特别有</span>趣。ARM/Thumb是一个很好的架构,<span style="color: black;">能够</span><span style="color: black;">做为</span>一个辅助项目来实施,<span style="color: black;">由于</span>它得到了编译器的良好支持,<span style="color: black;">况且</span>它的一个足够小的子集就足以运行有趣的代码。Logisim和Digital都是用于数字电路仿真的优秀工具。Rust是<span style="color: black;">办事</span>情和制造东西的好工具。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">最后,</span>本文项目源码:parm_extended:https://github.com/zdimension/parm_extended</p>




7wu1wm0 发表于 2024-10-16 02:23:50

网站建设seio论坛http://www.fok120.com/

b1gc8v 发表于 2024-11-10 16:03:33

交流如星光璀璨,点亮思想夜空。
页: [1]
查看完整版本: 【译】超硬核|在自制的 CPU 上运行 Rust