kvmtool下载编译

git clone https://github.com/kvmtool/kvmtool.git 下载后进入到目录执行make即可。

补码

计算机怎么表示负数?以四位有符号数为例,使用高位作为符号位,最高位为0表示正数,为1表示负数,其余三位用来表示值。在计算机中,我们将这种表示方式称为原码。例如:

十进制 二进制 十进制 二进制
2 0010 -2 1010

使用原码表示时,数字0有两种编码:0000和1000。因此,如果使用原码,在设计系统时就需要额外的电路区分+0和-0更糟糕的是如果一个数的正数相加应为0,2 - 2为例:2 - 2 = 2 + (-2) = 0010 + 1010 = 1100 = -4 这明显不对,也就是说,原码不能正确计算减法。怎么办呢?聪明的人类发现使用补码表示数字就可以加减法了。

即:正数的反码和补码都是原码,负数的反码是除符号位以外按位取反,补码是在反码基础上+1。使用补码重新计算 2 - 2:2 + (-2) = 0010 + 1110 = 0000

ASCII码

计算机用高低电平分别表示0和1,所有的数据在存储和运算时都要使用二进制表示,如果我们想用数字表示文本,就要对每一个文本进行编码,目的就是文本转换成数值。具体用哪些二进制是数字表示哪个符号,美国国家标准协会制定了美国信息交换标准代码(American Standard Code for Information Interchange)简称ASCII。ASCII使用8位编码,最多可以表示256个字符,hello world使用的编码就是为:68 65 6C 6C 6F 77 6F 72 6C 64

写外设指令

操作码 指令 描述
EE out DX,AL 将寄存器AL中的字节输出到寄存器DX中的I/O端口地址

第一列为操作码,第二列为汇编语法描述的指令,第三列是指令意义的详细描述。从描述中我们可以得到,EE操作码它会自动到寄存器AX中读取源操作数,到寄存器DX中读取目的操作数。out为操作码的助记符,AL为8位寄存器,DX是16位寄存器,ASCII码是8位的,串口的地址0x3F8(IBM计算机外设地址)是需要使用16位的表示的。

根据写串口指令的格式,我们的程序流程细化为:在执行EE命令前,将串口地址0x3F8存入寄存器DX,将字符存入寄存器AL。

准备源操作数

由out指令可见,在运行指令之前需要将字符A存储到寄存器AL中。x86提供了数据复制指令mov,用于将数据从原操作数复制到目的操作数。原操作数是一个8位立即数,目的操作数是r8,表示一个8位寄存器。

操作码 指令 描述
B0+rb mov r8,imm8 将一个8位立即数复制到一个8位寄存器

指令编码为(B0+rb)ib。根据x86手册,其中rb表示使用操作码的低三位编码目的操作数,即将目的操作寄存器r8的编码嵌入操作吗的低三位。寄存器AL对应的编码为0,因此B0+rb最终编码为B0。

指令编码中的ib对应原操作数,其中i表示立即数,b表示立即数的宽度是一个字节。所以,ib表示跟在操作码之后的是一个8位立即数,和字符对应的ASCII码组合最终(B0+rb)ib的编码分别为:B068 B065 B06C B06C B06F B077 B06F B072 B06C B064。

准备目的操作数

我们需要将串口地址写到寄存器DX中,实质是将一个16位立即数复制到一个16位寄存器, 这显然还是一个数据复制操作

操作码 指令 描述
B8+rw mov r16,imm16 将一个16位立即数复制到一个16位寄存器

这个格式mov指令也接受两个操作数,只不过是16位的。指令编码中的rw表示使用操作码的低三位编码目的操作数,即将目的操作寄存器r16的编码嵌入操作码的低三位。根据x86手册,寄存器DX对应的编码为2,因此B8+rw编码为BA。

指令编码的iw中的i表示立即数,w表示立即数的宽度是一个字,即两个字节。所以iw表示跟在操作码之后的是一个16位立即数,这里即串口地址。不过x86处理器使用小端模式,所以最终(B8+rw)iw的编码为BAF803。

跳转指令

我们的程序循环向串口输出字符串helloworld,在向串口输出后,需要跳转到程序开始的位置,因此需要一个跳转指令jmp,格式如下。

操作码 指令 描述
EB cb jmp rel8 跳转到指令指针+rel8的位置

jmp指令后接一个rel8。rel是relative的缩写,表示相对的意思,8代表8位,因此可以跳转到相对这条指令-128~127的范围。在指令编码中操作码EB之后“cb”就是这个8位的相对偏移。

如果我们想让指令指针指向程序开头,即B0所在的内存地址,那么jmp需要向后跳转35个字节。因为是向后跳转,所以是-35,又因为计算机中使用的是补码,所以我们需要转换一下,根据原码转补码的规则-35的原码是10100011,补码就为11011101,使用16进制表示,即DD。

至此,我们就完成了这段程序的机器语言编码:

ba f8 03
b0 68
ee
b0 65
ee
b0 6c
ee
b0 6c
ee
b0 6f
ee
b0 77
ee
b0 6f
ee
b0 72
ee
b0 6c
ee
b0 64
ee
eb dd

创建程序文件

我们使用vim以二进制模式打开一个文件,vim -b hello_world.bin。按i进入插入模式,然后输入我们的代码

。输入完成后,按下ESC返回标准模式。在标准模式下按下“:”键,进入命令行模式。在“:”的后面输入将16进制转为二进制命令:

:%! xxd -p -r

"%"表示整个文件内容,”%!“一起使用表示将整个文件内容作为后面xxd的输入,然后使用xxd从16进制转为二进制的输出替换整个文件内容。-p 表示不需要任何格式,-r意为反过来。最后输入:“wq”保存退出

使用kvmtool运行程序

sudo kvmtool/lkvm run -c 1 -k hello_world.bin

机器语言编写helloworld的更多相关文章

  1. Hadoop学习笔记(5) ——编写HelloWorld(2)

    Hadoop学习笔记(5) ——编写HelloWorld(2) 前面我们写了一个Hadoop程序,并让它跑起来了.但想想不对啊,Hadoop不是有两块功能么,DFS和MapReduce.没错,上一节我 ...

  2. 开天辟地-用visualstudio2010编写helloworld

    安装好visual之后,创建新项目 向源文件添加helloworld.cpp 编写helloworld代码,编译运行即可 在运行时候出现一个错误,错误和解决方法如下:

  3. 运维开发笔记整理-使用Django编写helloworld

    运维开发笔记整理-使用Django编写helloworld 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   一.创建Django项目 1>.创建Django项目 djang ...

  4. X86平台下用汇编写"HelloWorld"

    首先需要安装一个汇编器,我用的是Nasm,这个汇编器在Linux下安装还是很简单的. Nasm下载地址http://www.nasm.us/pub/nasm/releasebuilds/ 在下载之后对 ...

  5. Android环境搭建和编写helloworld

    一.配置jdk环境(学过java的请无视) 1.安装jdk jdk下载地址:http://www.oracle.com/technetwork/cn/java/javase/downloads/jdk ...

  6. Hadoop教程之编写HelloWorld(2)

    前面我们写了一个Hadoop程序,并让它跑起来了.但想想不对啊,Hadoop不是有两块功能么,DFS和MapReduce.没错,上一节我们写了一个MapReduce的HelloWorld程序,那这一节 ...

  7. 用记事本编写helloworld文件但是javac时报错

    刚开始编写第一个java文件时遇到的错误: javac HelloWorld.java HelloWorld.java:1: 错误: 写入HelloWorld时出错: HelloWorld.class ...

  8. 全志A33 lichee 搭建Qt App开发环境编写helloworld

    开发平台 * 芯灵思SinlinxA33开发板 淘宝店铺: https://sinlinx.taobao.com/ 嵌入式linux 开发板交流 QQ:641395230 Step 1 在虚拟机(Ce ...

  9. Linux安装jdk,编写helloworld程序

    今天学习了Linux安装jdk,做个笔记记录一下. 第一步,确定Linux是32位的还是64位的,然后到oracle官网上下载对应版本的jdk,一般下载.tar.gz文件.查看Linux的版本的命令是 ...

  10. 【SpringBoot】02.编写HelloWorld

    1.编写一个返回HelloWorld的Controller @Controller public class HelloWorld { @ResponseBody @RequestMapping(&q ...

随机推荐

  1. Java集合框架学习(七) Vector详解

    Vector介绍 Vector 实现了List接口.和ArrayList一样也维护元素的插入顺序. 但它一般只用在多线程环境,因为它是线程同步的. 还有就是它对元素的增删改查效率低下. 类定义 pub ...

  2. Java并发编程实例--2.获取和设置线程信息

    常用线程属性 ID: 每个线程的唯一标识: Name: 线程名称: Priority: 线程优先级,从1-10,数字越大优先级越高:不推荐改变线程优先级: Status: 线程状态,包含6种状态:ne ...

  3. Jenkins共享库使用

    简单使用 共享库(Shared libraries)是一种可以用来封装函数.变量甚至整个 Pipeline 的机制.通过共享库,可以将常用的功能和流程逻辑定义在单独的 Groovy 脚本中,然后在多个 ...

  4. go值接收者和指针接收者的区别

    方法的接收者 package main import ( "fmt" ) type Person struct { Name string Age int } func (p Pe ...

  5. 面向对象基础---day02

    成员变量和局部变量区别 封装 private关键字 1.是一个权限修饰符 2.可以修饰成员(成员变量和成员方法) 3.作用是保护成员不被别的类使用,被private修饰的成员只在本类中才能访问 针对p ...

  6. 从 Neo4j 导入 Nebula Graph 实践见 SPark 数据导入原理

    本文主要讲述如何使用数据导入工具 Nebula Graph Exchange 将数据从 Neo4j 导入到 Nebula Graph Database.在讲述如何实操数据导入之前,我们先来了解下 Ne ...

  7. python 微信自动发图片,批量发送

    自动发送批量的图片给微信联系人,可为自己的文件传输助手 已实现: 可设置发送时间间隔 发送图片数量 指定接收人 下载链接: python批量自动连发图片给微信好友自动发图片-Python文档类资源-C ...

  8. 聚焦企业流程智能化发展新趋势,中国信通院2022 RPA创新产业峰会即将开启

    机器人流程自动化(Robotic Process Automation,RPA)是数字时代的重要劳动力之一,流程的自动化.智能化运行是企业释放运营能效.提升客户服务水平的重要路径. 近年来,各行业对R ...

  9. mysql主从复制原理-binlog

    0.为什么需要主从复制? 1.在业务复杂的系统中,有这么一个情景,有一句sql语句需要锁表,导致暂时不能使用读的服务,那么就很影响运行中的业务,使用主从复制,让主库负责写,从库负责读,这样,即使主库出 ...

  10. Educational Codeforces Round 145 (Rated for Div. 2)C. Sum on Subarrays(构造)

    很意思的一道构造题 题意:给一个\(n.k\),让构造长度为n的数组满足,子数组为整数的个数为k个,负数的为\(k-(n+1)* n/2\),每个数的范围为\([-1000,1000]\) 这种构造题 ...